diff options
2542 files changed, 71505 insertions, 30456 deletions
diff --git a/Android.bp b/Android.bp index 240d803ef337..14b5b309ebdf 100644 --- a/Android.bp +++ b/Android.bp @@ -60,6 +60,44 @@ java_defaults { // // READ ME: ######################################################## +package { + default_applicable_licenses: ["frameworks_base_license"], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// See: http://go/android-license-faq +license { + name: "frameworks_base_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "SPDX-license-identifier-BSD", + "SPDX-license-identifier-CC-BY", + "SPDX-license-identifier-CPL-1.0", + "SPDX-license-identifier-GPL", + "SPDX-license-identifier-GPL-2.0", + "SPDX-license-identifier-MIT", + "SPDX-license-identifier-Unicode-DFS", + "SPDX-license-identifier-W3C", + "legacy_unencumbered", + ], + license_text: [ + "NOTICE", + ], +} + filegroup { name: "framework-core-sources", srcs: [ @@ -306,8 +344,8 @@ filegroup { genrule { name: "statslog-telephony-common-java-gen", tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common" - + " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog", + cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common" + + " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog", out: ["com/android/internal/telephony/TelephonyCommonStatsLog.java"], } @@ -333,7 +371,6 @@ filegroup { srcs: [ // Java/AIDL sources under frameworks/base ":framework-blobstore-sources", - ":framework-connectivity-sources", // framework-connectivity is not yet a module ":framework-core-sources", ":framework-drm-sources", ":framework-graphics-nonupdatable-sources", @@ -399,6 +436,7 @@ filegroup { name: "framework-updatable-sources", srcs: [ ":framework-appsearch-sources", + ":framework-connectivity-sources", ":framework-graphics-srcs", ":framework-mediaprovider-sources", ":framework-permission-sources", @@ -544,6 +582,7 @@ java_library { "android.hardware.vibrator-V1.3-java", "android.security.apc-java", "android.security.authorization-java", + "android.security.usermanager-java", "android.system.keystore2-V1-java", "android.system.suspend.control.internal-java", "cameraprotosnano", @@ -600,6 +639,7 @@ java_defaults { defaults: ["framework-aidl-export-defaults"], srcs: [ ":framework-non-updatable-sources", + ":framework-connectivity-sources", "core/java/**/*.logtags", ], // See comment on framework-atb-backward-compatibility module below @@ -661,6 +701,8 @@ java_library { apex_available: ["//apex_available:platform"], visibility: [ "//frameworks/base", + // TODO: remove when framework-connectivity can build against API + "//frameworks/base/packages/Connectivity/framework", // TODO(b/147128803) remove the below lines "//frameworks/base/apex/appsearch/framework", "//frameworks/base/apex/blobstore/framework", @@ -707,8 +749,8 @@ java_library { } platform_compat_config { - name: "framework-platform-compat-config", - src: ":framework-minus-apex", + name: "framework-platform-compat-config", + src: ":framework-minus-apex", } // A temporary build target that is conditionally included on the bootclasspath if @@ -729,7 +771,7 @@ genrule { name: "statslog-framework-java-gen", tools: ["stats-log-api-gen"], cmd: "$(location stats-log-api-gen) --java $(out) --module framework" + - " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource", + " --javaPackage com.android.internal.util --javaClass FrameworkStatsLog --worksource", out: ["com/android/internal/util/FrameworkStatsLog.java"], } @@ -838,7 +880,7 @@ filegroup { java_library { name: "framework-annotations-lib", - srcs: [ ":framework-annotations" ], + srcs: [":framework-annotations"], sdk_version: "core_current", } @@ -1116,7 +1158,6 @@ cc_library { }, } - // This is the full proto version of libplatformprotos. It may only // be used by test code that is not shipped on the device. cc_library { @@ -1182,68 +1223,58 @@ filegroup { path: "core/java", } -aidl_interface { - name: "libincremental_aidl", - unstable: true, +cc_defaults { + name: "incremental_default", + host_supported: true, + cflags: [ + "-Wall", + "-Wextra", + "-Wextra-semi", + "-Werror", + "-Wzero-as-null-pointer-constant", + "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION", + ], + shared_libs: [ + "libbinder", + "libutils", + ], + aidl: { + include_dirs: [ + "frameworks/native/aidl/binder", + ], + export_aidl_headers: true, + }, +} + +cc_library { + name: "libincremental_aidl-cpp", srcs: [ ":incremental_aidl", ], - backend: { - java: { - sdk_version: "28", - }, - cpp: { - enabled: true, - }, - ndk: { - enabled: true, - }, - }, + defaults: ["incremental_default"], } -aidl_interface { - name: "libdataloader_aidl", - unstable: true, +cc_library { + name: "libdataloader_aidl-cpp", srcs: [ ":dataloader_aidl", ], - imports: [ - "libincremental_aidl", + defaults: ["incremental_default"], + shared_libs: [ + "libincremental_aidl-cpp", ], - backend: { - java: { - sdk_version: "28", - }, - cpp: { - enabled: true, - }, - ndk: { - enabled: false, - }, - }, } -aidl_interface { - name: "libincremental_manager_aidl", - unstable: true, +cc_library { + name: "libincremental_manager_aidl-cpp", srcs: [ ":incremental_manager_aidl", ], - imports: [ - "libincremental_aidl", - "libdataloader_aidl", + defaults: ["incremental_default"], + shared_libs: [ + "libincremental_aidl-cpp", + "libdataloader_aidl-cpp", ], - backend: { - java: { - sdk_version: "28", - }, - cpp: { - enabled: true, - }, - ndk: { - enabled: false, - }, - }, } // TODO(b/77285514): remove this once the last few hidl interfaces have been @@ -1272,7 +1303,7 @@ java_library { "core/java/android/os/RemoteException.java", "core/java/android/util/AndroidException.java", ], - libs: [ "unsupportedappusage" ], + libs: ["unsupportedappusage"], dxflags: ["--core-library"], installable: false, @@ -1413,6 +1444,7 @@ java_library { ], libs: [ "framework-annotations-lib", + "framework-connectivity", "unsupportedappusage", ], visibility: [ @@ -1490,4 +1522,5 @@ java_library { ":protolog-common-src", ], } + // protolog end diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 30ed7de92614..175fb38bafc7 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,5 +1,6 @@ [Builtin Hooks] clang_format = true +bpfmt = true [Builtin Hooks Options] # Only turn on clang-format check for the following subfolders. @@ -15,7 +16,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp services/incremental/ tests/ tools/ - +bpfmt = -d [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} diff --git a/StubLibraries.bp b/StubLibraries.bp index 4bd524f229ca..bff222e7d42f 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -58,6 +58,9 @@ stubs_defaults { local_include_dirs: [ "apex/media/aidl/stable", "media/aidl", + // TODO: move to include-dirs for packages/modules/Connectivity when this moves out of + // frameworks/base + "packages/Connectivity/framework/aidl-export", "telephony/java", ], include_dirs: ["frameworks/av/aidl"], @@ -310,6 +313,7 @@ java_library_static { "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "framework-appsearch.stubs", + "framework-connectivity.stubs", "framework-graphics.stubs", "framework-media.stubs", "framework-mediaprovider.stubs", @@ -334,6 +338,7 @@ java_library_static { "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "framework-appsearch.stubs.system", + "framework-connectivity.stubs.system", "framework-graphics.stubs.system", "framework-media.stubs.system", "framework-mediaprovider.stubs.system", @@ -374,6 +379,7 @@ java_library_static { "art.module.public.api.stubs", "conscrypt.module.public.api.stubs", "framework-appsearch.stubs.system", + "framework-connectivity.stubs.system", "framework-graphics.stubs.system", "framework-media.stubs.system", "framework-mediaprovider.stubs.system", diff --git a/TEST_MAPPING b/TEST_MAPPING index 6c265bc1a338..d08c52782fb3 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -39,6 +39,20 @@ ] }, { + "name": "FrameworkPermissionTests", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + }, + { "name": "FrameworksServicesTests", "options": [ { diff --git a/apct-tests/perftests/autofill/Android.bp b/apct-tests/perftests/autofill/Android.bp index 79176ff993c4..84145beb5b5f 100644 --- a/apct-tests/perftests/autofill/Android.bp +++ b/apct-tests/perftests/autofill/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "AutofillPerfTests", srcs: ["src/**/*.java"], diff --git a/apct-tests/perftests/blobstore/Android.bp b/apct-tests/perftests/blobstore/Android.bp index be700a2b021c..25c4250729c4 100644 --- a/apct-tests/perftests/blobstore/Android.bp +++ b/apct-tests/perftests/blobstore/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "BlobStorePerfTests", srcs: ["src/**/*.java"], @@ -26,4 +35,4 @@ android_test { platform_apis: true, test_suites: ["device-tests"], certificate: "platform", -}
\ No newline at end of file +} diff --git a/apct-tests/perftests/contentcapture/Android.bp b/apct-tests/perftests/contentcapture/Android.bp index 19a66ad9f27b..638403d40ea3 100644 --- a/apct-tests/perftests/contentcapture/Android.bp +++ b/apct-tests/perftests/contentcapture/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ContentCapturePerfTests", srcs: ["src/**/*.java"], diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp index c5419637dec8..2182f0bc2501 100644 --- a/apct-tests/perftests/core/Android.bp +++ b/apct-tests/perftests/core/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "CorePerfTests", diff --git a/apct-tests/perftests/core/apps/overlay/Android.bp b/apct-tests/perftests/core/apps/overlay/Android.bp index 7bee30ee9cb4..646530788907 100644 --- a/apct-tests/perftests/core/apps/overlay/Android.bp +++ b/apct-tests/perftests/core/apps/overlay/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "Overlay0", aaptflags: [ @@ -185,4 +194,4 @@ java_library { ":LargeOverlay8", ":LargeOverlay9", ], -}
\ No newline at end of file +} diff --git a/apct-tests/perftests/core/apps/reources_manager/Android.bp b/apct-tests/perftests/core/apps/reources_manager/Android.bp index 451613236140..85dd0c4be48d 100644 --- a/apct-tests/perftests/core/apps/reources_manager/Android.bp +++ b/apct-tests/perftests/core/apps/reources_manager/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "LargeResourcesCompressed", static_libs: [ "androidx.appcompat_appcompat" ], @@ -31,4 +40,4 @@ java_library { ":LargeResourcesCompressed", ":LargeResourcesUncompressed", ], -}
\ No newline at end of file +} diff --git a/apct-tests/perftests/core/jni/Android.bp b/apct-tests/perftests/core/jni/Android.bp index d160d48538c3..1e4405dea2d2 100644 --- a/apct-tests/perftests/core/jni/Android.bp +++ b/apct-tests/perftests/core/jni/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_library_shared { name: "libperftestscore_jni", sdk_version: "21", diff --git a/apct-tests/perftests/inputmethod/Android.bp b/apct-tests/perftests/inputmethod/Android.bp index 463ac9b8b0c8..f2f1f758112e 100644 --- a/apct-tests/perftests/inputmethod/Android.bp +++ b/apct-tests/perftests/inputmethod/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ImePerfTests", srcs: ["src/**/*.java"], diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp index fc45d4adcc15..917753e28829 100644 --- a/apct-tests/perftests/multiuser/Android.bp +++ b/apct-tests/perftests/multiuser/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "MultiUserPerfTests", srcs: ["src/**/*.java"], diff --git a/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp b/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp index 08c54a60a290..892c14006f87 100644 --- a/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp +++ b/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "MultiUserPerfDummyApp", diff --git a/apct-tests/perftests/packagemanager/Android.bp b/apct-tests/perftests/packagemanager/Android.bp index c15b6418ce4d..0e76488d9642 100644 --- a/apct-tests/perftests/packagemanager/Android.bp +++ b/apct-tests/perftests/packagemanager/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "PackageManagerPerfTests", diff --git a/apct-tests/perftests/packagemanager/apps/query-all/Android.bp b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp index 3cb1589bc376..b2339d5965a0 100644 --- a/apct-tests/perftests/packagemanager/apps/query-all/Android.bp +++ b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "QueriesAll0", aaptflags: [ diff --git a/apct-tests/perftests/textclassifier/Android.bp b/apct-tests/perftests/textclassifier/Android.bp index c40e0252cb7e..1011267bcdc3 100644 --- a/apct-tests/perftests/textclassifier/Android.bp +++ b/apct-tests/perftests/textclassifier/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "TextClassifierPerfTests", srcs: ["src/**/*.java"], diff --git a/apct-tests/perftests/utils/Android.bp b/apct-tests/perftests/utils/Android.bp index be85816629c1..6c46a9b58f06 100644 --- a/apct-tests/perftests/utils/Android.bp +++ b/apct-tests/perftests/utils/Android.bp @@ -1,3 +1,12 @@ +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: "apct-perftests-utils", static_libs: [ diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfRunPreconditionBase.java b/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfRunPreconditionBase.java index 8d2ac0276592..330a19e03c39 100644 --- a/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfRunPreconditionBase.java +++ b/apct-tests/perftests/utils/src/android/perftests/utils/WindowPerfRunPreconditionBase.java @@ -89,7 +89,7 @@ public class WindowPerfRunPreconditionBase extends RunListener { navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY; break; } - executeShellCommand("cmd overlay enable-exclusive " + navOverlay); + executeShellCommand("cmd overlay enable-exclusive --category " + navOverlay); }); /** It only executes once before all tests. */ diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp index dbfe6ab3210f..365824eb6d00 100644 --- a/apct-tests/perftests/windowmanager/Android.bp +++ b/apct-tests/perftests/windowmanager/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "WmPerfTests", srcs: ["src/**/*.java"], diff --git a/apex/Android.bp b/apex/Android.bp index 8310ba769f55..f3a936233ae8 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -14,4 +14,10 @@ package { default_visibility: [":__subpackages__"], + // 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"], } diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp index b014fdcb3df3..87f65a922004 100644 --- a/apex/appsearch/Android.bp +++ b/apex/appsearch/Android.bp @@ -12,6 +12,15 @@ // 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", @@ -21,6 +30,7 @@ apex { ], key: "com.android.appsearch.key", certificate: ":com.android.appsearch.certificate", + updatable: false, } apex_key { diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp index 8ba79541088b..5def55fd31ff 100644 --- a/apex/appsearch/framework/Android.bp +++ b/apex/appsearch/framework/Android.bp @@ -12,6 +12,15 @@ // 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: [ diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index 905000ac7c3d..168c7c2f13cd 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -2,6 +2,7 @@ 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(); @@ -146,8 +147,8 @@ package android.app.appsearch { 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.RemoveByUriRequest, @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 @NonNull 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, @NonNull java.util.concurrent.Executor); + 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.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.SetSchemaResponse>>); } @@ -216,7 +217,7 @@ package android.app.appsearch { public class GlobalSearchSession implements java.io.Closeable { method public void close(); - method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor); + method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec); } public class PackageIdentifier { @@ -286,7 +287,7 @@ package android.app.appsearch { public class SearchResults implements java.io.Closeable { method public void close(); - method public void getNextPage(@NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.util.List<android.app.appsearch.SearchResult>>>); + 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 { @@ -358,7 +359,6 @@ package android.app.appsearch { 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(); - method public boolean isSuccess(); } public static class SetSchemaResponse.MigrationFailure { diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java index cd75b1456ba8..31ab259127c3 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java @@ -28,11 +28,19 @@ import java.util.Collections; import java.util.Map; /** - * Provides access to multiple {@link AppSearchResult}s from a batch operation accepting multiple - * inputs. + * Provides results for AppSearch batch operations which encompass multiple documents. * - * @param <KeyType> The type of the keys for {@link #getSuccesses} and {@link #getFailures}. - * @param <ValueType> The type of result objects associated with the keys. + * <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. + * + * @see AppSearchSession#put + * @see AppSearchSession#getByUri + * @see AppSearchSession#remove */ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { @NonNull private final Map<KeyType, ValueType> mSuccesses; @@ -75,8 +83,11 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl } /** - * Returns a {@link Map} of all successful keys mapped to the successful {@link - * AppSearchResult}s they produced. + * Returns a {@link Map} of keys mapped to instances of the value type for all successful + * individual results. + * + * <p>Example: {@link AppSearchSession#getByUri} returns an {@link AppSearchBatchResult}. Each + * key (a URI of {@code String} type) will map to a {@link GenericDocument} object. * * <p>The values of the {@link Map} will not be {@code null}. */ @@ -86,8 +97,8 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl } /** - * Returns a {@link Map} of all failed keys mapped to the failed {@link AppSearchResult}s they - * produced. + * 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}. */ @@ -97,10 +108,10 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl } /** - * Returns a {@link Map} of all keys mapped to the {@link AppSearchResult}s they produced. + * 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}. - * @hide */ @NonNull public Map<KeyType, AppSearchResult<ValueType>> getAll() { @@ -150,8 +161,8 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl /** * Builder for {@link AppSearchBatchResult} objects. * - * @param <KeyType> The type of keys. - * @param <ValueType> The type of result objects associated with the keys. + * <p>Once {@link #build} is called, the instance can no longer be used. + * * @hide */ public static final class Builder<KeyType, ValueType> { @@ -161,9 +172,11 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl private boolean mBuilt = false; /** - * Associates the {@code key} with the given successful return value. + * Associates the {@code key} with the provided successful return value. * * <p>Any previous mapping for a key, whether success or failure, is deleted. + * + * @throws IllegalStateException if the builder has already been used. */ @NonNull public Builder<KeyType, ValueType> setSuccess( @@ -174,9 +187,11 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl } /** - * Associates the {@code key} with the given failure code and error message. + * Associates the {@code key} with the provided failure code and error message. * * <p>Any previous mapping for a key, whether success or failure, is deleted. + * + * @throws IllegalStateException if the builder has already been used. */ @NonNull public Builder<KeyType, ValueType> setFailure( @@ -189,9 +204,11 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl } /** - * Associates the {@code key} with the given {@code result}. + * Associates the {@code key} with the provided {@code result}. * * <p>Any previous mapping for a key, whether success or failure, is deleted. + * + * @throws IllegalStateException if the builder has already been used. */ @NonNull public Builder<KeyType, ValueType> setResult( @@ -210,7 +227,11 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl return this; } - /** Builds an {@link AppSearchBatchResult} from the contents of this {@link Builder}. */ + /** + * Builds an {@link AppSearchBatchResult} object from the contents of this {@link Builder}. + * + * @throws IllegalStateException if the builder has already been used. + */ @NonNull public AppSearchBatchResult<KeyType, ValueType> build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index 69d4e536597f..6a5975ef7ff7 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -36,12 +36,89 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; /** - * This class provides access to the centralized AppSearch index maintained by the system. + * Provides access to the centralized AppSearch index maintained by the system. * - * <p>Apps can index structured text documents with AppSearch, which can then be retrieved through - * the query API. + * <p>AppSearch is a search library for managing structured data featuring: + * <ul> + * <li>A fully offline on-device solution + * <li>A set of APIs for applications to index documents and retrieve them via full-text search + * <li>APIs for applications to allow the System to display their content on system UI surfaces + * <li>Similarly, APIs for applications to allow the System to share their content with other + * specified applications. + * </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 a URI, 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. A URI identifies a single document within a namespace. The combination + * of URI and namespace 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<>(URI, EMAIL_SCHEMA_TYPE) + * .setNamespace(NAMESPACE) + * .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#getByUri} can be called to retrieve documents by URI + * and namespace. + * + * <p>Document removal is done either by time-to-live expiration, or explicitly calling a remove + * operation. Remove operations can be done by URI and namespace via + * {@link AppSearchSession#remove(RemoveByUriRequest, Executor, BatchResultCallback)}, + * or by query via {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}. */ -// TODO(b/148046169): This class header needs a detailed example/tutorial. @SystemService(Context.APP_SEARCH_SERVICE) public class AppSearchManager { /** diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index 73ca0ccab46d..24cc60e5eef5 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -41,7 +41,7 @@ import java.util.function.Consumer; * Represents a connection to an AppSearch storage system where {@link GenericDocument}s can be * placed and queried. * - * This class is thread safe. + * <p>This class is thread safe. */ public final class AppSearchSession implements Closeable { private static final String TAG = "AppSearchSession"; @@ -102,92 +102,23 @@ public final class AppSearchSession implements Closeable { } /** - * Sets the schema that will be used by documents provided to the {@link #put} method. - * - * <p>The schema provided here is compared to the stored copy of the schema previously supplied - * to {@link #setSchema}, if any, to determine how to treat existing documents. The following - * types of schema modifications are always safe and are made without deleting any existing - * documents: - * - * <ul> - * <li>Addition of new types - * <li>Addition of new {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or - * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} properties to a - * type - * <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} property. - * </ul> - * - * <p>The following types of schema changes are not backwards-compatible: - * - * <ul> - * <li>Removal of an existing type - * <li>Removal of a property from a type - * <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property - * <li>For properties of {@code Document} type, changing the schema type of {@code Document}s - * of that property - * <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property). - * <li>Adding a {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property. - * </ul> - * - * <p>Supplying a schema with such changes will, by default, result in this call completing its - * future with an {@link android.app.appsearch.exceptions.AppSearchException} with a code of - * {@link AppSearchResult#RESULT_INVALID_SCHEMA} and a message describing the incompatibility. - * In this case the previously set schema will remain active. - * - * <p>If you need to make non-backwards-compatible changes as described above, you can either: - * - * <ul> - * <li>Set the {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In - * this case, instead of completing its future with an {@link - * android.app.appsearch.exceptions.AppSearchException} with the {@link - * AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not - * compatible with the new schema will be deleted and the incompatible schema will be - * applied. Incompatible types and deleted types will be set into {@link - * SetSchemaResponse#getIncompatibleTypes()} and {@link - * SetSchemaResponse#getDeletedTypes()}, respectively. - * <li>Add a {@link android.app.appsearch.AppSearchSchema.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> - * - * <p>It is a no-op to set the same schema as has been previously set; this is handled - * efficiently. - * - * <p>By default, documents are visible on platform surfaces. To opt out, call {@code - * SetSchemaRequest.Builder#setPlatformSurfaceable} with {@code surfaceable} as false. Any - * visibility settings apply only to the schemas that are included in the {@code request}. - * Visibility settings for a schema type do not apply or persist across {@link - * SetSchemaRequest}s. - * - * <p>Migration: make non-backwards-compatible changes will delete all stored documents in old - * schema. You can save your documents by setting {@link - * android.app.appsearch.AppSearchSchema.Migrator} via the {@link - * SetSchemaRequest.Builder#setMigrator} for each type you want to save. - * - * <p>{@link android.app.appsearch.AppSearchSchema.Migrator#onDowngrade} or {@link - * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} will be triggered if the version - * number of the schema stored in AppSearch is different with the version in the request. + * Sets the schema that represents the organizational structure of data within the AppSearch + * database. * - * <p>If any error or Exception occurred in the {@link - * android.app.appsearch.AppSearchSchema.Migrator#onDowngrade}, {@link - * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} or {@link - * android.app.appsearch.AppSearchMigrationHelper.Transformer#transform}, the migration will be - * terminated, the setSchema request will be rejected unless the schema changes are - * backwards-compatible, and stored documents won't have any observable changes. + * <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 update request. + * @param request the schema to set or update the AppSearch database to. * @param executor 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}. * @see android.app.appsearch.AppSearchSchema.Migrator * @see android.app.appsearch.AppSearchMigrationHelper.Transformer */ + // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are + // exposed. public void setSchema( @NonNull SetSchemaRequest request, @NonNull @CallbackExecutor Executor executor, @@ -280,12 +211,13 @@ public final class AppSearchSession implements Closeable { } /** - * Indexes documents into AppSearch. + * Indexes documents into the {@link AppSearchSession} database. * - * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a - * schema type previously registered via the {@link #setSchema} method. + * <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 {@link PutDocumentsRequest} containing documents to be indexed + * @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 URIs of the input @@ -463,21 +395,15 @@ public final class AppSearchSession implements Closeable { * @param queryExpression query string to search. * @param searchSpec spec for setting document filters, adding projection, setting term match * type, etc. - * @param executor Executor on which to invoke the callback of the following request - * {@link SearchResults#getNextPage}. * @return a {@link SearchResults} object for retrieved matched documents. */ @NonNull - public SearchResults search( - @NonNull String queryExpression, - @NonNull SearchSpec searchSpec, - @NonNull @CallbackExecutor Executor executor) { + public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) { Objects.requireNonNull(queryExpression); Objects.requireNonNull(searchSpec); - Objects.requireNonNull(executor); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); return new SearchResults(mService, mPackageName, mDatabaseName, queryExpression, - searchSpec, mUserId, executor); + searchSpec, mUserId); } /** @@ -497,7 +423,6 @@ public final class AppSearchSession implements Closeable { * @param callback Callback to receive errors. If the operation succeeds, the callback will be * invoked with {@code null}. */ - @NonNull public void reportUsage( @NonNull ReportUsageRequest request, @NonNull @CallbackExecutor Executor executor, diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java index 09bca4fb3b9d..8dd9dc1be312 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java @@ -98,8 +98,7 @@ public class GlobalSearchSession implements Closeable { * <p>Document access can also be granted to system UIs by specifying {@link * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} when building a schema. * - * <p>See {@link AppSearchSession#search} for a detailed explanation on - * forming a query string. + * <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}. @@ -107,21 +106,15 @@ public class GlobalSearchSession implements Closeable { * @param queryExpression query string to search. * @param searchSpec spec for setting document filters, adding projection, setting term match * type, etc. - * @param executor Executor on which to invoke the callback of the following request - * {@link SearchResults#getNextPage}. * @return a {@link SearchResults} object for retrieved matched documents. */ @NonNull - public SearchResults search( - @NonNull String queryExpression, - @NonNull SearchSpec searchSpec, - @NonNull @CallbackExecutor Executor executor) { + public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) { Objects.requireNonNull(queryExpression); Objects.requireNonNull(searchSpec); - Objects.requireNonNull(executor); Preconditions.checkState(!mIsClosed, "GlobalSearchSession has already been closed"); return new SearchResults(mService, mPackageName, /*databaseName=*/null, queryExpression, - searchSpec, mUserId, executor); + searchSpec, mUserId); } /** Closes the {@link GlobalSearchSession}. */ diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java index a63e01555f6c..531c98425288 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java @@ -33,12 +33,16 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; /** - * SearchResults are a returned object from a query API. + * Encapsulates results of a search operation. * - * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets based - * on request. + * <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>Should close this object after finish fetching results. + * <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. */ @@ -61,8 +65,6 @@ public class SearchResults implements Closeable { @UserIdInt private final int mUserId; - private final Executor mExecutor; - private long mNextPageToken; private boolean mIsFirstLoad = true; @@ -75,28 +77,31 @@ public class SearchResults implements Closeable { @Nullable String databaseName, @NonNull String queryExpression, @NonNull SearchSpec searchSpec, - @UserIdInt int userId, - @NonNull @CallbackExecutor Executor executor) { + @UserIdInt int userId) { mService = Objects.requireNonNull(service); mPackageName = packageName; mDatabaseName = databaseName; mQueryExpression = Objects.requireNonNull(queryExpression); mSearchSpec = Objects.requireNonNull(searchSpec); mUserId = userId; - mExecutor = Objects.requireNonNull(executor); } /** - * Gets a whole page of {@link SearchResult}s. + * Retrieves the next page of {@link SearchResult} objects. * - * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an empty - * list. + * <p>The page size is configured by {@link SearchSpec.Builder#setResultCountPerPage}. * - * <p>The page size is set 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 Consumer<AppSearchResult<List<SearchResult>>> callback) { + 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) { @@ -104,14 +109,14 @@ public class SearchResults implements Closeable { if (mDatabaseName == null) { // Global query, there's no one package-database combination to check. mService.globalQuery(mPackageName, mQueryExpression, - mSearchSpec.getBundle(), mUserId, wrapCallback(callback)); + mSearchSpec.getBundle(), mUserId, wrapCallback(executor, callback)); } else { // Normal local query, pass in specified database. mService.query(mPackageName, mDatabaseName, mQueryExpression, - mSearchSpec.getBundle(), mUserId, wrapCallback(callback)); + mSearchSpec.getBundle(), mUserId, wrapCallback(executor, callback)); } } else { - mService.getNextPage(mNextPageToken, mUserId, wrapCallback(callback)); + mService.getNextPage(mNextPageToken, mUserId, wrapCallback(executor, callback)); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -131,10 +136,11 @@ public class SearchResults implements Closeable { } private IAppSearchResultCallback wrapCallback( + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) { return new IAppSearchResultCallback.Stub() { public void onResult(AppSearchResult result) { - mExecutor.execute(() -> invokeCallback(result, callback)); + executor.execute(() -> invokeCallback(result, callback)); } }; } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java index 138eb23148af..72bb9f3d07c8 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java @@ -583,9 +583,9 @@ public class GenericDocument { * @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}. + * 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 uri, @NonNull String schemaType) { diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java index 05b212880962..01473be062bc 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java @@ -28,9 +28,9 @@ import java.util.Collections; import java.util.List; /** - * Encapsulates a request to index a document into an {@link AppSearchSession} database. + * Encapsulates a request to index documents into an {@link AppSearchSession} database. * - * <p>@see AppSearchSession#putDocuments + * @see AppSearchSession#put */ public final class PutDocumentsRequest { private final List<GenericDocument> mDocuments; @@ -39,7 +39,7 @@ public final class PutDocumentsRequest { mDocuments = documents; } - /** Returns the documents that are part of this request. */ + /** Returns a list of {@link GenericDocument} objects that are part of this request. */ @NonNull public List<GenericDocument> getGenericDocuments() { return Collections.unmodifiableList(mDocuments); @@ -54,14 +54,22 @@ public final class PutDocumentsRequest { private final List<GenericDocument> mDocuments = new ArrayList<>(); private boolean mBuilt = false; - /** Adds one or more {@link GenericDocument} objects to the request. */ + /** + * Adds one or more {@link GenericDocument} objects to the request. + * + * @throws IllegalStateException if the builder has already been used. + */ @NonNull public Builder addGenericDocuments(@NonNull GenericDocument... documents) { Preconditions.checkNotNull(documents); return addGenericDocuments(Arrays.asList(documents)); } - /** Adds a collection of {@link GenericDocument} objects to the request. */ + /** + * Adds a collection of {@link GenericDocument} objects to the request. + * + * @throws IllegalStateException if the builder has already been used. + */ @NonNull public Builder addGenericDocuments( @NonNull Collection<? extends GenericDocument> documents) { @@ -71,7 +79,11 @@ public final class PutDocumentsRequest { return this; } - /** Creates a new {@link PutDocumentsRequest} object. */ + /** + * Creates a new {@link PutDocumentsRequest} object. + * + * @throws IllegalStateException if the builder has already been used. + */ @NonNull public PutDocumentsRequest build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java index 2caa94a5ef07..426a903981b3 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java @@ -32,6 +32,41 @@ 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 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. + * * @see AppSearchSession#setSchema */ public final class SetSchemaRequest { @@ -54,14 +89,15 @@ public final class SetSchemaRequest { mForceOverride = forceOverride; } - /** Returns the schemas that are part of this request. */ + /** Returns the {@link AppSearchSchema} types that are part of this request. */ @NonNull public Set<AppSearchSchema> getSchemas() { return Collections.unmodifiableSet(mSchemas); } /** - * Returns the set of schema types that have opted out of being visible on system UI surfaces. + * Returns all the schema types that are opted out of being displayed and visible on any system + * UI surface. */ @NonNull public Set<String> getSchemasNotVisibleToSystemUi() { @@ -70,10 +106,9 @@ public final class SetSchemaRequest { /** * Returns a mapping of schema types to the set of packages that have access to that schema - * type. Each package is represented by a {@link PackageIdentifier}. name and byte[] - * certificate. + * type. * - * <p>This method is inefficient to call repeatedly. + * <p>It’s inefficient to call this method repeatedly. */ @NonNull public Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackages() { @@ -91,9 +126,8 @@ public final class SetSchemaRequest { } /** - * Returns a mapping of schema types to the set of packages that have access to that schema - * type. Each package is represented by a {@link PackageIdentifier}. name and byte[] - * certificate. + * 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. @@ -110,7 +144,11 @@ public final class SetSchemaRequest { return mForceOverride; } - /** Builder for {@link SetSchemaRequest} objects. */ + /** + * Builder for {@link SetSchemaRequest} objects. + * + * <p>Once {@link #build} is called, the instance can no longer be used. + */ public static final class Builder { private final Set<AppSearchSchema> mSchemas = new ArraySet<>(); private final Set<String> mSchemasNotVisibleToSystemUi = new ArraySet<>(); @@ -121,9 +159,11 @@ public final class SetSchemaRequest { private boolean mBuilt = false; /** - * Adds one or more types to the schema. + * 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 visible on system UI surfaces by default. + * @throws IllegalStateException if the builder has already been used. */ @NonNull public Builder addSchemas(@NonNull AppSearchSchema... schemas) { @@ -132,9 +172,11 @@ public final class SetSchemaRequest { } /** - * Adds one or more types to the schema. + * Adds a collection of {@link AppSearchSchema} objects to the schema. * - * <p>Any documents of these types will be visible on system UI surfaces by default. + * <p>An {@link AppSearchSchema} object represents one type of structured data. + * + * @throws IllegalStateException if the builder has already been used. */ @NonNull public Builder addSchemas(@NonNull Collection<AppSearchSchema> schemas) { @@ -145,10 +187,17 @@ public final class SetSchemaRequest { } /** - * Sets visibility on system UI surfaces for the given {@code schemaType}. + * 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>By default, documents are displayed and visible on system UI surfaces. * * @param schemaType The schema type to set visibility on. * @param visible Whether the {@code schemaType} will be visible or not. + * @throws IllegalStateException if the builder has already been used. */ // Merged list available from getSchemasNotVisibleToSystemUi @SuppressLint("MissingGetterMatchingBuilder") @@ -167,11 +216,25 @@ public final class SetSchemaRequest { } /** - * Sets visibility for a package for the given {@code schemaType}. + * 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. + * @throws IllegalStateException if the builder has already been used. */ // Merged list available from getSchemasVisibleToPackages @SuppressLint("MissingGetterMatchingBuilder") @@ -224,13 +287,15 @@ public final class SetSchemaRequest { } /** - * Configures the {@link SetSchemaRequest} to delete any existing documents that don't - * follow the new schema. + * Sets whether or not to override the current schema in the {@link AppSearchSession} + * database. * - * <p>By default, this is {@code false} and schema incompatibility causes the {@link - * AppSearchSession#setSchema} call to fail. + * <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. * - * @see AppSearchSession#setSchema + * <p>By default, this is {@code false}. */ @NonNull public Builder setForceOverride(boolean forceOverride) { @@ -239,10 +304,11 @@ public final class SetSchemaRequest { } /** - * Builds a new {@link SetSchemaRequest}. + * Builds a new {@link SetSchemaRequest} object. * - * @throws IllegalArgumentException If schema types were referenced, but the corresponding - * {@link AppSearchSchema} was never added. + * @throws IllegalArgumentException if schema types were referenced, but the corresponding + * {@link AppSearchSchema} type was never added. + * @throws IllegalStateException if the builder has already been used. */ @NonNull public SetSchemaRequest build() { diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java index bc99d4f67d86..a146006f355c 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java @@ -79,12 +79,6 @@ public class SetSchemaResponse { return mBundle; } - /** TODO(b/177266929): Remove this deprecated method */ - //@Deprecated - public boolean isSuccess() { - return true; - } - /** * Returns a {@link List} of all failed {@link MigrationFailure}. * diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp index 2fd5c733473e..57ee1ec482d8 100644 --- a/apex/appsearch/service/Android.bp +++ b/apex/appsearch/service/Android.bp @@ -11,6 +11,15 @@ // WITHOUT WARRANTIES 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: "service-appsearch", installable: true, diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java index babcd25e3e26..7c92456bea49 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java @@ -218,9 +218,23 @@ public class VisibilityStore { * @throws AppSearchException AppSearchException on AppSearchImpl error. */ public void initialize() throws AppSearchException { - if (!mAppSearchImpl.hasSchemaTypeLocked(PACKAGE_NAME, DATABASE_NAME, VISIBILITY_TYPE) - || !mAppSearchImpl.hasSchemaTypeLocked( - PACKAGE_NAME, DATABASE_NAME, PACKAGE_ACCESSIBLE_TYPE)) { + List<AppSearchSchema> schemas = mAppSearchImpl.getSchema(PACKAGE_NAME, DATABASE_NAME); + boolean hasVisibilityType = false; + boolean hasPackageAccessibleType = false; + for (int i = 0; i < schemas.size(); i++) { + AppSearchSchema schema = schemas.get(i); + if (schema.getSchemaType().equals(VISIBILITY_TYPE)) { + hasVisibilityType = true; + } else if (schema.getSchemaType().equals(PACKAGE_ACCESSIBLE_TYPE)) { + hasPackageAccessibleType = true; + } + + if (hasVisibilityType && hasPackageAccessibleType) { + // Found both our types, can exit early. + break; + } + } + if (!hasVisibilityType || !hasPackageAccessibleType) { // Schema type doesn't exist yet. Add it. mAppSearchImpl.setSchema( PACKAGE_NAME, @@ -250,10 +264,11 @@ public class VisibilityStore { /*typePropertyPaths=*/ Collections.emptyMap()); // Update platform visibility settings - String[] schemas = + String[] notPlatformSurfaceableSchemas = document.getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY); - if (schemas != null) { - mNotPlatformSurfaceableMap.put(prefix, new ArraySet<>(Arrays.asList(schemas))); + if (notPlatformSurfaceableSchemas != null) { + mNotPlatformSurfaceableMap.put( + prefix, new ArraySet<>(Arrays.asList(notPlatformSurfaceableSchemas))); } // Update 3p package visibility settings @@ -333,7 +348,7 @@ public class VisibilityStore { schemasPackageAccessible.entrySet()) { for (int i = 0; i < entry.getValue().size(); i++) { GenericDocument packageAccessibleDocument = - new GenericDocument.Builder(/*uri=*/"", PACKAGE_ACCESSIBLE_TYPE) + new GenericDocument.Builder(/*uri=*/ "", PACKAGE_ACCESSIBLE_TYPE) .setNamespace(NAMESPACE) .setPropertyString( PACKAGE_NAME_PROPERTY, diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java index 6c2e30e98877..e2c211b7d303 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java @@ -654,6 +654,35 @@ public final class AppSearchImpl implements Closeable { } } + /** + * 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, @@ -1211,26 +1240,8 @@ public final class AppSearchImpl implements Closeable { return schemaProto.getSchema(); } - /** - * Returns true if the {@code packageName} and {@code databaseName} has the {@code schemaType} - */ - @GuardedBy("mReadWriteLock") - boolean hasSchemaTypeLocked( - @NonNull String packageName, @NonNull String databaseName, @NonNull String schemaType) { - Preconditions.checkNotNull(packageName); - Preconditions.checkNotNull(databaseName); - Preconditions.checkNotNull(schemaType); - - String prefix = createPrefix(packageName, databaseName); - Set<String> schemaTypes = mSchemaMapLocked.get(prefix); - if (schemaTypes == null) { - return false; - } - - return schemaTypes.contains(prefix + schemaType); - } - /** Returns a set of all prefixes AppSearchImpl knows about. */ + // TODO(b/180058203): Remove this method once platform has switched away from using this method. @GuardedBy("mReadWriteLock") @NonNull Set<String> getPrefixesLocked() { 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 new file mode 100644 index 000000000000..0f23d926648b --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java @@ -0,0 +1,43 @@ +/* + * 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 android.app.appsearch.exceptions.AppSearchException; + +import com.android.server.appsearch.external.localstorage.stats.CallStats; +import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; + +/** + * 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) throws AppSearchException; + + /** Logs {@link PutDocumentStats} */ + void logStats(@NonNull PutDocumentStats stats) throws AppSearchException; + + // 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/AppSearchMigrationHelperImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java index a501e99db1ef..a7f1cc4c793f 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java @@ -76,7 +76,7 @@ class AppSearchMigrationHelperImpl implements AppSearchMigrationHelper { int currentVersion = mCurrentVersionMap.get(schemaType); int finalVersion = mFinalVersionMap.get(schemaType); try (FileOutputStream outputStream = new FileOutputStream(mFile)) { - // TODO(b/177266929) change the output stream so that we can use it in platform + // TODO(b/151178558) change the output stream so that we can use it in platform CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream); SearchResultPage searchResultPage = mAppSearchImpl.query( 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 new file mode 100644 index 000000000000..81a5067c9fa1 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java @@ -0,0 +1,200 @@ +/* + * 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 com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * 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 like {@link android.app.appsearch.AppSearchSession#setSchema} have their + * own detailed stats class {@link placeholder}. 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, + CALL_TYPE_PUT_DOCUMENT, + CALL_TYPE_GET_DOCUMENT, + CALL_TYPE_REMOVE_DOCUMENT, + CALL_TYPE_QUERY, + CALL_TYPE_OPTIMIZE, + CALL_TYPE_FLUSH, + }) + @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 = 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 = 8; + public static final int CALL_TYPE_QUERY = 9; + public static final int CALL_TYPE_OPTIMIZE = 10; + public static final int CALL_TYPE_FLUSH = 11; + + @NonNull private final GeneralStats mGeneralStats; + @CallType private final int mCallType; + private final int mEstimatedBinderLatencyMillis; + private final int mNumOperationsSucceeded; + private final int mNumOperationsFailed; + + CallStats(@NonNull Builder builder) { + Preconditions.checkNotNull(builder); + mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStats); + mCallType = builder.mCallType; + mEstimatedBinderLatencyMillis = builder.mEstimatedBinderLatencyMillis; + mNumOperationsSucceeded = builder.mNumOperationsSucceeded; + mNumOperationsFailed = builder.mNumOperationsFailed; + } + + /** Returns general information for the call. */ + @NonNull + public GeneralStats getGeneralStats() { + return mGeneralStats; + } + + /** 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 { + @NonNull final GeneralStats mGeneralStats; + @CallType int mCallType; + int mEstimatedBinderLatencyMillis; + int mNumOperationsSucceeded; + int mNumOperationsFailed; + + /** Builder takes {@link GeneralStats} to hold general stats. */ + public Builder(@NonNull GeneralStats generalStats) { + mGeneralStats = Preconditions.checkNotNull(generalStats); + } + + /** 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/GeneralStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java new file mode 100644 index 000000000000..d2a45d5304f9 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java @@ -0,0 +1,122 @@ +/* + * 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 com.android.internal.util.Preconditions; + +/** + * A class for holding general logging information. + * + * <p>This class cannot be logged by {@link + * com.android.server.appsearch.external.localstorage.AppSearchLogger} directly. It is used for + * defining general logging information that is shared across different stats classes. + * + * @see PutDocumentStats + * @see CallStats + * @hide + */ +public final class GeneralStats { + /** Package name of the application. */ + @NonNull private final String mPackageName; + + /** Database name within AppSearch. */ + @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; + + GeneralStats(@NonNull Builder builder) { + Preconditions.checkNotNull(builder); + mPackageName = Preconditions.checkNotNull(builder.mPackageName); + mDatabase = Preconditions.checkNotNull(builder.mDatabase); + mStatusCode = builder.mStatusCode; + mTotalLatencyMillis = builder.mTotalLatencyMillis; + } + + /** Returns package name. */ + @NonNull + public String getPackageName() { + return mPackageName; + } + + /** Returns database name. */ + @NonNull + public String getDatabase() { + return mDatabase; + } + + /** Returns result code from {@link AppSearchResult#getResultCode()} */ + @AppSearchResult.ResultCode + public int getStatusCode() { + return mStatusCode; + } + + /** Returns total latency, in milliseconds. */ + public int getTotalLatencyMillis() { + return mTotalLatencyMillis; + } + + /** Builder for {@link GeneralStats}. */ + public static class Builder { + @NonNull final String mPackageName; + @NonNull final String mDatabase; + @AppSearchResult.ResultCode int mStatusCode; + int mTotalLatencyMillis; + + /** + * Constructor + * + * @param packageName name of the package logging stats + * @param dataBase name of the database logging stats + */ + public Builder(@NonNull String packageName, @NonNull String dataBase) { + mPackageName = Preconditions.checkNotNull(packageName); + mDatabase = Preconditions.checkNotNull(dataBase); + } + + /** Sets status code returned from {@link AppSearchResult#getResultCode()} */ + @NonNull + public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) { + mStatusCode = statusCode; + return this; + } + + /** Sets total latency, in milliseconds. */ + @NonNull + public Builder setTotalLatencyMillis(int totalLatencyMillis) { + mTotalLatencyMillis = totalLatencyMillis; + return this; + } + + /** + * Creates a new {@link GeneralStats} object from the contents of this {@link Builder} + * instance. + */ + @NonNull + public GeneralStats build() { + return new GeneralStats(/* 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 new file mode 100644 index 000000000000..b1b643b66859 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java @@ -0,0 +1,219 @@ +/* + * 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 com.android.internal.util.Preconditions; + +/** + * 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 { + /** {@link GeneralStats} holds the general stats. */ + @NonNull private final GeneralStats mGeneralStats; + + /** 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; + + /** Number of tokens clipped for exceeding the max number. */ + private final int mNativeNumTokensClipped; + + PutDocumentStats(@NonNull Builder builder) { + Preconditions.checkNotNull(builder); + mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStats); + mGenerateDocumentProtoLatencyMillis = builder.mGenerateDocumentProtoLatencyMillis; + mRewriteDocumentTypesLatencyMillis = builder.mRewriteDocumentTypesLatencyMillis; + mNativeLatencyMillis = builder.mNativeLatencyMillis; + mNativeDocumentStoreLatencyMillis = builder.mNativeDocumentStoreLatencyMillis; + mNativeIndexLatencyMillis = builder.mNativeIndexLatencyMillis; + mNativeIndexMergeLatencyMillis = builder.mNativeIndexMergeLatencyMillis; + mNativeDocumentSizeBytes = builder.mNativeDocumentSizeBytes; + mNativeNumTokensIndexed = builder.mNativeNumTokensIndexed; + mNativeNumTokensClipped = builder.mNativeNumTokensClipped; + } + + /** Returns the {@link GeneralStats} object attached to this instance. */ + @NonNull + public GeneralStats getGeneralStats() { + return mGeneralStats; + } + + /** 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 number of tokens clipped for exceeding the max number. */ + public int getNativeNumTokensClipped() { + return mNativeNumTokensClipped; + } + + /** Builder for {@link PutDocumentStats}. */ + public static class Builder { + @NonNull final GeneralStats mGeneralStats; + int mGenerateDocumentProtoLatencyMillis; + int mRewriteDocumentTypesLatencyMillis; + int mNativeLatencyMillis; + int mNativeDocumentStoreLatencyMillis; + int mNativeIndexLatencyMillis; + int mNativeIndexMergeLatencyMillis; + int mNativeDocumentSizeBytes; + int mNativeNumTokensIndexed; + int mNativeNumTokensClipped; + + /** Builder takes {@link GeneralStats} to hold general stats. */ + public Builder(@NonNull GeneralStats generalStats) { + mGeneralStats = Preconditions.checkNotNull(generalStats); + } + + /** 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 number of tokens clipped for exceeding the max number. */ + @NonNull + public Builder setNativeNumTokensClipped(int nativeNumTokensClipped) { + mNativeNumTokensClipped = nativeNumTokensClipped; + 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/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index d076db3b8f82..2c9477a5d76b 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -Ia9a8daef1a6d7d9432f7808d440abd64f4797701 +I593dfd22279739e5f578e07d36a55cf02ee942c5 diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp index 54d50395e3bd..eb072afec696 100644 --- a/apex/appsearch/testing/Android.bp +++ b/apex/appsearch/testing/Android.bp @@ -11,6 +11,15 @@ // WITHOUT WARRANTIES 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"], 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 index afa633a48b2b..9ef6e0b2dee8 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java @@ -124,8 +124,7 @@ public class AppSearchSessionShimImpl implements AppSearchSessionShim { @NonNull public SearchResultsShim search( @NonNull String queryExpression, @NonNull SearchSpec searchSpec) { - SearchResults searchResults = - mAppSearchSession.search(queryExpression, searchSpec, mExecutor); + SearchResults searchResults = mAppSearchSession.search(queryExpression, searchSpec); return new SearchResultsShimImpl(searchResults, mExecutor); } 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 index 6595d8d4abba..69a4c18c4028 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java @@ -75,8 +75,7 @@ public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim { @Override public SearchResultsShim search( @NonNull String queryExpression, @NonNull SearchSpec searchSpec) { - SearchResults searchResults = - mGlobalSearchSession.search(queryExpression, searchSpec, mExecutor); + SearchResults searchResults = mGlobalSearchSession.search(queryExpression, searchSpec); return new SearchResultsShimImpl(searchResults, mExecutor); } 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 index 75add81c8d64..5f26e8cba585 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java @@ -47,7 +47,7 @@ public class SearchResultsShimImpl implements SearchResultsShim { @NonNull public ListenableFuture<List<SearchResult>> getNextPage() { SettableFuture<AppSearchResult<List<SearchResult>>> future = SettableFuture.create(); - mSearchResults.getNextPage(future::set); + mSearchResults.getNextPage(mExecutor, future::set); return Futures.transform(future, AppSearchResult::getResultValue, mExecutor); } 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 index ea21e19b2bea..1428fb1d3c7a 100644 --- 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 @@ -33,91 +33,19 @@ import java.util.Set; public interface AppSearchSessionShim extends Closeable { /** - * Sets the schema that will be used by documents provided to the {@link #put} method. + * Sets the schema that represents the organizational structure of data within the AppSearch + * database. * - * <p>The schema provided here is compared to the stored copy of the schema previously supplied - * to {@link #setSchema}, if any, to determine how to treat existing documents. The following - * types of schema modifications are always safe and are made without deleting any existing - * documents: + * <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. * - * <ul> - * <li>Addition of new types - * <li>Addition of new {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or - * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} properties to a - * type - * <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} property. - * </ul> - * - * <p>The following types of schema changes are not backwards-compatible: - * - * <ul> - * <li>Removal of an existing type - * <li>Removal of a property from a type - * <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property - * <li>For properties of {@code Document} type, changing the schema type of {@code Document}s - * of that property - * <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link - * AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property). - * <li>Adding a {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property. - * </ul> - * - * <p>Supplying a schema with such changes will, by default, result in this call completing its - * future with an {@link android.app.appsearch.exceptions.AppSearchException} with a code of - * {@link AppSearchResult#RESULT_INVALID_SCHEMA} and a message describing the incompatibility. - * In this case the previously set schema will remain active. - * - * <p>If you need to make non-backwards-compatible changes as described above, you can either: - * - * <ul> - * <li>Set the {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In - * this case, instead of completing its future with an {@link - * android.app.appsearch.exceptions.AppSearchException} with the {@link - * AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not - * compatible with the new schema will be deleted and the incompatible schema will be - * applied. Incompatible types and deleted types will be set into {@link - * SetSchemaResponse#getIncompatibleTypes()} and {@link - * SetSchemaResponse#getDeletedTypes()}, respectively. - * <li>Add a {@link android.app.appsearch.AppSearchSchema.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> - * - * <p>It is a no-op to set the same schema as has been previously set; this is handled - * efficiently. - * - * <p>By default, documents are visible on platform surfaces. To opt out, call - * {@link SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi} with {@code visible} as - * false. Any visibility settings apply only to the schemas that are included in the - * {@code request}. Visibility settings for a schema type do not persist across - * {@link #setSchema} calls. - * - * <p>Migration: make non-backwards-compatible changes will delete all stored documents in old - * schema. You can save your documents by setting {@link - * android.app.appsearch.AppSearchSchema.Migrator} via the {@link - * SetSchemaRequest.Builder#setMigrator} for each type you want to save. - * - * <p>{@link android.app.appsearch.AppSearchSchema.Migrator#onDowngrade} or {@link - * android.app.appsearch.AppSearchSchema.Migrator#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 - * android.app.appsearch.AppSearchSchema.Migrator#onDowngrade}, {@link - * android.app.appsearch.AppSearchSchema.Migrator#onUpgrade} or {@link - * android.app.appsearch.AppSearchMigrationHelper.Transformer#transform}, the migration will be - * terminated, the setSchema request will be rejected unless the schema changes are - * backwards-compatible, and stored documents won't have any observable changes. - * - * @param request The schema update request. - * @return A {@link ListenableFuture} with exception if we hit any error. Or the pending {@link - * SetSchemaResponse} of performing this operation, if the schema has been successfully set. - * @see android.app.appsearch.AppSearchSchema.Migrator - * @see android.app.appsearch.AppSearchMigrationHelper.Transformer + * @param request the schema to set or update the AppSearch database to. + * @return a {@link ListenableFuture} which resolves to a {@link SetSchemaResponse} object. */ + // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are + // exposed. @NonNull ListenableFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest request); @@ -132,15 +60,17 @@ public interface AppSearchSessionShim extends Closeable { ListenableFuture<Set<AppSearchSchema>> getSchema(); /** - * Indexes documents into AppSearch. + * Indexes documents into the {@link AppSearchSessionShim} database. * - * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a - * schema type previously registered via the {@link #setSchema} method. + * <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 {@link PutDocumentsRequest} containing documents to be indexed - * @return The pending result of performing this operation. The keys of the returned {@link - * AppSearchBatchResult} are the URIs of the input documents. The values are {@code null} if - * they were successfully indexed, or a failed {@link AppSearchResult} otherwise. + * @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 URIs 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); @@ -213,7 +143,7 @@ public interface AppSearchSessionShim extends Closeable { * 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()}. + * SearchResultsShim#getNextPage}. * * @param queryExpression query string to search. * @param searchSpec spec for setting document filters, adding projection, setting term match 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 index 31c934f8bb27..d912c08e7d5f 100644 --- 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 @@ -37,11 +37,11 @@ public interface GlobalSearchSessionShim extends Closeable { * SetSchemaRequest.Builder#setSchemaTypeVisibilityForSystemUi}, or {@link * SetSchemaRequest.Builder#setDocumentClassVisibilityForSystemUi} when building a schema. * - * <p>See {@link AppSearchSessionShim#search(String, SearchSpec)} for a detailed explanation on - * forming a query string. + * <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()}. + * SearchResultsShim#getNextPage}. * * @param queryExpression query string to search. * @param searchSpec spec for setting document filters, adding projection, setting term match 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 index 328c65ca2727..38f61f83d24e 100644 --- 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 @@ -24,25 +24,29 @@ import java.io.Closeable; import java.util.List; /** - * SearchResultsShim are a returned object from a query API. + * Encapsulates results of a search operation. * - * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets based - * on request. + * <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>Should close this object after finish fetching results. + * <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 { /** - * Gets a whole page of {@link SearchResult}s. + * Retrieves the next page of {@link SearchResult} objects. * - * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an empty - * list. + * <p>The page size is configured by {@link SearchSpec.Builder#setResultCountPerPage}. * - * <p>The page size is set 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 The pending result of performing this operation. + * @return a {@link ListenableFuture} which resolves to a list of {@link SearchResult} objects. */ @NonNull ListenableFuture<List<SearchResult>> getNextPage(); diff --git a/apex/blobstore/framework/Android.bp b/apex/blobstore/framework/Android.bp index 349955368b17..1cb295ba0822 100644 --- a/apex/blobstore/framework/Android.bp +++ b/apex/blobstore/framework/Android.bp @@ -12,6 +12,15 @@ // 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-blobstore-sources", srcs: [ diff --git a/apex/blobstore/service/Android.bp b/apex/blobstore/service/Android.bp index f6cbac1628da..49cfd07443db 100644 --- a/apex/blobstore/service/Android.bp +++ b/apex/blobstore/service/Android.bp @@ -12,6 +12,15 @@ // 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: "service-blobstore", installable: true, diff --git a/apex/jobscheduler/framework/Android.bp b/apex/jobscheduler/framework/Android.bp index 6650e677544b..b51c2aae0bb8 100644 --- a/apex/jobscheduler/framework/Android.bp +++ b/apex/jobscheduler/framework/Android.bp @@ -1,3 +1,12 @@ +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-jobscheduler-sources", srcs: [ @@ -22,6 +31,7 @@ java_library { ], }, libs: [ + "app-compat-annotations", "framework-minus-apex", "unsupportedappusage", ], diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 7851087bac19..77146e0d1282 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -16,11 +16,14 @@ package android.app; +import android.Manifest; import android.annotation.IntDef; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; @@ -183,6 +186,25 @@ public class AlarmManager { @UnsupportedAppUsage public static final int FLAG_IDLE_UNTIL = 1<<4; + /** + * Flag for alarms: Used to provide backwards compatibility for apps with targetSdkVersion less + * than {@link Build.VERSION_CODES#S} + * @hide + */ + public static final int FLAG_ALLOW_WHILE_IDLE_COMPAT = 1 << 5; + + /** + * For apps targeting {@link Build.VERSION_CODES#S} or above, APIs + * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and + * {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} will require holding a new + * permission {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM} + * + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) + public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L; + @UnsupportedAppUsage private final IAlarmManager mService; private final Context mContext; @@ -588,6 +610,11 @@ public class AlarmManager { * This method is like {@link #setExact(int, long, PendingIntent)}, but implies * {@link #RTC_WAKEUP}. * + * <p> + * Starting from API {@link Build.VERSION_CODES#S}, using this method requires the + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission. Alarms scheduled via this API + * will be allowed to start a foreground service even if the app is in the background. + * * @param info * @param operation Action to perform when the alarm goes off; * typically comes from {@link PendingIntent#getBroadcast @@ -603,6 +630,7 @@ public class AlarmManager { * @see android.content.Context#registerReceiver * @see android.content.Intent#filterEquals */ + @RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM) public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) { setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation, null, null, null, null, info); @@ -876,6 +904,12 @@ public class AlarmManager { * device is idle it may take even more liberties with scheduling in order to optimize * for battery life.</p> * + * <p> + * Starting from API {@link Build.VERSION_CODES#S}, using this method requires the + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission, unless the app is exempt from + * battery restrictions. Alarms scheduled via this API will be allowed to start a foreground + * service even if the app is in the background. + * * @param type type of alarm. * @param triggerAtMillis time in milliseconds that the alarm should go * off, using the appropriate clock (depending on the alarm type). @@ -895,6 +929,7 @@ public class AlarmManager { * @see #RTC * @see #RTC_WAKEUP */ + @RequiresPermission(value = Manifest.permission.SCHEDULE_EXACT_ALARM, conditional = true) public void setExactAndAllowWhileIdle(@AlarmType int type, long triggerAtMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation, @@ -1018,6 +1053,18 @@ public class AlarmManager { } /** + * Called to check if the caller has permission to use alarms set via {@link } + * @return + */ + public boolean canScheduleExactAlarms() { + try { + return mService.canScheduleExactAlarms(); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Gets information about the next alarm clock currently scheduled. * * The alarm clocks considered are those scheduled by any application diff --git a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl index 2c51935dc446..2f21ce395df5 100644 --- a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl +++ b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl @@ -41,4 +41,5 @@ interface IAlarmManager { @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) AlarmManager.AlarmClockInfo getNextAlarmClock(int userId); long currentNetworkTimeMillis(); + boolean canScheduleExactAlarms(); } diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 930415fcd8fd..b7a3f1083176 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -59,6 +59,12 @@ import java.util.Objects; * constraint on the JobInfo object that you are creating. Otherwise, the builder would throw an * exception when building. From Android version {@link Build.VERSION_CODES#Q} and onwards, it is * valid to schedule jobs with no constraints. + * <p> In Android version {@link Build.VERSION_CODES#LOLLIPOP}, jobs had a maximum execution time + * of one minute. Starting with Android version {@link Build.VERSION_CODES#M} and ending with + * Android version {@link Build.VERSION_CODES#R}, jobs had a maximum execution time of 10 minutes. + * Starting from Android version {@link Build.VERSION_CODES#S}, jobs will still be stopped after + * 10 minutes if the system is busy or needs the resources, but if not, jobs may continue running + * longer than 10 minutes. */ public class JobInfo implements Parcelable { private static String TAG = "JobInfo"; @@ -1461,11 +1467,13 @@ public class JobInfo implements Parcelable { * possible with stronger guarantees than regular jobs. These "expedited" jobs will: * <ol> * <li>Run as soon as possible</li> - * <li>Be exempted from Doze and battery saver restrictions</li> + * <li>Be less restricted during Doze and battery saver</li> * <li>Have network access</li> - * <li>Less likely to be killed than regular jobs</li> + * <li>Be less likely to be killed than regular jobs</li> + * <li>Be subject to background location throttling</li> * </ol> * + * <p> * Since these jobs have stronger guarantees than regular jobs, they will be subject to * stricter quotas. As long as an app has available expedited quota, jobs scheduled with * this set to true will run with these guarantees. If an app has run out of available @@ -1475,9 +1483,18 @@ public class JobInfo implements Parcelable { * will immediately return {@link JobScheduler#RESULT_FAILURE} if the app does not have * available quota (and the job will not be successfully scheduled). * + * <p> * Expedited jobs may only set network, storage-not-low, and persistence constraints. * No other constraints are allowed. * + * <p> + * Assuming all constraints remain satisfied (including ideal system load conditions), + * expedited jobs are guaranteed to have a minimum allowed runtime of 1 minute. If your + * app has remaining expedited job quota, then the expedited job <i>may</i> potentially run + * longer until remaining quota is used up. Just like with regular jobs, quota is not + * consumed while the app is on top and visible to the user. + * + * <p> * Note: Even though expedited jobs are meant to run as soon as possible, they may be * deferred if the system is under heavy load or requested constraints are not satisfied. * diff --git a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl index 5693abe4d4e1..43d4873a3540 100644 --- a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl +++ b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl @@ -43,10 +43,10 @@ interface IDeviceIdleController { boolean isPowerSaveWhitelistApp(String name); @UnsupportedAppUsage(maxTargetSdk = 30, publicAlternatives = "Use SystemApi {@code PowerWhitelistManager#whitelistAppTemporarily(String, int, String)}.") - void addPowerSaveTempWhitelistApp(String name, long duration, int userId, String reason); - long addPowerSaveTempWhitelistAppForMms(String name, int userId, String reason); - long addPowerSaveTempWhitelistAppForSms(String name, int userId, String reason); - long whitelistAppTemporarily(String name, int userId, String reason); + void addPowerSaveTempWhitelistApp(String name, long duration, int userId, int reasonCode, String reason); + long addPowerSaveTempWhitelistAppForMms(String name, int userId, int reasonCode, String reason); + long addPowerSaveTempWhitelistAppForSms(String name, int userId, int reasonCode, String reason); + long whitelistAppTemporarily(String name, int userId, int reasonCode, String reason); void exitIdle(String reason); int setPreIdleTimeoutMode(int Mode); void resetPreIdleTimeoutMode(); diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java index 283e933e0fa9..df690d00a322 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java @@ -16,8 +16,16 @@ package android.os; +import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; +import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT; +import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI; +import static android.app.ActivityManager.PROCESS_STATE_TOP; + import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -87,13 +95,246 @@ public class PowerWhitelistManager { * The list of temp allowlist types. * @hide */ - @IntDef(flag = true, prefix = { "TEMPORARY_ALLOW_TYPE_" }, value = { + @IntDef(flag = true, prefix = { "TEMPORARY_ALLOWLIST_TYPE_" }, value = { TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED, }) @Retention(RetentionPolicy.SOURCE) public @interface TempAllowListType {} + /* Reason code for BG-FGS-launch. */ + /** + * BG-FGS-launch is denied. + * @hide + */ + public static final int REASON_DENIED = -1; + /** + * The default reason code if reason is unknown. + */ + public static final int REASON_UNKNOWN = 0; + /** + * Use REASON_OTHER if there is no better choice. + */ + public static final int REASON_OTHER = 1; + /** @hide */ + public static final int REASON_PROC_STATE_PERSISTENT = 10; + /** @hide */ + public static final int REASON_PROC_STATE_PERSISTENT_UI = 11; + /** @hide */ + public static final int REASON_PROC_STATE_TOP = 12; + /** @hide */ + public static final int REASON_PROC_STATE_BTOP = 13; + /** @hide */ + public static final int REASON_PROC_STATE_FGS = 14; + /** @hide */ + public static final int REASON_PROC_STATE_BFGS = 15; + /** @hide */ + public static final int REASON_UID_VISIBLE = 50; + /** @hide */ + public static final int REASON_SYSTEM_UID = 51; + /** @hide */ + public static final int REASON_ACTIVITY_STARTER = 52; + /** @hide */ + public static final int REASON_START_ACTIVITY_FLAG = 53; + /** @hide */ + public static final int REASON_FGS_BINDING = 54; + /** @hide */ + public static final int REASON_DEVICE_OWNER = 55; + /** @hide */ + public static final int REASON_PROFILE_OWNER = 56; + /** @hide */ + public static final int REASON_COMPANION_DEVICE_MANAGER = 57; + /** + * START_ACTIVITIES_FROM_BACKGROUND permission. + * @hide + */ + public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION = 58; + /** + * START_FOREGROUND_SERVICES_FROM_BACKGROUND permission. + * @hide + */ + public static final int REASON_BACKGROUND_FGS_PERMISSION = 59; + /** @hide */ + public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 60; + /** @hide */ + public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION = 61; + /** @hide */ + public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION = 62; + /** @hide */ + public static final int REASON_DEVICE_DEMO_MODE = 63; + /** @hide */ + public static final int REASON_EXEMPTED_PACKAGE = 64; + /** @hide */ + public static final int REASON_ALLOWLISTED_PACKAGE = 65; + /** + * If it's because of a role, + * @hide + */ + public static final int REASON_APPOP = 66; + + /* BG-FGS-launch is allowed by temp-allowlist or system-allowlist. + Reason code for temp and system allowlist starts here. + */ + public static final int REASON_GEOFENCING = 100; + public static final int REASON_PUSH_MESSAGING = 101; + public static final int REASON_ACTIVITY_RECOGNITION = 102; + + /** + * Broadcast ACTION_BOOT_COMPLETED. + * @hide + */ + public static final int REASON_BOOT_COMPLETED = 103; + /** + * Broadcast ACTION_PRE_BOOT_COMPLETED. + * @hide + */ + public static final int REASON_PRE_BOOT_COMPLETED = 104; + + /** + * Broadcast ACTION_LOCKED_BOOT_COMPLETED. + * @hide + */ + public static final int REASON_LOCKED_BOOT_COMPLETED = 105; + /** + * Device idle system allowlist, including EXCEPT-IDLE + * @hide + */ + public static final int REASON_SYSTEM_ALLOW_LISTED = 106; + /** @hide */ + public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 107; + /** + * AlarmManagerService. + * @hide + */ + public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 108; + /** + * ActiveServices. + * @hide + */ + public static final int REASON_SERVICE_LAUNCH = 109; + /** + * KeyChainSystemService. + * @hide + */ + public static final int REASON_KEY_CHAIN = 110; + /** + * PackageManagerService. + * @hide + */ + public static final int REASON_PACKAGE_VERIFIER = 111; + /** + * SyncManager. + * @hide + */ + public static final int REASON_SYNC_MANAGER = 112; + /** + * DomainVerificationProxyV1. + * @hide + */ + public static final int REASON_DOMAIN_VERIFICATION_V1 = 113; + /** + * DomainVerificationProxyV2. + * @hide + */ + public static final int REASON_DOMAIN_VERIFICATION_V2 = 114; + /** @hide */ + public static final int REASON_VPN = 115; + /** + * NotificationManagerService. + * @hide + */ + public static final int REASON_NOTIFICATION_SERVICE = 116; + /** + * Broadcast ACTION_MY_PACKAGE_REPLACED. + * @hide + */ + public static final int REASON_PACKAGE_REPLACED = 117; + /** + * LocationProviderManager. + * @hide + */ + public static final int REASON_LOCATION_PROVIDER = 118; + /** + * MediaButtonReceiver. + * @hide + */ + public static final int REASON_MEDIA_BUTTON = 119; + /** + * InboundSmsHandler. + * @hide + */ + public static final int REASON_EVENT_SMS = 120; + /** + * InboundSmsHandler. + * @hide + */ + public static final int REASON_EVENT_MMS = 121; + /** + * Shell app. + * @hide + */ + public static final int REASON_SHELL = 122; + + /** + * The list of BG-FGS-Launch and temp-allowlist reason code. + * @hide + */ + @IntDef(flag = true, prefix = { "REASON_" }, value = { + // BG-FGS-Launch reasons. + REASON_DENIED, + REASON_UNKNOWN, + REASON_OTHER, + REASON_PROC_STATE_PERSISTENT, + REASON_PROC_STATE_PERSISTENT_UI, + REASON_PROC_STATE_TOP, + REASON_PROC_STATE_BTOP, + REASON_PROC_STATE_FGS, + REASON_PROC_STATE_BFGS, + REASON_UID_VISIBLE, + REASON_SYSTEM_UID, + REASON_ACTIVITY_STARTER, + REASON_START_ACTIVITY_FLAG, + REASON_FGS_BINDING, + REASON_DEVICE_OWNER, + REASON_PROFILE_OWNER, + REASON_COMPANION_DEVICE_MANAGER, + REASON_BACKGROUND_ACTIVITY_PERMISSION, + REASON_BACKGROUND_FGS_PERMISSION, + REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION, + REASON_INSTR_BACKGROUND_FGS_PERMISSION, + REASON_SYSTEM_ALERT_WINDOW_PERMISSION, + REASON_DEVICE_DEMO_MODE, + REASON_EXEMPTED_PACKAGE, + REASON_ALLOWLISTED_PACKAGE, + REASON_APPOP, + // temp and system allowlist reasons. + REASON_GEOFENCING, + REASON_PUSH_MESSAGING, + REASON_ACTIVITY_RECOGNITION, + REASON_BOOT_COMPLETED, + REASON_PRE_BOOT_COMPLETED, + REASON_LOCKED_BOOT_COMPLETED, + REASON_SYSTEM_ALLOW_LISTED, + REASON_ALARM_MANAGER_ALARM_CLOCK, + REASON_ALARM_MANAGER_WHILE_IDLE, + REASON_SERVICE_LAUNCH, + REASON_KEY_CHAIN, + REASON_PACKAGE_VERIFIER, + REASON_SYNC_MANAGER, + REASON_DOMAIN_VERIFICATION_V1, + REASON_DOMAIN_VERIFICATION_V2, + REASON_VPN, + REASON_NOTIFICATION_SERVICE, + REASON_PACKAGE_REPLACED, + REASON_LOCATION_PROVIDER, + REASON_MEDIA_BUTTON, + REASON_EVENT_SMS, + REASON_EVENT_MMS, + REASON_SHELL, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ReasonCode {} + /** * @hide */ @@ -184,19 +425,34 @@ public class PowerWhitelistManager { * * @param packageName The package to add to the temp whitelist * @param durationMs How long to keep the app on the temp whitelist for (in milliseconds) + * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure. + * @param reason a optional human readable reason string, could be null or empty string. */ @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) - public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) { - String reason = "from:" + UserHandle.formatUid(Binder.getCallingUid()); + public void whitelistAppTemporarily(@NonNull String packageName, long durationMs, + @ReasonCode int reasonCode, @Nullable String reason) { try { mService.addPowerSaveTempWhitelistApp(packageName, durationMs, mContext.getUserId(), - reason); + reasonCode, reason); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** + * Add an app to the temporary whitelist for a short amount of time. + * + * @param packageName The package to add to the temp whitelist + * @param durationMs How long to keep the app on the temp whitelist for (in milliseconds) + * @deprecated Use {@link #whitelistAppTemporarily(String, long, int, String)} instead + */ + @Deprecated + @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) + public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) { + whitelistAppTemporarily(packageName, durationMs, REASON_UNKNOWN, packageName); + } + + /** * Add an app to the temporary whitelist for a short amount of time for a specific reason. The * temporary whitelist is kept separately from the permanent whitelist and apps are * automatically removed from the temporary whitelist after a predetermined amount of time. @@ -204,27 +460,179 @@ public class PowerWhitelistManager { * @param packageName The package to add to the temp whitelist * @param event The reason to add the app to the temp whitelist * @param reason A human-readable reason explaining why the app is temp whitelisted. Only - * used for logging purposes + * used for logging purposes. Could be null or empty string. * @return The duration (in milliseconds) that the app is whitelisted for + * @deprecated Use {@link #whitelistAppTemporarilyForEvent(String, int, int, String)} instead */ + @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String packageName, - @WhitelistEvent int event, @NonNull String reason) { + @WhitelistEvent int event, @Nullable String reason) { + return whitelistAppTemporarilyForEvent(packageName, event, REASON_UNKNOWN, reason); + } + + /** + * Add an app to the temporary whitelist for a short amount of time for a specific reason. The + * temporary whitelist is kept separately from the permanent whitelist and apps are + * automatically removed from the temporary whitelist after a predetermined amount of time. + * + * @param packageName The package to add to the temp whitelist + * @param event The reason to add the app to the temp whitelist + * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure. + * @param reason A human-readable reason explaining why the app is temp whitelisted. Only + * used for logging purposes. Could be null or empty string. + * @return The duration (in milliseconds) that the app is whitelisted for + */ + @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) + public long whitelistAppTemporarilyForEvent(@NonNull String packageName, + @WhitelistEvent int event, @ReasonCode int reasonCode, @Nullable String reason) { try { switch (event) { case EVENT_MMS: return mService.addPowerSaveTempWhitelistAppForMms( - packageName, mContext.getUserId(), reason); + packageName, mContext.getUserId(), reasonCode, reason); case EVENT_SMS: return mService.addPowerSaveTempWhitelistAppForSms( - packageName, mContext.getUserId(), reason); + packageName, mContext.getUserId(), reasonCode, reason); case EVENT_UNSPECIFIED: default: return mService.whitelistAppTemporarily( - packageName, mContext.getUserId(), reason); + packageName, mContext.getUserId(), reasonCode, reason); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + + /** + * @hide + */ + public static @ReasonCode int getReasonCodeFromProcState(int procState) { + if (procState <= PROCESS_STATE_PERSISTENT) { + return REASON_PROC_STATE_PERSISTENT; + } else if (procState <= PROCESS_STATE_PERSISTENT_UI) { + return REASON_PROC_STATE_PERSISTENT_UI; + } else if (procState <= PROCESS_STATE_TOP) { + return REASON_PROC_STATE_TOP; + } else if (procState <= PROCESS_STATE_BOUND_TOP) { + return REASON_PROC_STATE_BTOP; + } else if (procState <= PROCESS_STATE_FOREGROUND_SERVICE) { + return REASON_PROC_STATE_FGS; + } else if (procState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + return REASON_PROC_STATE_BFGS; + } else { + return REASON_DENIED; + } + } + + /** + * Return string name of the integer reason code. + * @hide + * @param reasonCode + * @return string name of the reason code. + */ + public static String reasonCodeToString(@ReasonCode int reasonCode) { + switch (reasonCode) { + case REASON_DENIED: + return "DENIED"; + case REASON_UNKNOWN: + return "UNKNOWN"; + case REASON_OTHER: + return "OTHER"; + case REASON_PROC_STATE_PERSISTENT: + return "PROC_STATE_PERSISTENT"; + case REASON_PROC_STATE_PERSISTENT_UI: + return "PROC_STATE_PERSISTENT_UI"; + case REASON_PROC_STATE_TOP: + return "PROC_STATE_TOP"; + case REASON_PROC_STATE_BTOP: + return "PROC_STATE_BTOP"; + case REASON_PROC_STATE_FGS: + return "PROC_STATE_FGS"; + case REASON_PROC_STATE_BFGS: + return "PROC_STATE_BFGS"; + case REASON_UID_VISIBLE: + return "UID_VISIBLE"; + case REASON_SYSTEM_UID: + return "SYSTEM_UID"; + case REASON_ACTIVITY_STARTER: + return "ACTIVITY_STARTER"; + case REASON_START_ACTIVITY_FLAG: + return "START_ACTIVITY_FLAG"; + case REASON_FGS_BINDING: + return "FGS_BINDING"; + case REASON_DEVICE_OWNER: + return "DEVICE_OWNER"; + case REASON_PROFILE_OWNER: + return "PROFILE_OWNER"; + case REASON_COMPANION_DEVICE_MANAGER: + return "COMPANION_DEVICE_MANAGER"; + case REASON_BACKGROUND_ACTIVITY_PERMISSION: + return "BACKGROUND_ACTIVITY_PERMISSION"; + case REASON_BACKGROUND_FGS_PERMISSION: + return "BACKGROUND_FGS_PERMISSION"; + case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION: + return "INSTR_BACKGROUND_ACTIVITY_PERMISSION"; + case REASON_INSTR_BACKGROUND_FGS_PERMISSION: + return "INSTR_BACKGROUND_FGS_PERMISSION"; + case REASON_SYSTEM_ALERT_WINDOW_PERMISSION: + return "SYSTEM_ALERT_WINDOW_PERMISSION"; + case REASON_DEVICE_DEMO_MODE: + return "DEVICE_DEMO_MODE"; + case REASON_EXEMPTED_PACKAGE: + return "EXEMPTED_PACKAGE"; + case REASON_ALLOWLISTED_PACKAGE: + return "ALLOWLISTED_PACKAGE"; + case REASON_APPOP: + return "APPOP"; + case REASON_GEOFENCING: + return "GEOFENCING"; + case REASON_PUSH_MESSAGING: + return "PUSH_MESSAGING"; + case REASON_ACTIVITY_RECOGNITION: + return "ACTIVITY_RECOGNITION"; + case REASON_BOOT_COMPLETED: + return "BOOT_COMPLETED"; + case REASON_PRE_BOOT_COMPLETED: + return "PRE_BOOT_COMPLETED"; + case REASON_LOCKED_BOOT_COMPLETED: + return "LOCKED_BOOT_COMPLETED"; + case REASON_SYSTEM_ALLOW_LISTED: + return "SYSTEM_ALLOW_LISTED"; + case REASON_ALARM_MANAGER_ALARM_CLOCK: + return "ALARM_MANAGER_ALARM_CLOCK"; + case REASON_ALARM_MANAGER_WHILE_IDLE: + return "ALARM_MANAGER_WHILE_IDLE"; + case REASON_SERVICE_LAUNCH: + return "SERVICE_LAUNCH"; + case REASON_KEY_CHAIN: + return "KEY_CHAIN"; + case REASON_PACKAGE_VERIFIER: + return "PACKAGE_VERIFIER"; + case REASON_SYNC_MANAGER: + return "SYNC_MANAGER"; + case REASON_DOMAIN_VERIFICATION_V1: + return "DOMAIN_VERIFICATION_V1"; + case REASON_DOMAIN_VERIFICATION_V2: + return "DOMAIN_VERIFICATION_V2"; + case REASON_VPN: + return "VPN"; + case REASON_NOTIFICATION_SERVICE: + return "NOTIFICATION_SERVICE"; + case REASON_PACKAGE_REPLACED: + return "PACKAGE_REPLACED"; + case REASON_LOCATION_PROVIDER: + return "LOCATION_PROVIDER"; + case REASON_MEDIA_BUTTON: + return "MEDIA_BUTTON"; + case REASON_EVENT_SMS: + return "EVENT_SMS"; + case REASON_EVENT_MMS: + return "EVENT_MMS"; + case REASON_SHELL: + return "SHELL"; + default: + return "(unknown:" + reasonCode + ")"; + } + } } diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java index e045b0fa3a6b..5e5717d11432 100644 --- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java @@ -16,6 +16,8 @@ package com.android.server; +import android.annotation.Nullable; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.PowerWhitelistManager.TempAllowListType; import com.android.server.deviceidle.IDeviceIdleConstraint; @@ -34,6 +36,10 @@ public interface DeviceIdleInternal { void addPowerSaveTempWhitelistApp(int callingUid, String packageName, long duration, int userId, boolean sync, String reason); + void addPowerSaveTempWhitelistApp(int callingUid, String packageName, + long duration, int userId, boolean sync, @ReasonCode int reasonCode, + @Nullable String reason); + /** * Called by ActivityManagerService to directly add UID to DeviceIdleController's temp * allowlist. @@ -41,11 +47,12 @@ public interface DeviceIdleInternal { * @param duration duration in milliseconds * @param type temp allowlist type defined at {@link TempAllowListType} * @param sync + * @param reasonCode one of {@link ReasonCode} * @param reason */ void addPowerSaveTempWhitelistAppDirect(int uid, long duration, - @TempAllowListType int type, boolean sync, - String reason); + @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode, + @Nullable String reason); // duration in milliseconds long getNotificationAllowlistDuration(); diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp index 35bd3175b14b..a4a0b4b29200 100644 --- a/apex/jobscheduler/service/Android.bp +++ b/apex/jobscheduler/service/Android.bp @@ -1,5 +1,14 @@ // Job Scheduler Service jar, which will eventually be put in the jobscheduler mainline apex. // service-jobscheduler needs to be added to PRODUCT_SYSTEM_SERVER_JARS. +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: "service-jobscheduler", installable: true, diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 8f7f705163ba..ac28e828eb2e 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -16,12 +16,17 @@ package com.android.server; +import static android.os.PowerWhitelistManager.REASON_SHELL; +import static android.os.PowerWhitelistManager.REASON_UNKNOWN; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; +import static android.os.Process.INVALID_UID; + import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AlarmManager; -import android.app.BroadcastOptions; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -56,6 +61,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.PowerWhitelistManager.TempAllowListType; import android.os.Process; import android.os.RemoteException; @@ -1838,31 +1844,34 @@ public class DeviceIdleController extends SystemService } @Override - public long whitelistAppTemporarily(String packageName, int userId, String reason) - throws RemoteException { + public long whitelistAppTemporarily(String packageName, int userId, + @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException { // At least 10 seconds. long durationMs = Math.max(10_000L, mConstants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS / 2); - addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason); + addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reasonCode, + reason); return durationMs; } @Override - public void addPowerSaveTempWhitelistApp(String packageName, long duration, - int userId, String reason) throws RemoteException { - addPowerSaveTempAllowlistAppChecked(packageName, duration, userId, reason); + public void addPowerSaveTempWhitelistApp(String packageName, long duration, int userId, + @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException { + addPowerSaveTempAllowlistAppChecked(packageName, duration, userId, reasonCode, reason); } - @Override public long addPowerSaveTempWhitelistAppForMms(String packageName, - int userId, String reason) throws RemoteException { + @Override public long addPowerSaveTempWhitelistAppForMms(String packageName, int userId, + @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException { long durationMs = mConstants.MMS_TEMP_APP_ALLOWLIST_DURATION_MS; - addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason); + addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reasonCode, + reason); return durationMs; } - @Override public long addPowerSaveTempWhitelistAppForSms(String packageName, - int userId, String reason) throws RemoteException { + @Override public long addPowerSaveTempWhitelistAppForSms(String packageName, int userId, + @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException { long durationMs = mConstants.SMS_TEMP_APP_ALLOWLIST_DURATION_MS; - addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reason); + addPowerSaveTempAllowlistAppChecked(packageName, durationMs, userId, reasonCode, + reason); return durationMs; } @@ -1934,18 +1943,29 @@ public class DeviceIdleController extends SystemService } // duration in milliseconds + @Deprecated @Override public void addPowerSaveTempWhitelistApp(int callingUid, String packageName, - long duration, int userId, boolean sync, String reason) { + long duration, int userId, boolean sync, @Nullable String reason) { addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration, - userId, sync, reason); + userId, sync, REASON_UNKNOWN, reason); + } + + @Override + public void addPowerSaveTempWhitelistApp(int callingUid, String packageName, + long duration, int userId, boolean sync, @ReasonCode int reasonCode, + @Nullable String reason) { + addPowerSaveTempAllowlistAppInternal(callingUid, packageName, duration, + userId, sync, reasonCode, reason); } // duration in milliseconds @Override public void addPowerSaveTempWhitelistAppDirect(int uid, long duration, - @TempAllowListType int type, boolean sync, String reason) { - addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync, reason); + @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode, + @Nullable String reason) { + addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync, + reasonCode, reason); } // duration in milliseconds @@ -2293,7 +2313,7 @@ public class DeviceIdleController extends SystemService filter.addAction(Intent.ACTION_SCREEN_ON); getContext().registerReceiver(mInteractivityReceiver, filter); - mLocalActivityManager.setDeviceIdleWhitelist( + mLocalActivityManager.setDeviceIdleAllowlist( mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray); mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray); @@ -2671,7 +2691,8 @@ public class DeviceIdleController extends SystemService } void addPowerSaveTempAllowlistAppChecked(String packageName, long duration, - int userId, String reason) throws RemoteException { + int userId, @ReasonCode int reasonCode, @Nullable String reason) + throws RemoteException { getContext().enforceCallingPermission( Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, "No permission to change device idle whitelist"); @@ -2686,7 +2707,7 @@ public class DeviceIdleController extends SystemService final long token = Binder.clearCallingIdentity(); try { addPowerSaveTempAllowlistAppInternal(callingUid, - packageName, duration, userId, true, reason); + packageName, duration, userId, true, reasonCode, reason); } finally { Binder.restoreCallingIdentity(token); } @@ -2718,12 +2739,12 @@ public class DeviceIdleController extends SystemService * app an exemption to access network and acquire wakelocks. */ void addPowerSaveTempAllowlistAppInternal(int callingUid, String packageName, - long duration, int userId, boolean sync, String reason) { + long duration, int userId, boolean sync, @ReasonCode int reasonCode, + @Nullable String reason) { try { int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId); addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration, - BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED, sync, - reason); + TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, sync, reasonCode, reason); } catch (NameNotFoundException e) { } } @@ -2733,7 +2754,8 @@ public class DeviceIdleController extends SystemService * app an exemption to access network and acquire wakelocks. */ void addPowerSaveTempWhitelistAppDirectInternal(int callingUid, int uid, - long duration, @TempAllowListType int type, boolean sync, String reason) { + long duration, @TempAllowListType int type, boolean sync, @ReasonCode int reasonCode, + @Nullable String reason) { final long timeNow = SystemClock.elapsedRealtime(); boolean informWhitelistChanged = false; int appId = UserHandle.getAppId(uid); @@ -2765,7 +2787,8 @@ public class DeviceIdleController extends SystemService } catch (RemoteException e) { } postTempActiveTimeoutMessage(uid, duration); - updateTempWhitelistAppIdsLocked(uid, true, duration, type); + updateTempWhitelistAppIdsLocked(uid, true, duration, type, reasonCode, + reason, callingUid); if (sync) { informWhitelistChanged = true; } else { @@ -2844,12 +2867,13 @@ public class DeviceIdleController extends SystemService } @GuardedBy("this") - private void onAppRemovedFromTempWhitelistLocked(int uid, String reason) { + private void onAppRemovedFromTempWhitelistLocked(int uid, @Nullable String reason) { if (DEBUG) { Slog.d(TAG, "Removing uid " + uid + " from temp whitelist"); } final int appId = UserHandle.getAppId(uid); - updateTempWhitelistAppIdsLocked(uid, false, 0, 0); + updateTempWhitelistAppIdsLocked(uid, false, 0, 0, REASON_UNKNOWN, + reason, INVALID_UID); mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS, appId, 0) .sendToTarget(); reportTempWhitelistChangedLocked(uid, false); @@ -3860,7 +3884,7 @@ public class DeviceIdleController extends SystemService mPowerSaveWhitelistUserAppIdArray = buildAppIdArray(null, mPowerSaveWhitelistUserApps, mPowerSaveWhitelistUserAppIds); if (mLocalActivityManager != null) { - mLocalActivityManager.setDeviceIdleWhitelist( + mLocalActivityManager.setDeviceIdleAllowlist( mPowerSaveWhitelistAllAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray); } if (mLocalPowerManager != null) { @@ -3880,9 +3904,14 @@ public class DeviceIdleController extends SystemService * @param durationMs duration in milliseconds to add to temp allowlist, only valid when * param adding is true. * @param type temp allowlist type defined at {@link TempAllowListType} + * @prama reasonCode one of {@Link ReasonCode} + * @param reason A human-readable reason for logging purposes. + * @param callingUid the callingUid that setup this temp-allowlist, only valid when param adding + * is true. */ private void updateTempWhitelistAppIdsLocked(int uid, boolean adding, long durationMs, - @TempAllowListType int type) { + @TempAllowListType int type, @ReasonCode int reasonCode, @Nullable String reason, + int callingUid) { final int size = mTempWhitelistAppIdEndTimes.size(); if (mTempWhitelistAppIdArray.length != size) { mTempWhitelistAppIdArray = new int[size]; @@ -3895,8 +3924,8 @@ public class DeviceIdleController extends SystemService Slog.d(TAG, "Setting activity manager temp whitelist to " + Arrays.toString(mTempWhitelistAppIdArray)); } - mLocalActivityManager.updateDeviceIdleTempWhitelist(mTempWhitelistAppIdArray, uid, - adding, durationMs, type); + mLocalActivityManager.updateDeviceIdleTempAllowlist(mTempWhitelistAppIdArray, uid, + adding, durationMs, type, reasonCode, reason, callingUid); } if (mLocalPowerManager != null) { if (DEBUG) { @@ -4428,7 +4457,8 @@ public class DeviceIdleController extends SystemService if (removePkg) { removePowerSaveTempAllowlistAppChecked(arg, shell.userId); } else { - addPowerSaveTempAllowlistAppChecked(arg, duration, shell.userId, "shell"); + addPowerSaveTempAllowlistAppChecked(arg, duration, shell.userId, + REASON_SHELL, "shell"); } } catch (Exception e) { pw.println("Failed: " + e); diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java index 657c368d0aee..3bc7b307c334 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java @@ -26,6 +26,7 @@ import static com.android.server.alarm.AlarmManagerService.clampPositive; import android.app.AlarmManager; import android.app.IAlarmListener; import android.app.PendingIntent; +import android.os.Bundle; import android.os.WorkSource; import android.util.IndentingPrintWriter; import android.util.TimeUtils; @@ -92,10 +93,12 @@ class Alarm { private long mWhenElapsed; private long mMaxWhenElapsed; public AlarmManagerService.PriorityClass priorityClass; + /** Broadcast options to use when delivering this alarm */ + public Bundle mIdleOptions; Alarm(int type, long when, long requestedWhenElapsed, long windowLength, long interval, PendingIntent op, IAlarmListener rec, String listenerTag, WorkSource ws, int flags, - AlarmManager.AlarmClockInfo info, int uid, String pkgName) { + AlarmManager.AlarmClockInfo info, int uid, String pkgName, Bundle idleOptions) { this.type = type; origWhen = when; wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP @@ -115,6 +118,7 @@ class Alarm { alarmClock = info; this.uid = uid; packageName = pkgName; + mIdleOptions = idleOptions; sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName; creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid; } @@ -303,6 +307,10 @@ class Alarm { ipw.print("listener="); ipw.println(listener.asBinder()); } + if (mIdleOptions != null) { + ipw.print("idle-options="); + ipw.println(mIdleOptions.toString()); + } } public void dumpDebug(ProtoOutputStream proto, long fieldId, long nowElapsed) { 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 f6a1b8af9e49..2b2918c0a6f0 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -20,11 +20,17 @@ import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.AlarmManager.ELAPSED_REALTIME; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE; +import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT; import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; import static android.app.AlarmManager.FLAG_IDLE_UNTIL; +import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE; +import static android.app.AlarmManager.INTERVAL_HOUR; import static android.app.AlarmManager.RTC; import static android.app.AlarmManager.RTC_WAKEUP; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; +import static android.os.PowerWhitelistManager.REASON_ALARM_MANAGER_WHILE_IDLE; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED; import static android.os.UserHandle.USER_SYSTEM; import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX; @@ -32,6 +38,7 @@ import static com.android.server.alarm.Alarm.BATTERY_SAVER_POLICY_INDEX; import static com.android.server.alarm.Alarm.DEVICE_IDLE_POLICY_INDEX; import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX; +import android.Manifest; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.Activity; @@ -43,12 +50,14 @@ import android.app.IAlarmCompleteListener; import android.app.IAlarmListener; import android.app.IAlarmManager; import android.app.PendingIntent; +import android.app.compat.CompatChanges; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.PermissionChecker; import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.BatteryManager; @@ -65,6 +74,7 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; +import android.os.ServiceManager; import android.os.ShellCallback; import android.os.ShellCommand; import android.os.SystemClock; @@ -95,6 +105,8 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.IAppOpsCallback; +import com.android.internal.app.IAppOpsService; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.LocalLog; @@ -177,6 +189,7 @@ public class AlarmManagerService extends SystemService { final LocalLog mLog = new LocalLog(TAG); AppOpsManager mAppOps; + IAppOpsService mAppOpsService; DeviceIdleInternal mLocalDeviceIdleController; private UsageStatsManagerInternal mUsageStatsManagerInternal; private ActivityManagerInternal mActivityManagerInternal; @@ -253,10 +266,8 @@ public class AlarmManagerService extends SystemService { "REORDER_ALARMS_FOR_STANDBY", }); - /** - * Broadcast options to use for FLAG_ALLOW_WHILE_IDLE. - */ - Bundle mIdleOptions; + BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic(); + BroadcastOptions mOptsWithoutFgs = BroadcastOptions.makeBasic(); // TODO(b/172085676): Move inside alarm store. private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser = @@ -410,6 +421,12 @@ public class AlarmManagerService extends SystemService { @VisibleForTesting static final String KEY_ALLOW_WHILE_IDLE_QUOTA = "allow_while_idle_quota"; + @VisibleForTesting + static final String KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA = "allow_while_idle_compat_quota"; + private static final String KEY_ALLOW_WHILE_IDLE_WINDOW = "allow_while_idle_window"; + + private static final String KEY_CRASH_NON_CLOCK_APPS = "crash_non_clock_apps"; + private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS; @@ -433,8 +450,16 @@ public class AlarmManagerService extends SystemService { private static final boolean DEFAULT_LAZY_BATCHING = true; private static final boolean DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE = true; - private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 7; - public static final long ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour. + /** + * Default quota for pre-S apps. Enough to accommodate the existing policy of an alarm + * every ALLOW_WHILE_IDLE_LONG_DELAY, which was 9 minutes. + */ + private static final int DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA = 7; + private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 72; + + private static final long DEFAULT_ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour. + // TODO (b/171306433): Change to true by default. + private static final boolean DEFAULT_CRASH_NON_CLOCK_APPS = false; // Minimum futurity of a new alarm public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; @@ -463,6 +488,26 @@ public class AlarmManagerService extends SystemService { public int ALLOW_WHILE_IDLE_QUOTA = DEFAULT_ALLOW_WHILE_IDLE_QUOTA; + /** + * Used to provide backwards compatibility to pre-S apps with a quota equivalent to the + * earlier delay throttling mechanism. + */ + public int ALLOW_WHILE_IDLE_COMPAT_QUOTA = DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA; + + /** + * The window used for enforcing {@link #ALLOW_WHILE_IDLE_QUOTA} and + * {@link #ALLOW_WHILE_IDLE_COMPAT_QUOTA}. Can be configured, but only recommended for + * testing. + */ + public long ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW; + + /** + * Whether or not to crash callers that use setExactAndAllowWhileIdle or setAlarmClock + * but don't hold the required permission. This is useful to catch broken + * apps and reverting to a softer failure in case of broken apps. + */ + public boolean CRASH_NON_CLOCK_APPS = DEFAULT_CRASH_NON_CLOCK_APPS; + private long mLastAllowWhileIdleWhitelistDuration = -1; Constants() { @@ -480,9 +525,13 @@ public class AlarmManagerService extends SystemService { public void updateAllowWhileIdleWhitelistDurationLocked() { if (mLastAllowWhileIdleWhitelistDuration != ALLOW_WHILE_IDLE_WHITELIST_DURATION) { mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION; - BroadcastOptions opts = BroadcastOptions.makeBasic(); - opts.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION); - mIdleOptions = opts.toBundle(); + + mOptsWithFgs.setTemporaryAppAllowlist(ALLOW_WHILE_IDLE_WHITELIST_DURATION, + TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + REASON_ALARM_MANAGER_WHILE_IDLE, ""); + mOptsWithoutFgs.setTemporaryAppAllowlist(ALLOW_WHILE_IDLE_WHITELIST_DURATION, + TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED, + REASON_ALARM_MANAGER_WHILE_IDLE, ""); } } @@ -516,6 +565,27 @@ public class AlarmManagerService extends SystemService { ALLOW_WHILE_IDLE_QUOTA = 1; } break; + case KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA: + ALLOW_WHILE_IDLE_COMPAT_QUOTA = properties.getInt( + KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, + DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA); + if (ALLOW_WHILE_IDLE_COMPAT_QUOTA <= 0) { + Slog.w(TAG, "Cannot have quota lower than 1."); + ALLOW_WHILE_IDLE_COMPAT_QUOTA = 1; + } + break; + case KEY_ALLOW_WHILE_IDLE_WINDOW: + ALLOW_WHILE_IDLE_WINDOW = properties.getLong( + KEY_ALLOW_WHILE_IDLE_WINDOW, DEFAULT_ALLOW_WHILE_IDLE_WINDOW); + if (ALLOW_WHILE_IDLE_WINDOW > DEFAULT_ALLOW_WHILE_IDLE_WINDOW) { + Slog.w(TAG, "Cannot have allow_while_idle_window > " + + DEFAULT_ALLOW_WHILE_IDLE_WINDOW); + ALLOW_WHILE_IDLE_WINDOW = DEFAULT_ALLOW_WHILE_IDLE_WINDOW; + } else if (ALLOW_WHILE_IDLE_WINDOW < DEFAULT_ALLOW_WHILE_IDLE_WINDOW) { + Slog.w(TAG, "Using a non-default allow_while_idle_window = " + + ALLOW_WHILE_IDLE_WINDOW); + } + break; case KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION: ALLOW_WHILE_IDLE_WHITELIST_DURATION = properties.getLong( KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, @@ -552,6 +622,10 @@ public class AlarmManagerService extends SystemService { KEY_TIME_TICK_ALLOWED_WHILE_IDLE, DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE); break; + case KEY_CRASH_NON_CLOCK_APPS: + CRASH_NON_CLOCK_APPS = properties.getBoolean(KEY_CRASH_NON_CLOCK_APPS, + DEFAULT_CRASH_NON_CLOCK_APPS); + break; default: if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) { // The quotas need to be updated in order, so we can't just rely @@ -643,10 +717,14 @@ public class AlarmManagerService extends SystemService { TimeUtils.formatDuration(LISTENER_TIMEOUT, pw); pw.println(); - pw.print("allow_while_idle_window="); + pw.print(KEY_ALLOW_WHILE_IDLE_WINDOW); + pw.print("="); TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WINDOW, pw); pw.println(); + pw.print(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, ALLOW_WHILE_IDLE_COMPAT_QUOTA); + pw.println(); + pw.print(KEY_ALLOW_WHILE_IDLE_QUOTA, ALLOW_WHILE_IDLE_QUOTA); pw.println(); @@ -682,6 +760,9 @@ public class AlarmManagerService extends SystemService { pw.print(KEY_TIME_TICK_ALLOWED_WHILE_IDLE, TIME_TICK_ALLOWED_WHILE_IDLE); pw.println(); + pw.print(KEY_CRASH_NON_CLOCK_APPS, CRASH_NON_CLOCK_APPS); + pw.println(); + pw.decreaseIndent(); } @@ -830,7 +911,15 @@ public class AlarmManagerService extends SystemService { if (futurity < MIN_FUZZABLE_INTERVAL) { futurity = 0; } - return clampPositive(triggerAtTime + (long) (.75 * futurity)); + long maxElapsed = triggerAtTime + (long) (0.75 * futurity); + // For non-repeating alarms, window is capped at a maximum of one hour from the requested + // delivery time. This allows for inexact-while-idle alarms to be slightly more reliable. + // In practice, the delivery window should generally be much smaller than that + // when the device is not idling. + if (interval == 0) { + maxElapsed = Math.min(maxElapsed, triggerAtTime + INTERVAL_HOUR); + } + return clampPositive(maxElapsed); } // The RTC clock has moved arbitrarily, so we need to recalculate all the RTC alarm deliveries. @@ -998,7 +1087,7 @@ public class AlarmManagerService extends SystemService { setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed, nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null, null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid, - alarm.packageName); + alarm.packageName, null); // Kernel alarms will be rescheduled as needed in setImplLocked } } @@ -1241,7 +1330,8 @@ public class AlarmManagerService extends SystemService { mAlarmStore.setAlarmClockRemovalListener(mAlarmClockUpdater); mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW); - mAllowWhileIdleHistory = new AppWakeupHistory(Constants.ALLOW_WHILE_IDLE_WINDOW); + mAllowWhileIdleHistory = new AppWakeupHistory( + Constants.DEFAULT_ALLOW_WHILE_IDLE_WINDOW); mNextWakeup = mNextNonWakeup = 0; @@ -1327,6 +1417,28 @@ public class AlarmManagerService extends SystemService { synchronized (mLock) { mConstants.start(); mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); + mAppOpsService = mInjector.getAppOpsService(); + try { + mAppOpsService.startWatchingMode(AppOpsManager.OP_SCHEDULE_EXACT_ALARM, null, + new IAppOpsCallback.Stub() { + @Override + public void opChanged(int op, int uid, String packageName) + throws RemoteException { + if (op != AppOpsManager.OP_SCHEDULE_EXACT_ALARM) { + return; + } + final int mode = mAppOpsService.checkOperation(op, uid, + packageName); + if (mode != AppOpsManager.MODE_ALLOWED + && mode != AppOpsManager.MODE_DEFAULT) { + mHandler.obtainMessage(AlarmHandler.REMOVE_EXACT_ALARMS, + uid, 0, packageName).sendToTarget(); + } + } + }); + } catch (RemoteException e) { + } + mLocalDeviceIdleController = LocalServices.getService(DeviceIdleInternal.class); mUsageStatsManagerInternal = @@ -1428,7 +1540,7 @@ public class AlarmManagerService extends SystemService { void setImpl(int type, long triggerAtTime, long windowLength, long interval, PendingIntent operation, IAlarmListener directReceiver, String listenerTag, int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock, - int callingUid, String callingPackage) { + int callingUid, String callingPackage, Bundle idleOptions) { if ((operation == null && directReceiver == null) || (operation != null && directReceiver != null)) { Slog.w(TAG, "Alarms must either supply a PendingIntent or an AlarmReceiver"); @@ -1519,17 +1631,18 @@ public class AlarmManagerService extends SystemService { } setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, interval, operation, directReceiver, listenerTag, flags, workSource, alarmClock, callingUid, - callingPackage); + callingPackage, idleOptions); } } private void setImplLocked(int type, long when, long whenElapsed, long windowLength, long interval, PendingIntent operation, IAlarmListener directReceiver, String listenerTag, int flags, WorkSource workSource, - AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) { + AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage, + Bundle idleOptions) { final Alarm a = new Alarm(type, when, whenElapsed, windowLength, interval, operation, directReceiver, listenerTag, workSource, flags, alarmClock, - callingUid, callingPackage); + callingUid, callingPackage, idleOptions); if (mActivityManagerInternal.isAppStartModeDisabled(callingUid, callingPackage)) { Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a + " -- package not allowed to start"); @@ -1605,19 +1718,21 @@ public class AlarmManagerService extends SystemService { return false; } - if (!(mAppStateTracker != null && mAppStateTracker.areAlarmsRestrictedByBatterySaver( - alarm.creatorUid, alarm.sourcePackage))) { + if (mAppStateTracker == null || !mAppStateTracker.areAlarmsRestrictedByBatterySaver( + alarm.creatorUid, alarm.sourcePackage)) { return alarm.setPolicyElapsed(BATTERY_SAVER_POLICY_INDEX, nowElapsed); } final long batterySaverPolicyElapsed; - if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) { + if ((alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0) { // Unrestricted. batterySaverPolicyElapsed = nowElapsed; - } else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { + } else if (isAllowedWhileIdleRestricted(alarm)) { // Allowed but limited. final int userId = UserHandle.getUserId(alarm.creatorUid); - final int quota = mConstants.ALLOW_WHILE_IDLE_QUOTA; + final int quota = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0) + ? mConstants.ALLOW_WHILE_IDLE_QUOTA + : mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA; final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow( alarm.sourcePackage, userId); if (dispatchesInWindow < quota) { @@ -1625,7 +1740,7 @@ public class AlarmManagerService extends SystemService { batterySaverPolicyElapsed = nowElapsed; } else { batterySaverPolicyElapsed = mAllowWhileIdleHistory.getNthLastWakeupForPackage( - alarm.sourcePackage, userId, quota) + Constants.ALLOW_WHILE_IDLE_WINDOW; + alarm.sourcePackage, userId, quota) + mConstants.ALLOW_WHILE_IDLE_WINDOW; } } else { // Not allowed. @@ -1635,6 +1750,16 @@ public class AlarmManagerService extends SystemService { } /** + * Returns {@code true} if the given alarm has the flag + * {@link AlarmManager#FLAG_ALLOW_WHILE_IDLE} or + * {@link AlarmManager#FLAG_ALLOW_WHILE_IDLE_COMPAT} + * + */ + private static boolean isAllowedWhileIdleRestricted(Alarm a) { + return (a.flags & (FLAG_ALLOW_WHILE_IDLE | FLAG_ALLOW_WHILE_IDLE_COMPAT)) != 0; + } + + /** * Adjusts the delivery time of the alarm based on device_idle (doze) rules. * * @param alarm The alarm to adjust @@ -1647,14 +1772,15 @@ public class AlarmManagerService extends SystemService { } final long deviceIdlePolicyTime; - if ((alarm.flags & (AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED - | AlarmManager.FLAG_WAKE_FROM_IDLE)) != 0) { + if ((alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_WAKE_FROM_IDLE)) != 0) { // Unrestricted. deviceIdlePolicyTime = nowElapsed; - } else if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { + } else if (isAllowedWhileIdleRestricted(alarm)) { // Allowed but limited. final int userId = UserHandle.getUserId(alarm.creatorUid); - final int quota = mConstants.ALLOW_WHILE_IDLE_QUOTA; + final int quota = ((alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0) + ? mConstants.ALLOW_WHILE_IDLE_QUOTA + : mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA; final int dispatchesInWindow = mAllowWhileIdleHistory.getTotalWakeupsInWindow( alarm.sourcePackage, userId); if (dispatchesInWindow < quota) { @@ -1662,7 +1788,7 @@ public class AlarmManagerService extends SystemService { deviceIdlePolicyTime = nowElapsed; } else { final long whenInQuota = mAllowWhileIdleHistory.getNthLastWakeupForPackage( - alarm.sourcePackage, userId, quota) + Constants.ALLOW_WHILE_IDLE_WINDOW; + alarm.sourcePackage, userId, quota) + mConstants.ALLOW_WHILE_IDLE_WINDOW; deviceIdlePolicyTime = Math.min(whenInQuota, mPendingIdleUntil.getWhenElapsed()); } } else { @@ -1749,7 +1875,7 @@ public class AlarmManagerService extends SystemService { } else if (mPendingIdleUntil != null) { adjustDeliveryTimeBasedOnDeviceIdle(a); } - if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { + if ((a.flags & FLAG_WAKE_FROM_IDLE) != 0) { if (mNextWakeFromIdle == null || mNextWakeFromIdle.getWhenElapsed() > a.getWhenElapsed()) { mNextWakeFromIdle = a; @@ -1810,6 +1936,15 @@ public class AlarmManagerService extends SystemService { } /** + * Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact, + * allow-while-idle alarms. + */ + boolean isExemptFromPermission(int uid) { + return (UserHandle.isSameApp(mSystemUiUid, uid) || mLocalDeviceIdleController == null + || mLocalDeviceIdleController.isAppOnWhitelist(UserHandle.getAppId(uid))); + } + + /** * Public-facing binder interface */ private final IBinder mService = new IAlarmManager.Stub() { @@ -1824,6 +1959,60 @@ public class AlarmManagerService extends SystemService { // wakelock time spent in alarm delivery mAppOps.checkPackage(callingUid, callingPackage); + final boolean allowWhileIdle = (flags & FLAG_ALLOW_WHILE_IDLE) != 0; + + Bundle idleOptions = null; + if (alarmClock != null || allowWhileIdle) { + // make sure the caller is allowed to use the requested kind of alarm, and also + // decide what broadcast options to use. + final boolean needsPermission; + boolean lowQuota; + if (CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, + callingPackage, UserHandle.getUserHandleForUid(callingUid))) { + if (windowLength != AlarmManager.WINDOW_EXACT) { + needsPermission = false; + lowQuota = true; + idleOptions = isExemptFromPermission(callingUid) ? mOptsWithFgs.toBundle() + : mOptsWithoutFgs.toBundle(); + } else if (alarmClock != null) { + needsPermission = true; + lowQuota = false; + idleOptions = mOptsWithFgs.toBundle(); + } else { + needsPermission = true; + lowQuota = false; + idleOptions = mOptsWithFgs.toBundle(); + } + } else { + needsPermission = false; + lowQuota = allowWhileIdle; + idleOptions = allowWhileIdle ? mOptsWithFgs.toBundle() : null; + } + if (needsPermission && !canScheduleExactAlarms()) { + if (alarmClock == null && isExemptFromPermission(callingUid)) { + // If the app is on the full system allow-list (not except-idle), we still + // allow the alarms, but with a lower quota to keep pre-S compatibility. + lowQuota = true; + } else { + final String errorMessage = "Caller " + callingPackage + " needs to hold " + + Manifest.permission.SCHEDULE_EXACT_ALARM + " to set " + + ((allowWhileIdle) ? "exact, allow-while-idle" : "alarm-clock") + + " alarms."; + if (mConstants.CRASH_NON_CLOCK_APPS) { + throw new SecurityException(errorMessage); + } else { + Slog.wtf(TAG, errorMessage); + idleOptions = mOptsWithoutFgs.toBundle(); + lowQuota = allowWhileIdle; + } + } + } + if (lowQuota) { + flags &= ~FLAG_ALLOW_WHILE_IDLE; + flags |= FLAG_ALLOW_WHILE_IDLE_COMPAT; + } + } + // Repeating alarms must use PendingIntent, not direct listener if (interval != 0) { if (directReceiver != null) { @@ -1840,8 +2029,7 @@ public class AlarmManagerService extends SystemService { // No incoming callers can request either WAKE_FROM_IDLE or // ALLOW_WHILE_IDLE_UNRESTRICTED -- we will apply those later as appropriate. - flags &= ~(AlarmManager.FLAG_WAKE_FROM_IDLE - | AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED); + flags &= ~(FLAG_WAKE_FROM_IDLE | FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED); // Only the system can use FLAG_IDLE_UNTIL -- this is used to tell the alarm // manager when to come out of idle mode, which is only for DeviceIdleController. @@ -1857,22 +2045,32 @@ public class AlarmManagerService extends SystemService { // If this alarm is for an alarm clock, then it must be standalone and we will // use it to wake early from idle if needed. if (alarmClock != null) { - flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE; + flags |= FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE; // If the caller is a core system component or on the user's whitelist, and not calling // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED. // This means we will allow these alarms to go off as normal even while idle, with no // timing restrictions. - } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID + } else if (workSource == null && (UserHandle.isCore(callingUid) || UserHandle.isSameApp(callingUid, mSystemUiUid) || ((mAppStateTracker != null) && mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) { - flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; - flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE; + flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; + flags &= ~FLAG_ALLOW_WHILE_IDLE; + flags &= ~FLAG_ALLOW_WHILE_IDLE_COMPAT; + idleOptions = null; } setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver, - listenerTag, flags, workSource, alarmClock, callingUid, callingPackage); + listenerTag, flags, workSource, alarmClock, callingUid, callingPackage, + idleOptions); + } + + @Override + public boolean canScheduleExactAlarms() { + return PermissionChecker.checkCallingOrSelfPermissionForPreflight(getContext(), + Manifest.permission.SCHEDULE_EXACT_ALARM) + == PermissionChecker.PERMISSION_GRANTED; } @Override @@ -2755,6 +2953,77 @@ public class AlarmManagerService extends SystemService { } } + /** + * Called when an app loses {@link Manifest.permission#SCHEDULE_EXACT_ALARM} to remove alarms + * that the app is no longer eligible to use. + * TODO (b/179541791): Revisit and write tests once UX is final. + */ + void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) { + if (UserHandle.isCore(uid) || uid == mSystemUiUid) { + return; + } + if (isExemptFromPermission(uid)) { + return; + } + if (!CompatChanges.isChangeEnabled( + AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, + packageName, UserHandle.getUserHandleForUid(uid))) { + return; + } + + final Predicate<Alarm> whichAlarms = + a -> (a.uid == uid && a.packageName.equals(packageName) + && ((a.flags & FLAG_ALLOW_WHILE_IDLE) != 0 || a.alarmClock != null)); + final ArrayList<Alarm> removed = mAlarmStore.remove(whichAlarms); + final boolean didRemove = !removed.isEmpty(); + if (didRemove) { + decrementAlarmCount(uid, removed.size()); + } + + for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) { + final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i); + for (int j = alarmsForUid.size() - 1; j >= 0; j--) { + final Alarm alarm = alarmsForUid.get(j); + if (whichAlarms.test(alarm)) { + // Don't set didRemove, since this doesn't impact the scheduled alarms. + alarmsForUid.remove(j); + decrementAlarmCount(alarm.uid, 1); + } + } + if (alarmsForUid.size() == 0) { + mPendingBackgroundAlarms.removeAt(i); + } + } + for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) { + final Alarm a = mPendingNonWakeupAlarms.get(i); + if (whichAlarms.test(a)) { + // Don't set didRemove, since this doesn't impact the scheduled alarms. + mPendingNonWakeupAlarms.remove(i); + decrementAlarmCount(a.uid, 1); + } + } + + if (didRemove) { + if (mNextWakeFromIdle != null && whichAlarms.test(mNextWakeFromIdle)) { + mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); + if (mPendingIdleUntil != null) { + final boolean idleUntilUpdated = mAlarmStore.updateAlarmDeliveries(alarm -> { + if (alarm != mPendingIdleUntil) { + return false; + } + return adjustIdleUntilTime(alarm); + }); + if (idleUntilUpdated) { + mAlarmStore.updateAlarmDeliveries( + alarm -> adjustDeliveryTimeBasedOnDeviceIdle(alarm)); + } + } + } + rescheduleKernelAlarmsLocked(); + updateNextAlarmClockLocked(); + } + } + void removeLocked(PendingIntent operation, IAlarmListener directReceiver) { if (operation == null && directReceiver == null) { if (localLOGV) { @@ -3082,7 +3351,7 @@ public class AlarmManagerService extends SystemService { } } - private boolean isExemptFromBatterySaver(Alarm alarm) { + private static boolean isExemptFromBatterySaver(Alarm alarm) { if (alarm.alarmClock != null) { return true; } @@ -3142,7 +3411,7 @@ public class AlarmManagerService extends SystemService { alarm.count = 1; triggerList.add(alarm); - if ((alarm.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { + if ((alarm.flags & FLAG_WAKE_FROM_IDLE) != 0) { EventLogTags.writeDeviceIdleWakeFromIdle(mPendingIdleUntil != null ? 1 : 0, alarm.statsTag); } @@ -3180,7 +3449,7 @@ public class AlarmManagerService extends SystemService { setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed, nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null, null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid, - alarm.packageName); + alarm.packageName, null); } if (alarm.wakeup) { @@ -3257,7 +3526,6 @@ public class AlarmManagerService extends SystemService { mLastAlarmDeliveryTime = nowELAPSED; for (int i = 0; i < triggerList.size(); i++) { Alarm alarm = triggerList.get(i); - final boolean allowWhileIdle = (alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0; if (alarm.wakeup) { Trace.traceBegin(Trace.TRACE_TAG_POWER, "Dispatch wakeup alarm to " + alarm.packageName); @@ -3273,7 +3541,7 @@ public class AlarmManagerService extends SystemService { mActivityManagerInternal.noteAlarmStart(alarm.operation, alarm.workSource, alarm.uid, alarm.statsTag); } - mDeliveryTracker.deliverLocked(alarm, nowELAPSED, allowWhileIdle); + mDeliveryTracker.deliverLocked(alarm, nowELAPSED); } catch (RuntimeException e) { Slog.w(TAG, "Failure sending alarm.", e); } @@ -3282,9 +3550,10 @@ public class AlarmManagerService extends SystemService { } } - private boolean isExemptFromAppStandby(Alarm a) { + @VisibleForTesting + static boolean isExemptFromAppStandby(Alarm a) { return a.alarmClock != null || UserHandle.isCore(a.creatorUid) - || (a.flags & FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED) != 0; + || (a.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_ALLOW_WHILE_IDLE)) != 0; } @VisibleForTesting @@ -3368,6 +3637,11 @@ public class AlarmManagerService extends SystemService { MATCH_SYSTEM_ONLY, USER_SYSTEM); } + IAppOpsService getAppOpsService() { + return IAppOpsService.Stub.asInterface( + ServiceManager.getService(Context.APP_OPS_SERVICE)); + } + ClockReceiver getClockReceiver(AlarmManagerService service) { return service.new ClockReceiver(); } @@ -3566,6 +3840,7 @@ public class AlarmManagerService extends SystemService { public static final int APP_STANDBY_BUCKET_CHANGED = 5; public static final int CHARGING_STATUS_CHANGED = 6; public static final int REMOVE_FOR_CANCELED = 7; + public static final int REMOVE_EXACT_ALARMS = 8; AlarmHandler() { super(Looper.myLooper()); @@ -3645,6 +3920,14 @@ public class AlarmManagerService extends SystemService { } break; + case REMOVE_EXACT_ALARMS: + final int uid = msg.arg1; + final String packageName = (String) msg.obj; + synchronized (mLock) { + removeExactAlarmsOnPermissionRevokedLocked(uid, packageName); + } + break; + default: // nope, just ignore it break; @@ -3720,7 +4003,7 @@ public class AlarmManagerService extends SystemService { setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0, 0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null, - Process.myUid(), "android"); + Process.myUid(), "android", null); // Finally, remember when we set the tick alarm synchronized (mLock) { @@ -3740,7 +4023,7 @@ public class AlarmManagerService extends SystemService { final WorkSource workSource = null; // Let system take blame for date change events. setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, null, null, AlarmManager.FLAG_STANDALONE, workSource, null, - Process.myUid(), "android"); + Process.myUid(), "android", null); } } @@ -4112,7 +4395,7 @@ public class AlarmManagerService extends SystemService { * Deliver an alarm and set up the post-delivery handling appropriately */ @GuardedBy("mLock") - public void deliverLocked(Alarm alarm, long nowELAPSED, boolean allowWhileIdle) { + public void deliverLocked(Alarm alarm, long nowELAPSED) { final long workSourceToken = ThreadLocalWorkSource.setUid( getAlarmAttributionUid(alarm)); try { @@ -4122,10 +4405,8 @@ public class AlarmManagerService extends SystemService { try { alarm.operation.send(getContext(), 0, - mBackgroundIntent.putExtra( - Intent.EXTRA_ALARM_COUNT, alarm.count), - mDeliveryTracker, mHandler, null, - allowWhileIdle ? mIdleOptions : null); + mBackgroundIntent.putExtra(Intent.EXTRA_ALARM_COUNT, alarm.count), + mDeliveryTracker, mHandler, null, alarm.mIdleOptions); } catch (PendingIntent.CanceledException e) { if (alarm.repeatInterval > 0) { // This IntentSender is no longer valid, but this @@ -4194,7 +4475,7 @@ public class AlarmManagerService extends SystemService { if (inflight.isBroadcast()) { notifyBroadcastAlarmPendingLocked(alarm.uid); } - if (allowWhileIdle) { + if (isAllowedWhileIdleRestricted(alarm)) { final boolean doze = (mPendingIdleUntil != null); final boolean batterySaver = (mAppStateTracker != null && mAppStateTracker.isForceAllAppsStandbyEnabled()); @@ -4204,8 +4485,7 @@ public class AlarmManagerService extends SystemService { mAllowWhileIdleHistory.recordAlarmForPackage(alarm.sourcePackage, UserHandle.getUserId(alarm.creatorUid), nowELAPSED); mAlarmStore.updateAlarmDeliveries(a -> { - if (a.creatorUid != alarm.creatorUid - || (a.flags & FLAG_ALLOW_WHILE_IDLE) == 0) { + if (a.creatorUid != alarm.creatorUid || !isAllowedWhileIdleRestricted(a)) { return false; } return (doze && adjustDeliveryTimeBasedOnDeviceIdle(a)) diff --git a/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java b/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java index b7e8cf6e3fc8..7f191d4a306a 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java +++ b/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java @@ -26,6 +26,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; import android.util.proto.ProtoOutputStream; + import com.android.server.LocalServices; import com.android.server.uri.UriGrantsManagerInternal; @@ -144,13 +145,13 @@ public final class GrantedUriPermissions { } // Dumpsys infrastructure - public void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("mGrantFlags=0x"); pw.print(Integer.toHexString(mGrantFlags)); + public void dump(PrintWriter pw) { + pw.print("mGrantFlags=0x"); pw.print(Integer.toHexString(mGrantFlags)); pw.print(" mSourceUserId="); pw.println(mSourceUserId); - pw.print(prefix); pw.print("mTag="); pw.println(mTag); - pw.print(prefix); pw.print("mPermissionOwner="); pw.println(mPermissionOwner); + pw.print("mTag="); pw.println(mTag); + pw.print("mPermissionOwner="); pw.println(mPermissionOwner); for (int i = 0; i < mUris.size(); i++) { - pw.print(prefix); pw.print("#"); pw.print(i); pw.print(": "); + pw.print("#"); pw.print(i); pw.print(": "); pw.println(mUris.get(i)); } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java index 164781a250b7..e8e2c27f1554 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -20,6 +20,7 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.UserSwitchObserver; @@ -80,6 +81,8 @@ class JobConcurrencyManager { static final int WORK_TYPE_BGUSER = 1 << 3; @VisibleForTesting static final int NUM_WORK_TYPES = 4; + private static final int ALL_WORK_TYPES = + WORK_TYPE_TOP | WORK_TYPE_EJ | WORK_TYPE_BG | WORK_TYPE_BGUSER; @IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = { WORK_TYPE_NONE, @@ -92,6 +95,23 @@ class JobConcurrencyManager { public @interface WorkType { } + private static String workTypeToString(@WorkType int workType) { + switch (workType) { + case WORK_TYPE_NONE: + return "NONE"; + case WORK_TYPE_TOP: + return "TOP"; + case WORK_TYPE_EJ: + return "EJ"; + case WORK_TYPE_BG: + return "BG"; + case WORK_TYPE_BGUSER: + return "BGUSER"; + default: + return "WORK(" + workType + ")"; + } + } + private final Object mLock; private final JobSchedulerService mService; private final Context mContext; @@ -182,10 +202,16 @@ class JobConcurrencyManager { int[] mRecycledWorkTypeForContext = new int[MAX_JOB_CONTEXTS_COUNT]; + String[] mRecycledPreemptReasonForContext = new String[MAX_JOB_CONTEXTS_COUNT]; + + String[] mRecycledShouldStopJobReason = new String[MAX_JOB_CONTEXTS_COUNT]; + private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>(); private final WorkCountTracker mWorkCountTracker = new WorkCountTracker(); + private WorkTypeConfig mWorkTypeConfig = CONFIG_LIMITS_SCREEN_OFF.normal; + /** Wait for this long after screen off before adjusting the job concurrency. */ private long mScreenOffAdjustmentDelayMs = DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS; @@ -353,23 +379,22 @@ class JobConcurrencyManager { final WorkConfigLimitsPerMemoryTrimLevel workConfigs = mEffectiveInteractiveState ? CONFIG_LIMITS_SCREEN_ON : CONFIG_LIMITS_SCREEN_OFF; - WorkTypeConfig workTypeConfig; switch (mLastMemoryTrimLevel) { case ProcessStats.ADJ_MEM_FACTOR_MODERATE: - workTypeConfig = workConfigs.moderate; + mWorkTypeConfig = workConfigs.moderate; break; case ProcessStats.ADJ_MEM_FACTOR_LOW: - workTypeConfig = workConfigs.low; + mWorkTypeConfig = workConfigs.low; break; case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: - workTypeConfig = workConfigs.critical; + mWorkTypeConfig = workConfigs.critical; break; default: - workTypeConfig = workConfigs.normal; + mWorkTypeConfig = workConfigs.normal; break; } - mWorkCountTracker.setConfig(workTypeConfig); + mWorkCountTracker.setConfig(mWorkTypeConfig); } /** @@ -401,13 +426,20 @@ class JobConcurrencyManager { boolean[] slotChanged = mRecycledSlotChanged; int[] preferredUidForContext = mRecycledPreferredUidForContext; int[] workTypeForContext = mRecycledWorkTypeForContext; + String[] preemptReasonForContext = mRecycledPreemptReasonForContext; + String[] shouldStopJobReason = mRecycledShouldStopJobReason; updateCounterConfigLocked(); // Reset everything since we'll re-evaluate the current state. mWorkCountTracker.resetCounts(); + // Update the priorities of jobs that aren't running, and also count the pending work types. + // Do this before the following loop to hopefully reduce the cost of + // shouldStopRunningJobLocked(). + updateNonRunningPriorities(pendingJobs, true); + for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { - final JobServiceContext js = mService.mActiveServices.get(i); + final JobServiceContext js = activeServices.get(i); final JobStatus status = js.getRunningJobLocked(); if ((contextIdToJobMap[i] = status) != null) { @@ -417,14 +449,13 @@ class JobConcurrencyManager { slotChanged[i] = false; preferredUidForContext[i] = js.getPreferredUid(); + preemptReasonForContext[i] = null; + shouldStopJobReason[i] = shouldStopRunningJobLocked(js); } if (DEBUG) { Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial")); } - // Next, update the job priorities, and also count the pending FG / BG jobs. - updateNonRunningPriorities(pendingJobs, true); - mWorkCountTracker.onCountDone(); for (int i = 0; i < pendingJobs.size(); i++) { @@ -434,8 +465,6 @@ class JobConcurrencyManager { continue; } - // TODO(171305774): make sure HPJs aren't pre-empted and add dedicated contexts for them - // Find an available slot for nextPending. The context should be available OR // it should have lowest priority among all running jobs // (sharing the same Uid as nextPending) @@ -444,6 +473,9 @@ class JobConcurrencyManager { int allWorkTypes = getJobWorkTypes(nextPending); int workType = mWorkCountTracker.canJobStart(allWorkTypes); boolean startingJob = false; + String preemptReason = null; + // TODO(141645789): rewrite this to look at empty contexts first so we don't + // unnecessarily preempt for (int j = 0; j < MAX_JOB_CONTEXTS_COUNT; j++) { JobStatus job = contextIdToJobMap[j]; int preferredUid = preferredUidForContext[j]; @@ -464,6 +496,15 @@ class JobConcurrencyManager { continue; } if (job.getUid() != nextPending.getUid()) { + // Maybe stop the job if it has had its day in the sun. + final String reason = shouldStopJobReason[j]; + if (reason != null && mWorkCountTracker.canJobStart(allWorkTypes, + activeServices.get(j).getRunningJobWorkType()) != WORK_TYPE_NONE) { + // Right now, the way the code is set up, we don't need to explicitly + // assign the new job to this context since we'll reassign when the + // preempted job finally stops. + preemptReason = reason; + } continue; } @@ -477,6 +518,7 @@ class JobConcurrencyManager { // the lowest-priority running job minPriorityForPreemption = jobPriority; selectedContextId = j; + preemptReason = "higher priority job found"; // In this case, we're just going to preempt a low priority job, we're not // actually starting a job, so don't set startingJob. } @@ -484,6 +526,7 @@ class JobConcurrencyManager { if (selectedContextId != -1) { contextIdToJobMap[selectedContextId] = nextPending; slotChanged[selectedContextId] = true; + preemptReasonForContext[selectedContextId] = preemptReason; } if (startingJob) { // Increase the counters when we're going to start a job. @@ -509,7 +552,7 @@ class JobConcurrencyManager { + activeServices.get(i).getRunningJobLocked()); } // preferredUid will be set to uid of currently running job. - activeServices.get(i).preemptExecutingJobLocked(); + activeServices.get(i).preemptExecutingJobLocked(preemptReasonForContext[i]); preservePreferredUid = true; } else { final JobStatus pendingJob = contextIdToJobMap[i]; @@ -692,6 +735,93 @@ class JobConcurrencyManager { noteConcurrency(); } + /** + * Returns {@code null} if the job can continue running and a non-null String if the job should + * be stopped. The non-null String details the reason for stopping the job. A job will generally + * be stopped if there similar job types waiting to be run and stopping this job would allow + * another job to run, or if system state suggests the job should stop. + */ + @Nullable + String shouldStopRunningJobLocked(@NonNull JobServiceContext context) { + final JobStatus js = context.getRunningJobLocked(); + if (js == null) { + // This can happen when we try to assign newly found pending jobs to contexts. + return null; + } + + if (context.isWithinExecutionGuaranteeTime()) { + return null; + } + + // We're over the minimum guaranteed runtime. Stop the job if we're over config limits, + // there are pending jobs that could replace this one, or the device state is not conducive + // to long runs. + + if (mPowerManager.isPowerSaveMode()) { + return "battery saver"; + } + if (mPowerManager.isDeviceIdleMode()) { + return "deep doze"; + } + + // Update config in case memory usage has changed significantly. + updateCounterConfigLocked(); + + @WorkType final int workType = context.getRunningJobWorkType(); + + if (mRunningJobs.size() > mWorkTypeConfig.getMaxTotal() + || mWorkCountTracker.isOverTypeLimit(workType)) { + return "too many jobs running"; + } + + final List<JobStatus> pendingJobs = mService.mPendingJobs; + final int numPending = pendingJobs.size(); + if (numPending == 0) { + // All quiet. We can let this job run to completion. + return null; + } + + // Only expedited jobs can replace expedited jobs. + if (js.shouldTreatAsExpeditedJob()) { + // Keep fg/bg user distinction. + if (workType == WORK_TYPE_BGUSER) { + // For now, let any bg user job replace a bg user expedited job. + // TODO: limit to ej once we have dedicated bg user ej slots. + if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_BGUSER) > 0) { + return "blocking " + workTypeToString(workType) + " queue"; + } + } else { + if (mWorkCountTracker.getPendingJobCount(WORK_TYPE_EJ) > 0) { + return "blocking " + workTypeToString(workType) + " queue"; + } + } + } + + // Easy check. If there are pending jobs of the same work type, then we know that + // something will replace this. + if (mWorkCountTracker.getPendingJobCount(workType) > 0) { + return "blocking " + workTypeToString(workType) + " queue"; + } + + // Harder check. We need to see if a different work type can replace this job. + int remainingWorkTypes = ALL_WORK_TYPES; + for (int i = 0; i < numPending; ++i) { + final JobStatus pending = pendingJobs.get(i); + final int workTypes = getJobWorkTypes(pending); + if ((workTypes & remainingWorkTypes) > 0 + && mWorkCountTracker.canJobStart(workTypes, workType) != WORK_TYPE_NONE) { + return "blocking other pending jobs"; + } + + remainingWorkTypes = remainingWorkTypes & ~workTypes; + if (remainingWorkTypes == 0) { + break; + } + } + + return null; + } + @GuardedBy("mLock") private String printPendingQueueLocked() { StringBuilder s = new StringBuilder("Pending queue: "); @@ -1362,10 +1492,40 @@ class JobConcurrencyManager { return WORK_TYPE_NONE; } + int canJobStart(int workTypes, @WorkType int replacingWorkType) { + final boolean changedNums; + int oldNumRunning = mNumRunningJobs.get(replacingWorkType); + if (replacingWorkType != WORK_TYPE_NONE && oldNumRunning > 0) { + mNumRunningJobs.put(replacingWorkType, oldNumRunning - 1); + // Lazy implementation to avoid lots of processing. Best way would be to go + // through the whole process of adjusting reservations, but the processing cost + // is likely not worth it. + mNumUnspecializedRemaining++; + changedNums = true; + } else { + changedNums = false; + } + + final int ret = canJobStart(workTypes); + if (changedNums) { + mNumRunningJobs.put(replacingWorkType, oldNumRunning); + mNumUnspecializedRemaining--; + } + return ret; + } + + int getPendingJobCount(@WorkType final int workType) { + return mNumPendingJobs.get(workType, 0); + } + int getRunningJobCount(@WorkType final int workType) { return mNumRunningJobs.get(workType, 0); } + boolean isOverTypeLimit(@WorkType final int workType) { + return getRunningJobCount(workType) > mConfigAbsoluteMaxSlots.get(workType); + } + public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java index d05034797f3d..6ffac91d7098 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java @@ -25,6 +25,7 @@ import android.app.job.JobParameters; import android.os.UserHandle; import android.text.format.DateFormat; import android.util.ArrayMap; +import android.util.IndentingPrintWriter; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.TimeUtils; @@ -33,8 +34,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.util.RingBufferIndices; import com.android.server.job.controllers.JobStatus; -import java.io.PrintWriter; - public final class JobPackageTracker { // We batch every 30 minutes. static final long BATCHING_TIME = 30*60*1000; @@ -294,53 +293,69 @@ public final class JobPackageTracker { } } - void printDuration(PrintWriter pw, long period, long duration, int count, String suffix) { + /** Return {@code true} if text was printed. */ + boolean printDuration(IndentingPrintWriter pw, long period, long duration, int count, + String suffix) { float fraction = duration / (float) period; int percent = (int) ((fraction * 100) + .5f); if (percent > 0) { - pw.print(" "); pw.print(percent); pw.print("% "); pw.print(count); pw.print("x "); pw.print(suffix); + return true; } else if (count > 0) { - pw.print(" "); pw.print(count); pw.print("x "); pw.print(suffix); + return true; } + + return false; } - void dump(PrintWriter pw, String header, String prefix, long now, long nowElapsed, - int filterUid) { + void dump(IndentingPrintWriter pw, String header, long now, long nowElapsed, + int filterAppId) { final long period = getTotalTime(now); - pw.print(prefix); pw.print(header); pw.print(" at "); + pw.print(header); pw.print(" at "); pw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", mStartClockTime).toString()); pw.print(" ("); TimeUtils.formatDuration(mStartElapsedTime, nowElapsed, pw); pw.print(") over "); TimeUtils.formatDuration(period, pw); pw.println(":"); + pw.increaseIndent(); + pw.print("Max concurrency: "); + pw.print(mMaxTotalActive); pw.print(" total, "); + pw.print(mMaxFgActive); pw.println(" foreground"); + + pw.println(); final int NE = mEntries.size(); for (int i = 0; i < NE; i++) { int uid = mEntries.keyAt(i); - if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) { + if (filterAppId != -1 && filterAppId != UserHandle.getAppId(uid)) { continue; } ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i); final int NP = uidMap.size(); for (int j = 0; j < NP; j++) { PackageEntry pe = uidMap.valueAt(j); - pw.print(prefix); pw.print(" "); UserHandle.formatUid(pw, uid); pw.print(" / "); pw.print(uidMap.keyAt(j)); pw.println(":"); - pw.print(prefix); pw.print(" "); - printDuration(pw, period, pe.getPendingTime(now), pe.pendingCount, "pending"); - printDuration(pw, period, pe.getActiveTime(now), pe.activeCount, "active"); - printDuration(pw, period, pe.getActiveTopTime(now), pe.activeTopCount, - "active-top"); + + pw.increaseIndent(); + if (printDuration(pw, period, + pe.getPendingTime(now), pe.pendingCount, "pending")) { + pw.print(" "); + } + if (printDuration(pw, period, + pe.getActiveTime(now), pe.activeCount, "active")) { + pw.print(" "); + } + printDuration(pw, period, + pe.getActiveTopTime(now), pe.activeTopCount, "active-top"); if (pe.pendingNesting > 0 || pe.hadPending) { pw.print(" (pending)"); } @@ -352,7 +367,6 @@ public final class JobPackageTracker { } pw.println(); if (pe.stopReasons.size() > 0) { - pw.print(prefix); pw.print(" "); for (int k = 0; k < pe.stopReasons.size(); k++) { if (k > 0) { pw.print(", "); @@ -364,11 +378,10 @@ public final class JobPackageTracker { } pw.println(); } + pw.decreaseIndent(); } } - pw.print(prefix); pw.print(" Max concurrency: "); - pw.print(mMaxTotalActive); pw.print(" total, "); - pw.print(mMaxFgActive); pw.println(" foreground"); + pw.decreaseIndent(); } private void printPackageEntryState(ProtoOutputStream proto, long fieldId, @@ -520,7 +533,7 @@ public final class JobPackageTracker { return time / (float)period; } - public void dump(PrintWriter pw, String prefix, int filterUid) { + void dump(IndentingPrintWriter pw, int filterAppId) { final long now = sUptimeMillisClock.millis(); final long nowElapsed = sElapsedRealtimeClock.millis(); final DataSet total; @@ -533,11 +546,11 @@ public final class JobPackageTracker { mCurDataSet.addTo(total, now); for (int i = 1; i < mLastDataSets.length; i++) { if (mLastDataSets[i] != null) { - mLastDataSets[i].dump(pw, "Historical stats", prefix, now, nowElapsed, filterUid); + mLastDataSets[i].dump(pw, "Historical stats", now, nowElapsed, filterAppId); pw.println(); } } - total.dump(pw, "Current stats", prefix, now, nowElapsed, filterUid); + total.dump(pw, "Current stats", now, nowElapsed, filterAppId); } public void dump(ProtoOutputStream proto, long fieldId, int filterUid) { @@ -566,17 +579,19 @@ public final class JobPackageTracker { proto.end(token); } - public boolean dumpHistory(PrintWriter pw, String prefix, int filterUid) { + boolean dumpHistory(IndentingPrintWriter pw, int filterAppId) { final int size = mEventIndices.size(); if (size <= 0) { return false; } - pw.println(" Job history:"); + pw.increaseIndent(); + pw.println("Job history:"); + pw.decreaseIndent(); final long now = sElapsedRealtimeClock.millis(); for (int i=0; i<size; i++) { final int index = mEventIndices.indexOf(i); final int uid = mEventUids[index]; - if (filterUid != -1 && filterUid != UserHandle.getAppId(uid)) { + if (filterAppId != -1 && filterAppId != UserHandle.getAppId(uid)) { continue; } final int cmd = mEventCmds[index] & EVENT_CMD_MASK; @@ -591,7 +606,6 @@ public final class JobPackageTracker { case EVENT_STOP_PERIODIC_JOB: label = " STOP-P"; break; default: label = " ??"; break; } - pw.print(prefix); TimeUtils.formatDuration(mEventTimes[index]-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN); pw.print(" "); pw.print(label); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index fdbc0864a59d..8bb03e911528 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -151,6 +151,8 @@ public class JobSchedulerService extends com.android.server.SystemService private static final boolean ENFORCE_MAX_JOBS = true; /** The maximum number of jobs that we allow an unprivileged app to schedule */ private static final int MAX_JOBS_PER_APP = 100; + /** The number of the most recently completed jobs to keep track of for debugging purposes. */ + private static final int NUM_COMPLETED_JOB_HISTORY = 20; @VisibleForTesting public static Clock sSystemClock = Clock.systemUTC(); @@ -297,6 +299,10 @@ public class JobSchedulerService extends com.android.server.SystemService */ boolean mReportedActive; + private int mLastCompletedJobIndex = 0; + private final JobStatus[] mLastCompletedJobs = new JobStatus[NUM_COMPLETED_JOB_HISTORY]; + private final long[] mLastCompletedJobTimeElapsed = new long[NUM_COMPLETED_JOB_HISTORY]; + /** * A mapping of which uids are currently in the foreground to their effective priority. */ @@ -339,6 +345,7 @@ public class JobSchedulerService extends com.android.server.SystemService public void onPropertiesChanged(DeviceConfig.Properties properties) { boolean apiQuotaScheduleUpdated = false; boolean concurrencyUpdated = false; + boolean runtimeUpdated = false; for (int controller = 0; controller < mControllers.size(); controller++) { final StateController sc = mControllers.get(controller); sc.prepareForUpdatedConstantsLocked(); @@ -377,6 +384,14 @@ public class JobSchedulerService extends com.android.server.SystemService case Constants.KEY_CONN_PREFETCH_RELAX_FRAC: mConstants.updateConnectivityConstantsLocked(); break; + case Constants.KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS: + case Constants.KEY_RUNTIME_MIN_GUARANTEE_MS: + case Constants.KEY_RUNTIME_MIN_EJ_GUARANTEE_MS: + if (!runtimeUpdated) { + mConstants.updateRuntimeConstantsLocked(); + runtimeUpdated = true; + } + break; default: if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY) && !concurrencyUpdated) { @@ -432,6 +447,11 @@ public class JobSchedulerService extends com.android.server.SystemService private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = "aq_schedule_return_failure"; + private static final String KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = + "runtime_free_quota_max_limit_ms"; + private static final String KEY_RUNTIME_MIN_GUARANTEE_MS = "runtime_min_guarantee_ms"; + private static final String KEY_RUNTIME_MIN_EJ_GUARANTEE_MS = "runtime_min_ej_guarantee_ms"; + private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5; private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS; private static final float DEFAULT_HEAVY_USE_FACTOR = .9f; @@ -445,6 +465,12 @@ public class JobSchedulerService extends com.android.server.SystemService private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS; private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true; private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; + @VisibleForTesting + public static final long DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = 30 * MINUTE_IN_MILLIS; + @VisibleForTesting + public static final long DEFAULT_RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS; + @VisibleForTesting + public static final long DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS = 3 * MINUTE_IN_MILLIS; /** * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early. @@ -509,6 +535,19 @@ public class JobSchedulerService extends com.android.server.SystemService public boolean API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT; + /** The maximum amount of time we will let a job run for when quota is "free". */ + public long RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; + + /** + * The minimum amount of time we try to guarantee regular jobs will run for. + */ + public long RUNTIME_MIN_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_GUARANTEE_MS; + + /** + * The minimum amount of time we try to guarantee EJs will run for. + */ + public long RUNTIME_MIN_EJ_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS; + private void updateBatchingConstantsLocked() { MIN_READY_NON_ACTIVE_JOBS_COUNT = DeviceConfig.getInt( DeviceConfig.NAMESPACE_JOB_SCHEDULER, @@ -568,6 +607,25 @@ public class JobSchedulerService extends com.android.server.SystemService DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT); } + private void updateRuntimeConstantsLocked() { + DeviceConfig.Properties properties = DeviceConfig.getProperties( + DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + KEY_RUNTIME_MIN_GUARANTEE_MS, KEY_RUNTIME_MIN_EJ_GUARANTEE_MS); + + // Make sure min runtime for regular jobs is at least 10 minutes. + RUNTIME_MIN_GUARANTEE_MS = Math.max(10 * MINUTE_IN_MILLIS, + properties.getLong( + KEY_RUNTIME_MIN_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_GUARANTEE_MS)); + // Make sure min runtime for expedited jobs is at least one minute. + RUNTIME_MIN_EJ_GUARANTEE_MS = Math.max(MINUTE_IN_MILLIS, + properties.getLong( + KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS)); + RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = Math.max(RUNTIME_MIN_GUARANTEE_MS, + properties.getLong(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)); + } + void dump(IndentingPrintWriter pw) { pw.println("Settings:"); pw.increaseIndent(); @@ -591,6 +649,11 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT, API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT).println(); + pw.print(KEY_RUNTIME_MIN_GUARANTEE_MS, RUNTIME_MIN_GUARANTEE_MS).println(); + pw.print(KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, RUNTIME_MIN_EJ_GUARANTEE_MS).println(); + pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) + .println(); + pw.decreaseIndent(); } @@ -824,8 +887,10 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override - public void onUserStarting(@NonNull TargetUser user) { + public void onUserUnlocked(@NonNull TargetUser user) { synchronized (mLock) { + // Note that the user has started after its unlocked instead of when the user + // actually starts because the storage won't be decrypted until unlock. mStartedUsers = ArrayUtils.appendInt(mStartedUsers, user.getUserIdentifier()); } // Let's kick any outstanding jobs for this user. @@ -833,12 +898,6 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override - public void onUserUnlocking(@NonNull TargetUser user) { - // Let's kick any outstanding jobs for this user. - mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); - } - - @Override public void onUserStopping(@NonNull TargetUser user) { synchronized (mLock) { mStartedUsers = ArrayUtils.removeInt(mStartedUsers, user.getUserIdentifier()); @@ -1602,7 +1661,7 @@ public class JobSchedulerService extends com.android.server.SystemService * time of the job to be the time of completion (i.e. the time at which this function is * called). * <p>This could be inaccurate b/c the job can run for as long as - * {@link com.android.server.job.JobServiceContext#DEFAULT_EXECUTING_TIMESLICE_MILLIS}, but + * {@link Constants#DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS}, but * will lead to underscheduling at least, rather than if we had taken the last execution time * to be the start of the execution. * @@ -1695,6 +1754,10 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); } + mLastCompletedJobs[mLastCompletedJobIndex] = jobStatus; + mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis(); + mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY; + // Intentionally not checking expedited job quota here. An app can't find out if it's run // out of quota when it asks JS to reschedule an expedited job. Instead, the rescheduled // EJ will just be demoted to a regular job if the app has no EJ quota left. @@ -2213,11 +2276,25 @@ public class JobSchedulerService extends com.android.server.SystemService return isComponentUsable(job); } + /** Returns the minimum amount of time we should let this job run before timing out. */ + public long getMinJobExecutionGuaranteeMs(JobStatus job) { + synchronized (mLock) { + if (job.shouldTreatAsExpeditedJob()) { + // Don't guarantee RESTRICTED jobs more than 5 minutes. + return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX + ? mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS + : Math.min(mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 5 * MINUTE_IN_MILLIS); + } else { + return mConstants.RUNTIME_MIN_GUARANTEE_MS; + } + } + } + /** Returns the maximum amount of time this job could run for. */ public long getMaxJobExecutionTimeMs(JobStatus job) { synchronized (mLock) { - return Math.min(mQuotaController.getMaxJobExecutionTimeMsLocked(job), - JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS); + return Math.min(mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } } @@ -3021,14 +3098,14 @@ public class JobSchedulerService extends com.android.server.SystemService } void dumpInternal(final IndentingPrintWriter pw, int filterUid) { - final int filterUidFinal = UserHandle.getAppId(filterUid); + final int filterAppId = UserHandle.getAppId(filterUid); final long now = sSystemClock.millis(); final long nowElapsed = sElapsedRealtimeClock.millis(); final long nowUptime = sUptimeMillisClock.millis(); final Predicate<JobStatus> predicate = (js) -> { - return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal - || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal; + return filterAppId == -1 || UserHandle.getAppId(js.getUid()) == filterAppId + || UserHandle.getAppId(js.getSourceUid()) == filterAppId; }; synchronized (mLock) { mConstants.dump(pw); @@ -3041,7 +3118,6 @@ public class JobSchedulerService extends com.android.server.SystemService for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { mJobRestrictions.get(i).dumpConstants(pw); - pw.println(); } pw.println(); @@ -3052,21 +3128,25 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print("Registered "); pw.print(mJobs.size()); pw.println(" jobs:"); + pw.increaseIndent(); + boolean jobPrinted = false; if (mJobs.size() > 0) { final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs(); sortJobs(jobs); for (JobStatus job : jobs) { - pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": "); - pw.println(job.toShortStringExceptUniqueId()); - // Skip printing details if the caller requested a filter if (!predicate.test(job)) { continue; } + jobPrinted = true; + + pw.print("JOB #"); job.printUniqueId(pw); pw.print(": "); + pw.println(job.toShortStringExceptUniqueId()); - job.dump(pw, " ", true, nowElapsed); + pw.increaseIndent(); + job.dump(pw, true, nowElapsed); - pw.print(" Restricted due to:"); + pw.print("Restricted due to:"); final boolean isRestricted = checkIfRestricted(job) != null; if (isRestricted) { for (int i = mJobRestrictions.size() - 1; i >= 0; i--) { @@ -3081,7 +3161,7 @@ public class JobSchedulerService extends com.android.server.SystemService } pw.println("."); - pw.print(" Ready: "); + pw.print("Ready: "); pw.print(isReadyToBeExecutedLocked(job)); pw.print(" (job="); pw.print(job.isReady()); @@ -3098,10 +3178,15 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(" comp="); pw.print(isComponentUsable(job)); pw.println(")"); + + pw.decreaseIndent(); } - } else { - pw.println(" None."); } + if (!jobPrinted) { + pw.println("None."); + } + pw.decreaseIndent(); + for (int i=0; i<mControllers.size(); i++) { pw.println(); pw.println(mControllers.get(i).getClass().getSimpleName() + ":"); @@ -3109,66 +3194,105 @@ public class JobSchedulerService extends com.android.server.SystemService mControllers.get(i).dumpControllerStateLocked(pw, predicate); pw.decreaseIndent(); } - pw.println(); - pw.println("Uid priority overrides:"); + + boolean overridePrinted = false; for (int i=0; i< mUidPriorityOverride.size(); i++) { int uid = mUidPriorityOverride.keyAt(i); - if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) { - pw.print(" "); pw.print(UserHandle.formatUid(uid)); + if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { + if (!overridePrinted) { + overridePrinted = true; + pw.println(); + pw.println("Uid priority overrides:"); + pw.increaseIndent(); + } + pw.print(UserHandle.formatUid(uid)); pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i)); } } - if (mBackingUpUids.size() > 0) { - pw.println(); - pw.println("Backing up uids:"); - boolean first = true; - for (int i = 0; i < mBackingUpUids.size(); i++) { - int uid = mBackingUpUids.keyAt(i); - if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) { - if (first) { - pw.print(" "); - first = false; - } else { - pw.print(", "); - } - pw.print(UserHandle.formatUid(uid)); + if (overridePrinted) { + pw.decreaseIndent(); + } + + boolean backingPrinted = false; + for (int i = 0; i < mBackingUpUids.size(); i++) { + int uid = mBackingUpUids.keyAt(i); + if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { + if (!backingPrinted) { + pw.println(); + pw.println("Backing up uids:"); + pw.increaseIndent(); + backingPrinted = true; + } else { + pw.print(", "); } + pw.print(UserHandle.formatUid(uid)); } + } + if (backingPrinted) { + pw.decreaseIndent(); pw.println(); } + pw.println(); - mJobPackageTracker.dump(pw, "", filterUidFinal); + mJobPackageTracker.dump(pw, filterAppId); pw.println(); - if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) { + if (mJobPackageTracker.dumpHistory(pw, filterAppId)) { pw.println(); } + + boolean pendingPrinted = false; pw.println("Pending queue:"); + pw.increaseIndent(); for (int i=0; i<mPendingJobs.size(); i++) { JobStatus job = mPendingJobs.get(i); - pw.print(" Pending #"); pw.print(i); pw.print(": "); + if (!predicate.test(job)) { + continue; + } + if (!pendingPrinted) { + pendingPrinted = true; + } + + pw.print("Pending #"); pw.print(i); pw.print(": "); pw.println(job.toShortString()); - job.dump(pw, " ", false, nowElapsed); + + pw.increaseIndent(); + job.dump(pw, false, nowElapsed); int priority = evaluateJobPriorityLocked(job); - pw.print(" Evaluated priority: "); + pw.print("Evaluated priority: "); pw.println(JobInfo.getPriorityString(priority)); - pw.print(" Tag: "); pw.println(job.getTag()); - pw.print(" Enq: "); + pw.print("Tag: "); pw.println(job.getTag()); + pw.print("Enq: "); TimeUtils.formatDuration(job.madePending - nowUptime, pw); + pw.decreaseIndent(); pw.println(); } + if (!pendingPrinted) { + pw.println("None"); + } + pw.decreaseIndent(); + pw.println(); pw.println("Active jobs:"); pw.increaseIndent(); for (int i=0; i<mActiveServices.size(); i++) { JobServiceContext jsc = mActiveServices.get(i); + final JobStatus job = jsc.getRunningJobLocked(); + + if (job != null && !predicate.test(job)) { + continue; + } + pw.print("Slot #"); pw.print(i); pw.print(": "); jsc.dumpLocked(pw, nowElapsed); - final JobStatus job = jsc.getRunningJobLocked(); if (job != null) { pw.increaseIndent(); - job.dump(pw, " ", false, nowElapsed); + + pw.increaseIndent(); + job.dump(pw, false, nowElapsed); + pw.decreaseIndent(); + pw.print("Evaluated priority: "); pw.println(JobInfo.getPriorityString(job.lastEvaluatedPriority)); @@ -3176,11 +3300,42 @@ public class JobSchedulerService extends com.android.server.SystemService TimeUtils.formatDuration(job.madeActive - nowUptime, pw); pw.print(", pending for "); TimeUtils.formatDuration(job.madeActive - job.madePending, pw); + pw.decreaseIndent(); + pw.println(); + } + } + pw.decreaseIndent(); + + pw.println(); + boolean recentPrinted = false; + pw.println("Recently completed jobs:"); + pw.increaseIndent(); + for (int r = 1; r <= NUM_COMPLETED_JOB_HISTORY; ++r) { + // Print most recent first + final int idx = (mLastCompletedJobIndex + NUM_COMPLETED_JOB_HISTORY - r) + % NUM_COMPLETED_JOB_HISTORY; + final JobStatus job = mLastCompletedJobs[idx]; + if (job != null) { + if (!predicate.test(job)) { + continue; + } + recentPrinted = true; + TimeUtils.formatDuration(mLastCompletedJobTimeElapsed[idx], nowElapsed, pw); pw.println(); + // Double indent for readability + pw.increaseIndent(); + pw.increaseIndent(); + job.dump(pw, true, nowElapsed); + pw.decreaseIndent(); pw.decreaseIndent(); } } + if (!recentPrinted) { + pw.println("None"); + } pw.decreaseIndent(); + pw.println(); + if (filterUid == -1) { pw.println(); pw.print("mReadyToRock="); pw.println(mReadyToRock); @@ -3199,13 +3354,13 @@ public class JobSchedulerService extends com.android.server.SystemService void dumpInternalProto(final FileDescriptor fd, int filterUid) { ProtoOutputStream proto = new ProtoOutputStream(fd); - final int filterUidFinal = UserHandle.getAppId(filterUid); + final int filterAppId = UserHandle.getAppId(filterUid); final long now = sSystemClock.millis(); final long nowElapsed = sElapsedRealtimeClock.millis(); final long nowUptime = sUptimeMillisClock.millis(); final Predicate<JobStatus> predicate = (js) -> { - return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal - || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal; + return filterAppId == -1 || UserHandle.getAppId(js.getUid()) == filterAppId + || UserHandle.getAppId(js.getSourceUid()) == filterAppId; }; synchronized (mLock) { @@ -3278,7 +3433,7 @@ public class JobSchedulerService extends com.android.server.SystemService } for (int i=0; i< mUidPriorityOverride.size(); i++) { int uid = mUidPriorityOverride.keyAt(i); - if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) { + if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES); proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid); proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE, @@ -3288,15 +3443,15 @@ public class JobSchedulerService extends com.android.server.SystemService } for (int i = 0; i < mBackingUpUids.size(); i++) { int uid = mBackingUpUids.keyAt(i); - if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) { + if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid); } } mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER, - filterUidFinal); + filterAppId); mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY, - filterUidFinal); + filterAppId); for (JobStatus job : mPendingJobs) { final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 0aca2461b41c..2a23d60d8af6 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -17,9 +17,9 @@ package com.android.server.job; import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; -import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.job.IJobCallback; import android.app.job.IJobService; @@ -76,14 +76,6 @@ public final class JobServiceContext implements ServiceConnection { private static final boolean DEBUG_STANDBY = JobSchedulerService.DEBUG_STANDBY; private static final String TAG = "JobServiceContext"; - /** Amount of time a job is allowed to execute for before being considered timed-out. */ - public static final long DEFAULT_EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins. - /** - * Amount of time a RESTRICTED expedited job is allowed to execute for before being considered - * timed-out. - */ - public static final long DEFAULT_RESTRICTED_EXPEDITED_JOB_EXECUTING_TIMESLICE_MILLIS = - DEFAULT_EXECUTING_TIMESLICE_MILLIS / 2; /** Amount of time the JobScheduler waits for the initial service launch+bind. */ private static final long OP_BIND_TIMEOUT_MILLIS = 18 * 1000; /** Amount of time the JobScheduler will wait for a response from an app for a message. */ @@ -110,6 +102,7 @@ public final class JobServiceContext implements ServiceConnection { /** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */ private final JobCompletedListener mCompletedListener; private final JobConcurrencyManager mJobConcurrencyManager; + private final JobSchedulerService mService; /** Used for service binding, etc. */ private final Context mContext; private final Object mLock; @@ -149,6 +142,13 @@ public final class JobServiceContext implements ServiceConnection { private long mExecutionStartTimeElapsed; /** Track when job will timeout. */ private long mTimeoutElapsed; + /** + * The minimum amount of time the context will allow the job to run before checking whether to + * stop it or not. + */ + private long mMinExecutionGuaranteeMillis; + /** The absolute maximum amount of time the job can run */ + private long mMaxExecutionTimeMillis; // Debugging: reason this job was last stopped. public String mStoppedReason; @@ -190,6 +190,7 @@ public final class JobServiceContext implements ServiceConnection { IBatteryStats batteryStats, JobPackageTracker tracker, Looper looper) { mContext = service.getContext(); mLock = service.getLock(); + mService = service; mBatteryStats = batteryStats; mJobPackageTracker = tracker; mCallbackHandler = new JobServiceHandler(looper); @@ -239,6 +240,9 @@ public final class JobServiceContext implements ServiceConnection { isDeadlineExpired, job.shouldTreatAsExpeditedJob(), triggeredUris, triggeredAuthorities, job.network); mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis(); + mMinExecutionGuaranteeMillis = mService.getMinJobExecutionGuaranteeMs(job); + mMaxExecutionTimeMillis = + Math.max(mService.getMaxJobExecutionTimeMs(job), mMinExecutionGuaranteeMillis); final long whenDeferred = job.getWhenStandbyDeferred(); if (whenDeferred > 0) { @@ -267,7 +271,9 @@ public final class JobServiceContext implements ServiceConnection { if (job.shouldTreatAsExpeditedJob()) { // TODO(171305774): The job should run on the little cores. We'll probably need // another binding flag for that. - bindFlags = Context.BIND_AUTO_CREATE; + bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND + | Context.BIND_ALMOST_PERCEPTIBLE + | Context.BIND_ALLOW_NETWORK_ACCESS; } else { bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_PERCEPTIBLE; @@ -352,8 +358,8 @@ public final class JobServiceContext implements ServiceConnection { } @GuardedBy("mLock") - void preemptExecutingJobLocked() { - doCancelLocked(JobParameters.REASON_PREEMPT, "cancelled due to preemption"); + void preemptExecutingJobLocked(@NonNull String reason) { + doCancelLocked(JobParameters.REASON_PREEMPT, reason); } int getPreferredUid() { @@ -372,6 +378,11 @@ public final class JobServiceContext implements ServiceConnection { return mTimeoutElapsed; } + boolean isWithinExecutionGuaranteeTime() { + return mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis + < sElapsedRealtimeClock.millis(); + } + @GuardedBy("mLock") boolean timeoutIfExecutingLocked(String pkgName, int userId, boolean matchJobId, int jobId, String reason) { @@ -607,7 +618,7 @@ public final class JobServiceContext implements ServiceConnection { } @GuardedBy("mLock") - void doCancelLocked(int arg1, String debugReason) { + private void doCancelLocked(int stopReasonCode, String debugReason) { if (mVerb == VERB_FINISHED) { if (DEBUG) { Slog.d(TAG, @@ -615,8 +626,8 @@ public final class JobServiceContext implements ServiceConnection { } return; } - mParams.setStopReason(arg1, debugReason); - if (arg1 == JobParameters.REASON_PREEMPT) { + mParams.setStopReason(stopReasonCode, debugReason); + if (stopReasonCode == JobParameters.REASON_PREEMPT) { mPreferredUid = mRunningJob != null ? mRunningJob.getUid() : NO_PREFERRED_UID; } @@ -767,11 +778,30 @@ public final class JobServiceContext implements ServiceConnection { closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping"); break; case VERB_EXECUTING: - // Not an error - client ran out of time. - Slog.i(TAG, "Client timed out while executing (no jobFinished received), " + - "sending onStop: " + getRunningJobNameLocked()); - mParams.setStopReason(JobParameters.REASON_TIMEOUT, "client timed out"); - sendStopMessageLocked("timeout while executing"); + final long latestStopTimeElapsed = + mExecutionStartTimeElapsed + mMaxExecutionTimeMillis; + final long nowElapsed = sElapsedRealtimeClock.millis(); + if (nowElapsed >= latestStopTimeElapsed) { + // Not an error - client ran out of time. + Slog.i(TAG, "Client timed out while executing (no jobFinished received)." + + " Sending onStop: " + getRunningJobNameLocked()); + mParams.setStopReason(JobParameters.REASON_TIMEOUT, "client timed out"); + sendStopMessageLocked("timeout while executing"); + } else { + // We've given the app the minimum execution time. See if we should stop it or + // let it continue running + final String reason = mJobConcurrencyManager.shouldStopRunningJobLocked(this); + if (reason != null) { + Slog.i(TAG, "Stopping client after min execution time: " + + getRunningJobNameLocked() + " because " + reason); + mParams.setStopReason(JobParameters.REASON_TIMEOUT, reason); + sendStopMessageLocked(reason); + } else { + Slog.i(TAG, "Letting " + getRunningJobNameLocked() + + " continue to run past min execution time"); + scheduleOpTimeOutLocked(); + } + } break; default: Slog.e(TAG, "Handling timeout for an invalid job state: " @@ -878,10 +908,16 @@ public final class JobServiceContext implements ServiceConnection { final long timeoutMillis; switch (mVerb) { case VERB_EXECUTING: - timeoutMillis = mRunningJob.shouldTreatAsExpeditedJob() - && mRunningJob.getStandbyBucket() == RESTRICTED_INDEX - ? DEFAULT_RESTRICTED_EXPEDITED_JOB_EXECUTING_TIMESLICE_MILLIS - : DEFAULT_EXECUTING_TIMESLICE_MILLIS; + final long earliestStopTimeElapsed = + mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis; + final long latestStopTimeElapsed = + mExecutionStartTimeElapsed + mMaxExecutionTimeMillis; + final long nowElapsed = sElapsedRealtimeClock.millis(); + if (nowElapsed < earliestStopTimeElapsed) { + timeoutMillis = earliestStopTimeElapsed - nowElapsed; + } else { + timeoutMillis = latestStopTimeElapsed - nowElapsed; + } break; case VERB_BINDING: @@ -925,6 +961,13 @@ public final class JobServiceContext implements ServiceConnection { pw.print(", timeout at: "); TimeUtils.formatDuration(mTimeoutElapsed - nowElapsed, pw); pw.println(); + pw.print("Remaining execution limits: ["); + TimeUtils.formatDuration( + (mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis) - nowElapsed, pw); + pw.print(", "); + TimeUtils.formatDuration( + (mExecutionStartTimeElapsed + mMaxExecutionTimeMillis) - nowElapsed, pw); + pw.println("]"); pw.decreaseIndent(); } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java index 58396eb69d14..a230b23f03a4 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -17,6 +17,7 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.os.SystemClock; import android.os.UserHandle; @@ -59,6 +60,8 @@ public final class BackgroundJobsController extends StateController { private final AppStateTrackerImpl mAppStateTracker; + private final UpdateJobFunctor mUpdateJobFunctor = new UpdateJobFunctor(); + public BackgroundJobsController(JobSchedulerService service) { super(service); @@ -69,7 +72,7 @@ public final class BackgroundJobsController extends StateController { @Override public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { - updateSingleJobRestrictionLocked(jobStatus, UNKNOWN); + updateSingleJobRestrictionLocked(jobStatus, sElapsedRealtimeClock.millis(), UNKNOWN); } @Override @@ -79,7 +82,7 @@ public final class BackgroundJobsController extends StateController { @Override public void evaluateStateLocked(JobStatus jobStatus) { - updateSingleJobRestrictionLocked(jobStatus, UNKNOWN); + updateSingleJobRestrictionLocked(jobStatus, sElapsedRealtimeClock.millis(), UNKNOWN); } @Override @@ -163,33 +166,34 @@ public final class BackgroundJobsController extends StateController { } private void updateJobRestrictionsLocked(int filterUid, int newActiveState) { - final UpdateJobFunctor updateTrackedJobs = new UpdateJobFunctor(newActiveState); + mUpdateJobFunctor.prepare(newActiveState); final long start = DEBUG ? SystemClock.elapsedRealtimeNanos() : 0; final JobStore store = mService.getJobStore(); if (filterUid > 0) { - store.forEachJobForSourceUid(filterUid, updateTrackedJobs); + store.forEachJobForSourceUid(filterUid, mUpdateJobFunctor); } else { - store.forEachJob(updateTrackedJobs); + store.forEachJob(mUpdateJobFunctor); } final long time = DEBUG ? (SystemClock.elapsedRealtimeNanos() - start) : 0; if (DEBUG) { Slog.d(TAG, String.format( "Job status updated: %d/%d checked/total jobs, %d us", - updateTrackedJobs.mCheckedCount, - updateTrackedJobs.mTotalCount, + mUpdateJobFunctor.mCheckedCount, + mUpdateJobFunctor.mTotalCount, (time / 1000) - )); + )); } - if (updateTrackedJobs.mChanged) { + if (mUpdateJobFunctor.mChanged) { mStateChangedListener.onControllerStateChanged(); } } - boolean updateSingleJobRestrictionLocked(JobStatus jobStatus, int activeState) { + boolean updateSingleJobRestrictionLocked(JobStatus jobStatus, final long nowElapsed, + int activeState) { final int uid = jobStatus.getSourceUid(); final String packageName = jobStatus.getSourcePackageName(); @@ -205,26 +209,32 @@ public final class BackgroundJobsController extends StateController { if (isActive && jobStatus.getStandbyBucket() == NEVER_INDEX) { jobStatus.maybeLogBucketMismatch(); } - boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun); + boolean didChange = + jobStatus.setBackgroundNotRestrictedConstraintSatisfied(nowElapsed, canRun); didChange |= jobStatus.setUidActive(isActive); return didChange; } private final class UpdateJobFunctor implements Consumer<JobStatus> { - final int activeState; + int mActiveState; boolean mChanged = false; int mTotalCount = 0; int mCheckedCount = 0; - - public UpdateJobFunctor(int newActiveState) { - activeState = newActiveState; + long mUpdateTimeElapsed = 0; + + void prepare(int newActiveState) { + mActiveState = newActiveState; + mUpdateTimeElapsed = sElapsedRealtimeClock.millis(); + mChanged = false; + mTotalCount = 0; + mCheckedCount = 0; } @Override public void accept(JobStatus jobStatus) { mTotalCount++; mCheckedCount++; - if (updateSingleJobRestrictionLocked(jobStatus, activeState)) { + if (updateSingleJobRestrictionLocked(jobStatus, mUpdateTimeElapsed, mActiveState)) { mChanged = true; } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java index 28269c89d13b..6fd094844cd6 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java @@ -65,10 +65,12 @@ public final class BatteryController extends RestrictingController { @Override public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { if (taskStatus.hasPowerConstraint()) { + final long nowElapsed = sElapsedRealtimeClock.millis(); mTrackedTasks.add(taskStatus); taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY); - taskStatus.setChargingConstraintSatisfied(mChargeTracker.isOnStablePower()); - taskStatus.setBatteryNotLowConstraintSatisfied(mChargeTracker.isBatteryNotLow()); + taskStatus.setChargingConstraintSatisfied(nowElapsed, mChargeTracker.isOnStablePower()); + taskStatus.setBatteryNotLowConstraintSatisfied( + nowElapsed, mChargeTracker.isBatteryNotLow()); } } @@ -97,14 +99,15 @@ public final class BatteryController extends RestrictingController { if (DEBUG) { Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower); } + final long nowElapsed = sElapsedRealtimeClock.millis(); boolean reportChange = false; for (int i = mTrackedTasks.size() - 1; i >= 0; i--) { final JobStatus ts = mTrackedTasks.valueAt(i); - boolean previous = ts.setChargingConstraintSatisfied(stablePower); + boolean previous = ts.setChargingConstraintSatisfied(nowElapsed, stablePower); if (previous != stablePower) { reportChange = true; } - previous = ts.setBatteryNotLowConstraintSatisfied(batteryNotLow); + previous = ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow); if (previous != batteryNotLow) { reportChange = true; } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index d249f2ae813c..6e542f346f81 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -20,6 +20,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.Nullable; import android.app.job.JobInfo; @@ -325,6 +326,8 @@ public final class ConnectivityController extends RestrictingController implemen */ private boolean isInsane(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { + // Use the maximum possible time since it gives us an upper bound, even though the job + // could end up stopping earlier. final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus); final long downloadBytes = jobStatus.getEstimatedNetworkDownloadBytes(); @@ -459,11 +462,12 @@ public final class ConnectivityController extends RestrictingController implemen final Network network = mConnManager.getActiveNetworkForUid( jobStatus.getSourceUid(), jobStatus.shouldIgnoreNetworkBlocking()); final NetworkCapabilities capabilities = getNetworkCapabilities(network); - return updateConstraintsSatisfied(jobStatus, network, capabilities); + return updateConstraintsSatisfied(jobStatus, sElapsedRealtimeClock.millis(), + network, capabilities); } - private boolean updateConstraintsSatisfied(JobStatus jobStatus, Network network, - NetworkCapabilities capabilities) { + private boolean updateConstraintsSatisfied(JobStatus jobStatus, final long nowElapsed, + Network network, NetworkCapabilities capabilities) { // TODO: consider matching against non-active networks final boolean ignoreBlocked = jobStatus.shouldIgnoreNetworkBlocking(); @@ -474,7 +478,7 @@ public final class ConnectivityController extends RestrictingController implemen final boolean satisfied = isSatisfied(jobStatus, network, capabilities, mConstants); final boolean changed = jobStatus - .setConnectivityConstraintSatisfied(connected && satisfied); + .setConnectivityConstraintSatisfied(nowElapsed, connected && satisfied); // Pass along the evaluated network for job to use; prevents race // conditions as default routes change over time, and opens the door to @@ -528,6 +532,7 @@ public final class ConnectivityController extends RestrictingController implemen NetworkCapabilities exemptedNetworkCapabilities = null; boolean exemptedNetworkMatch = false; + final long nowElapsed = sElapsedRealtimeClock.millis(); boolean changed = false; for (int i = jobs.size() - 1; i >= 0; i--) { final JobStatus js = jobs.valueAt(i); @@ -553,7 +558,7 @@ public final class ConnectivityController extends RestrictingController implemen // job hasn't yet been evaluated against the currently // active network; typically when we just lost a network. if (match || !Objects.equals(js.network, net)) { - changed |= updateConstraintsSatisfied(js, net, netCap); + changed |= updateConstraintsSatisfied(js, nowElapsed, net, netCap); } } return changed; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java index 131a6d4f4791..8b0da3471781 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java @@ -16,6 +16,8 @@ package com.android.server.job.controllers; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + import android.annotation.UserIdInt; import android.app.job.JobInfo; import android.database.ContentObserver; @@ -74,6 +76,7 @@ public final class ContentObserverController extends StateController { @Override public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { if (taskStatus.hasContentTriggerConstraint()) { + final long nowElapsed = sElapsedRealtimeClock.millis(); if (taskStatus.contentObserverJobInstance == null) { taskStatus.contentObserverJobInstance = new JobInstance(taskStatus); } @@ -110,7 +113,7 @@ public final class ContentObserverController extends StateController { } taskStatus.changedAuthorities = null; taskStatus.changedUris = null; - taskStatus.setContentTriggerConstraintSatisfied(havePendingUris); + taskStatus.setContentTriggerConstraintSatisfied(nowElapsed, havePendingUris); } if (lastJob != null && lastJob.contentObserverJobInstance != null) { // And now we can detach the instance state from the last job. @@ -295,7 +298,8 @@ public final class ContentObserverController extends StateController { boolean reportChange = false; synchronized (mLock) { if (mTriggerPending) { - if (mJobStatus.setContentTriggerConstraintSatisfied(true)) { + final long nowElapsed = sElapsedRealtimeClock.millis(); + if (mJobStatus.setContentTriggerConstraintSatisfied(nowElapsed, true)) { reportChange = true; } unscheduleLocked(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java index 04b41646b48b..192f5e66255d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java @@ -16,6 +16,8 @@ package com.android.server.job.controllers; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + import android.app.job.JobInfo; import android.content.BroadcastReceiver; import android.content.Context; @@ -104,8 +106,10 @@ public final class DeviceIdleJobsController extends StateController { + Arrays.toString(mPowerSaveTempWhitelistAppIds)); } boolean changed = false; + final long nowElapsed = sElapsedRealtimeClock.millis(); for (int i = 0; i < mAllowInIdleJobs.size(); i++) { - changed |= updateTaskStateLocked(mAllowInIdleJobs.valueAt(i)); + changed |= + updateTaskStateLocked(mAllowInIdleJobs.valueAt(i), nowElapsed); } if (changed) { mStateChangedListener.onControllerStateChanged(); @@ -147,6 +151,7 @@ public final class DeviceIdleJobsController extends StateController { } mDeviceIdleMode = enabled; if (DEBUG) Slog.d(TAG, "mDeviceIdleMode=" + mDeviceIdleMode); + mDeviceIdleUpdateFunctor.prepare(); if (enabled) { mHandler.removeMessages(PROCESS_BACKGROUND_JOBS); mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor); @@ -180,7 +185,7 @@ public final class DeviceIdleJobsController extends StateController { Slog.d(TAG, "uid " + uid + " going " + (active ? "active" : "inactive")); } mForegroundUids.put(uid, active); - mDeviceIdleUpdateFunctor.mChanged = false; + mDeviceIdleUpdateFunctor.prepare(); mService.getJobStore().forEachJobForSourceUid(uid, mDeviceIdleUpdateFunctor); if (mDeviceIdleUpdateFunctor.mChanged) { mStateChangedListener.onControllerStateChanged(); @@ -203,12 +208,12 @@ public final class DeviceIdleJobsController extends StateController { UserHandle.getAppId(job.getSourceUid())); } - private boolean updateTaskStateLocked(JobStatus task) { + private boolean updateTaskStateLocked(JobStatus task, final long nowElapsed) { final boolean allowInIdle = ((task.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) && (mForegroundUids.get(task.getSourceUid()) || isTempWhitelistedLocked(task)); final boolean whitelisted = isWhitelistedLocked(task); final boolean enableTask = !mDeviceIdleMode || whitelisted || allowInIdle; - return task.setDeviceNotDozingConstraintSatisfied(enableTask, whitelisted); + return task.setDeviceNotDozingConstraintSatisfied(nowElapsed, enableTask, whitelisted); } @Override @@ -216,7 +221,7 @@ public final class DeviceIdleJobsController extends StateController { if ((jobStatus.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) { mAllowInIdleJobs.add(jobStatus); } - updateTaskStateLocked(jobStatus); + updateTaskStateLocked(jobStatus, sElapsedRealtimeClock.millis()); } @Override @@ -282,10 +287,16 @@ public final class DeviceIdleJobsController extends StateController { final class DeviceIdleUpdateFunctor implements Consumer<JobStatus> { boolean mChanged; + long mUpdateTimeElapsed = 0; + + void prepare() { + mChanged = false; + mUpdateTimeElapsed = sElapsedRealtimeClock.millis(); + } @Override public void accept(JobStatus jobStatus) { - mChanged |= updateTaskStateLocked(jobStatus); + mChanged |= updateTaskStateLocked(jobStatus, mUpdateTimeElapsed); } } @@ -300,7 +311,7 @@ public final class DeviceIdleJobsController extends StateController { case PROCESS_BACKGROUND_JOBS: // Just process all the jobs, the ones in foreground should already be running. synchronized (mLock) { - mDeviceIdleUpdateFunctor.mChanged = false; + mDeviceIdleUpdateFunctor.prepare(); mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor); if (mDeviceIdleUpdateFunctor.mChanged) { mStateChangedListener.onControllerStateChanged(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java index 2fe827e338e9..e26a3c6962d5 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java @@ -16,6 +16,8 @@ package com.android.server.job.controllers; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + import android.content.Context; import android.content.pm.PackageManager; import android.os.UserHandle; @@ -58,9 +60,10 @@ public final class IdleController extends RestrictingController implements Idlen @Override public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { if (taskStatus.hasIdleConstraint()) { + final long nowElapsed = sElapsedRealtimeClock.millis(); mTrackedTasks.add(taskStatus); taskStatus.setTrackingController(JobStatus.TRACKING_IDLE); - taskStatus.setIdleConstraintSatisfied(mIdleTracker.isIdle()); + taskStatus.setIdleConstraintSatisfied(nowElapsed, mIdleTracker.isIdle()); } } @@ -90,8 +93,9 @@ public final class IdleController extends RestrictingController implements Idlen @Override public void reportNewIdleState(boolean isIdle) { synchronized (mLock) { + final long nowElapsed = sElapsedRealtimeClock.millis(); for (int i = mTrackedTasks.size()-1; i >= 0; i--) { - mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(isIdle); + mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(nowElapsed, isIdle); } } mStateChangedListener.onControllerStateChanged(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index 539c3c960f2e..5bdeb38a1424 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -35,6 +35,7 @@ import android.os.UserHandle; import android.provider.MediaStore; import android.text.format.DateFormat; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.Pair; import android.util.Slog; import android.util.TimeUtils; @@ -72,6 +73,8 @@ public final class JobStatus { private static final String TAG = "JobScheduler.JobStatus"; static final boolean DEBUG = JobSchedulerService.DEBUG; + private static final int NUM_CONSTRAINT_CHANGE_HISTORY = 10; + public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; public static final long NO_EARLIEST_RUNTIME = 0L; @@ -349,6 +352,10 @@ public final class JobStatus { */ private Pair<Long, Long> mPersistedUtcTimes; + private int mConstraintChangeHistoryIndex = 0; + private final long[] mConstraintUpdatedTimesElapsed = new long[NUM_CONSTRAINT_CHANGE_HISTORY]; + private final int[] mConstraintStatusHistory = new int[NUM_CONSTRAINT_CHANGE_HISTORY]; + /** * For use only by ContentObserverController: state it is maintaining about content URIs * being observed. @@ -1089,28 +1096,28 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setChargingConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_CHARGING, state); + boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setBatteryNotLowConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state); + boolean setBatteryNotLowConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setStorageNotLowConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, state); + boolean setStorageNotLowConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setTimingDelayConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state); + boolean setTimingDelayConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setDeadlineConstraintSatisfied(boolean state) { - if (setConstraintSatisfied(CONSTRAINT_DEADLINE, state)) { + boolean setDeadlineConstraintSatisfied(final long nowElapsed, boolean state) { + if (setConstraintSatisfied(CONSTRAINT_DEADLINE, nowElapsed, state)) { // The constraint was changed. Update the ready flag. mReadyDeadlineSatisfied = !job.isPeriodic() && hasDeadlineConstraint() && state; return true; @@ -1119,24 +1126,25 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setIdleConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_IDLE, state); + boolean setIdleConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_IDLE, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setConnectivityConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state); + boolean setConnectivityConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setContentTriggerConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state); + boolean setContentTriggerConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) { + boolean setDeviceNotDozingConstraintSatisfied(final long nowElapsed, + boolean state, boolean whitelisted) { dozeWhitelisted = whitelisted; - if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) { + if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, nowElapsed, state)) { // The constraint was changed. Update the ready flag. mReadyNotDozing = state || canRunInDoze(); return true; @@ -1145,8 +1153,8 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) { - if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state)) { + boolean setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state) { + if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, nowElapsed, state)) { // The constraint was changed. Update the ready flag. mReadyNotRestrictedInBg = state; return true; @@ -1155,8 +1163,8 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setQuotaConstraintSatisfied(boolean state) { - if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, state)) { + boolean setQuotaConstraintSatisfied(final long nowElapsed, boolean state) { + if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, nowElapsed, state)) { // The constraint was changed. Update the ready flag. mReadyWithinQuota = state; return true; @@ -1165,8 +1173,8 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setExpeditedJobQuotaConstraintSatisfied(boolean state) { - if (setConstraintSatisfied(CONSTRAINT_WITHIN_EXPEDITED_QUOTA, state)) { + boolean setExpeditedJobQuotaConstraintSatisfied(final long nowElapsed, boolean state) { + if (setConstraintSatisfied(CONSTRAINT_WITHIN_EXPEDITED_QUOTA, nowElapsed, state)) { // The constraint was changed. Update the ready flag. mReadyWithinExpeditedQuota = state; // DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag. @@ -1189,7 +1197,7 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setConstraintSatisfied(int constraint, boolean state) { + boolean setConstraintSatisfied(int constraint, final long nowElapsed, boolean state) { boolean old = (satisfiedConstraints&constraint) != 0; if (old == state) { return false; @@ -1211,6 +1219,12 @@ public final class JobStatus { : FrameworkStatsLog .SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED); } + + mConstraintUpdatedTimesElapsed[mConstraintChangeHistoryIndex] = nowElapsed; + mConstraintStatusHistory[mConstraintChangeHistoryIndex] = satisfiedConstraints; + mConstraintChangeHistoryIndex = + (mConstraintChangeHistoryIndex + 1) % NUM_CONSTRAINT_CHANGE_HISTORY; + return true; } @@ -1644,14 +1658,18 @@ public final class JobStatus { } } - private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) { - pw.print(prefix); pw.print(" #"); pw.print(index); pw.print(": #"); + private void dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index) { + pw.increaseIndent(); + pw.print("#"); pw.print(index); pw.print(": #"); pw.print(work.getWorkId()); pw.print(" "); pw.print(work.getDeliveryCount()); pw.print("x "); pw.println(work.getIntent()); if (work.getGrants() != null) { - pw.print(prefix); pw.println(" URI grants:"); - ((GrantedUriPermissions)work.getGrants()).dump(pw, prefix + " "); + pw.println("URI grants:"); + pw.increaseIndent(); + ((GrantedUriPermissions) work.getGrants()).dump(pw); + pw.decreaseIndent(); } + pw.decreaseIndent(); } private void dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work) { @@ -1695,36 +1713,38 @@ public final class JobStatus { } // Dumpsys infrastructure - public void dump(PrintWriter pw, String prefix, boolean full, long elapsedRealtimeMillis) { - pw.print(prefix); UserHandle.formatUid(pw, callingUid); + public void dump(IndentingPrintWriter pw, boolean full, long nowElapsed) { + UserHandle.formatUid(pw, callingUid); pw.print(" tag="); pw.println(tag); - pw.print(prefix); + pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid()); pw.print(" user="); pw.print(getSourceUserId()); pw.print(" pkg="); pw.println(getSourcePackageName()); if (full) { - pw.print(prefix); pw.println("JobInfo:"); - pw.print(prefix); pw.print(" Service: "); + pw.println("JobInfo:"); + pw.increaseIndent(); + + pw.print("Service: "); pw.println(job.getService().flattenToShortString()); if (job.isPeriodic()) { - pw.print(prefix); pw.print(" PERIODIC: interval="); + pw.print("PERIODIC: interval="); TimeUtils.formatDuration(job.getIntervalMillis(), pw); pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw); pw.println(); } if (job.isPersisted()) { - pw.print(prefix); pw.println(" PERSISTED"); + pw.println("PERSISTED"); } if (job.getPriority() != 0) { - pw.print(prefix); pw.print(" Priority: "); + pw.print("Priority: "); pw.println(JobInfo.getPriorityString(job.getPriority())); } if (job.getFlags() != 0) { - pw.print(prefix); pw.print(" Flags: "); + pw.print("Flags: "); pw.println(Integer.toHexString(job.getFlags())); } if (getInternalFlags() != 0) { - pw.print(prefix); pw.print(" Internal flags: "); + pw.print("Internal flags: "); pw.print(Integer.toHexString(getInternalFlags())); if ((getInternalFlags()&INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) { @@ -1732,106 +1752,125 @@ public final class JobStatus { } pw.println(); } - pw.print(prefix); pw.print(" Requires: charging="); + pw.print("Requires: charging="); pw.print(job.isRequireCharging()); pw.print(" batteryNotLow="); pw.print(job.isRequireBatteryNotLow()); pw.print(" deviceIdle="); pw.println(job.isRequireDeviceIdle()); if (job.getTriggerContentUris() != null) { - pw.print(prefix); pw.println(" Trigger content URIs:"); + pw.println("Trigger content URIs:"); + pw.increaseIndent(); for (int i = 0; i < job.getTriggerContentUris().length; i++) { JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i]; - pw.print(prefix); pw.print(" "); pw.print(Integer.toHexString(trig.getFlags())); pw.print(' '); pw.println(trig.getUri()); } + pw.decreaseIndent(); if (job.getTriggerContentUpdateDelay() >= 0) { - pw.print(prefix); pw.print(" Trigger update delay: "); + pw.print("Trigger update delay: "); TimeUtils.formatDuration(job.getTriggerContentUpdateDelay(), pw); pw.println(); } if (job.getTriggerContentMaxDelay() >= 0) { - pw.print(prefix); pw.print(" Trigger max delay: "); + pw.print("Trigger max delay: "); TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw); pw.println(); } } if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) { - pw.print(prefix); pw.print(" Extras: "); + pw.print("Extras: "); pw.println(job.getExtras().toShortString()); } if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) { - pw.print(prefix); pw.print(" Transient extras: "); + pw.print("Transient extras: "); pw.println(job.getTransientExtras().toShortString()); } if (job.getClipData() != null) { - pw.print(prefix); pw.print(" Clip data: "); + pw.print("Clip data: "); StringBuilder b = new StringBuilder(128); b.append(job.getClipData()); pw.println(b); } if (uriPerms != null) { - pw.print(prefix); pw.println(" Granted URI permissions:"); - uriPerms.dump(pw, prefix + " "); + pw.println("Granted URI permissions:"); + uriPerms.dump(pw); } if (job.getRequiredNetwork() != null) { - pw.print(prefix); pw.print(" Network type: "); + pw.print("Network type: "); pw.println(job.getRequiredNetwork()); } if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { - pw.print(prefix); pw.print(" Network download bytes: "); + pw.print("Network download bytes: "); pw.println(mTotalNetworkDownloadBytes); } if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { - pw.print(prefix); pw.print(" Network upload bytes: "); + pw.print("Network upload bytes: "); pw.println(mTotalNetworkUploadBytes); } if (job.getMinLatencyMillis() != 0) { - pw.print(prefix); pw.print(" Minimum latency: "); + pw.print("Minimum latency: "); TimeUtils.formatDuration(job.getMinLatencyMillis(), pw); pw.println(); } if (job.getMaxExecutionDelayMillis() != 0) { - pw.print(prefix); pw.print(" Max execution delay: "); + pw.print("Max execution delay: "); TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw); pw.println(); } - pw.print(prefix); pw.print(" Backoff: policy="); pw.print(job.getBackoffPolicy()); + pw.print("Backoff: policy="); pw.print(job.getBackoffPolicy()); pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw); pw.println(); if (job.hasEarlyConstraint()) { - pw.print(prefix); pw.println(" Has early constraint"); + pw.println("Has early constraint"); } if (job.hasLateConstraint()) { - pw.print(prefix); pw.println(" Has late constraint"); + pw.println("Has late constraint"); } + + pw.decreaseIndent(); } - pw.print(prefix); pw.print("Required constraints:"); + + pw.print("Required constraints:"); dumpConstraints(pw, requiredConstraints); pw.println(); - pw.print(prefix); pw.print("Dynamic constraints:"); dumpConstraints(pw, mDynamicConstraints); pw.println(); if (full) { - pw.print(prefix); pw.print("Satisfied constraints:"); + pw.print("Satisfied constraints:"); dumpConstraints(pw, satisfiedConstraints); pw.println(); - pw.print(prefix); pw.print("Unsatisfied constraints:"); + pw.print("Unsatisfied constraints:"); dumpConstraints(pw, ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints)); pw.println(); + + pw.println("Constraint history:"); + pw.increaseIndent(); + for (int h = 0; h < NUM_CONSTRAINT_CHANGE_HISTORY; ++h) { + final int idx = (h + mConstraintChangeHistoryIndex) % NUM_CONSTRAINT_CHANGE_HISTORY; + if (mConstraintUpdatedTimesElapsed[idx] == 0) { + continue; + } + TimeUtils.formatDuration(mConstraintUpdatedTimesElapsed[idx], nowElapsed, pw); + // dumpConstraints prepends with a space, so no need to add a space after the = + pw.print(" ="); + dumpConstraints(pw, mConstraintStatusHistory[idx]); + pw.println(); + } + pw.decreaseIndent(); + if (dozeWhitelisted) { - pw.print(prefix); pw.println("Doze whitelisted: true"); + pw.println("Doze whitelisted: true"); } if (uidActive) { - pw.print(prefix); pw.println("Uid: active"); + pw.println("Uid: active"); } if (job.isExemptedFromAppStandby()) { - pw.print(prefix); pw.println("Is exempted from app standby"); + pw.println("Is exempted from app standby"); } } if (trackingControllers != 0) { - pw.print(prefix); pw.print("Tracking:"); + pw.print("Tracking:"); if ((trackingControllers&TRACKING_BATTERY) != 0) pw.print(" BATTERY"); if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) pw.print(" CONNECTIVITY"); if ((trackingControllers&TRACKING_CONTENT) != 0) pw.print(" CONTENT"); @@ -1842,92 +1881,93 @@ public final class JobStatus { pw.println(); } - pw.print(prefix); pw.println("Implicit constraints:"); - pw.print(prefix); pw.print(" readyNotDozing: "); + pw.println("Implicit constraints:"); + pw.increaseIndent(); + pw.print("readyNotDozing: "); pw.println(mReadyNotDozing); - pw.print(prefix); pw.print(" readyNotRestrictedInBg: "); + pw.print("readyNotRestrictedInBg: "); pw.println(mReadyNotRestrictedInBg); if (!job.isPeriodic() && hasDeadlineConstraint()) { - pw.print(prefix); pw.print(" readyDeadlineSatisfied: "); + pw.print("readyDeadlineSatisfied: "); pw.println(mReadyDeadlineSatisfied); } if (mDynamicConstraints != 0) { - pw.print(prefix); - pw.print(" readyDynamicSatisfied: "); + pw.print("readyDynamicSatisfied: "); pw.println(mReadyDynamicSatisfied); } - pw.print(prefix); - pw.print(" readyComponentEnabled: "); + pw.print("readyComponentEnabled: "); pw.println(serviceInfo != null); if ((getFlags() & JobInfo.FLAG_EXPEDITED) != 0) { - pw.print(prefix); - pw.print(" mReadyWithinExpeditedQuota: "); + pw.print("readyWithinExpeditedQuota: "); pw.println(mReadyWithinExpeditedQuota); } + pw.decreaseIndent(); if (changedAuthorities != null) { - pw.print(prefix); pw.println("Changed authorities:"); + pw.println("Changed authorities:"); + pw.increaseIndent(); for (int i=0; i<changedAuthorities.size(); i++) { - pw.print(prefix); pw.print(" "); pw.println(changedAuthorities.valueAt(i)); + pw.println(changedAuthorities.valueAt(i)); } + pw.decreaseIndent(); } if (changedUris != null) { - pw.print(prefix); pw.println("Changed URIs:"); + pw.increaseIndent(); for (int i = 0; i < changedUris.size(); i++) { - pw.print(prefix); - pw.print(" "); pw.println(changedUris.valueAt(i)); } + pw.decreaseIndent(); } if (network != null) { - pw.print(prefix); pw.print("Network: "); pw.println(network); + pw.print("Network: "); pw.println(network); } if (pendingWork != null && pendingWork.size() > 0) { - pw.print(prefix); pw.println("Pending work:"); + pw.println("Pending work:"); for (int i = 0; i < pendingWork.size(); i++) { - dumpJobWorkItem(pw, prefix, pendingWork.get(i), i); + dumpJobWorkItem(pw, pendingWork.get(i), i); } } if (executingWork != null && executingWork.size() > 0) { - pw.print(prefix); pw.println("Executing work:"); + pw.println("Executing work:"); for (int i = 0; i < executingWork.size(); i++) { - dumpJobWorkItem(pw, prefix, executingWork.get(i), i); + dumpJobWorkItem(pw, executingWork.get(i), i); } } - pw.print(prefix); pw.print("Standby bucket: "); + pw.print("Standby bucket: "); pw.println(getBucketName()); + pw.increaseIndent(); if (whenStandbyDeferred != 0) { - pw.print(prefix); pw.print(" Deferred since: "); - TimeUtils.formatDuration(whenStandbyDeferred, elapsedRealtimeMillis, pw); + pw.print("Deferred since: "); + TimeUtils.formatDuration(whenStandbyDeferred, nowElapsed, pw); pw.println(); } if (mFirstForceBatchedTimeElapsed != 0) { - pw.print(prefix); - pw.print(" Time since first force batch attempt: "); - TimeUtils.formatDuration(mFirstForceBatchedTimeElapsed, elapsedRealtimeMillis, pw); + pw.print("Time since first force batch attempt: "); + TimeUtils.formatDuration(mFirstForceBatchedTimeElapsed, nowElapsed, pw); pw.println(); } - pw.print(prefix); pw.print("Enqueue time: "); - TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw); + pw.decreaseIndent(); + + pw.print("Enqueue time: "); + TimeUtils.formatDuration(enqueueTime, nowElapsed, pw); pw.println(); - pw.print(prefix); pw.print("Run time: earliest="); - formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, elapsedRealtimeMillis); + pw.print("Run time: earliest="); + formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, nowElapsed); pw.print(", latest="); - formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, elapsedRealtimeMillis); + formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed); pw.print(", original latest="); - formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, - NO_LATEST_RUNTIME, elapsedRealtimeMillis); + formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed); pw.println(); if (numFailures != 0) { - pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures); + pw.print("Num failures: "); pw.println(numFailures); } if (mLastSuccessfulRunTime != 0) { - pw.print(prefix); pw.print("Last successful run: "); + pw.print("Last successful run: "); pw.println(formatTime(mLastSuccessfulRunTime)); } if (mLastFailedRunTime != 0) { - pw.print(prefix); pw.print("Last failed run: "); + pw.print("Last failed run: "); pw.println(formatTime(mLastFailedRunTime)); } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index f2d10ac0f7d7..b70e68b739d8 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -72,7 +72,6 @@ import com.android.server.LocalServices; import com.android.server.PowerAllowlistInternal; import com.android.server.job.ConstantsProto; import com.android.server.job.JobSchedulerService; -import com.android.server.job.JobServiceContext; import com.android.server.job.StateControllerProto; import com.android.server.usage.AppStandbyInternal; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; @@ -627,6 +626,7 @@ public final class QuotaController extends StateController { @Override public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { + final long nowElapsed = sElapsedRealtimeClock.millis(); final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName); @@ -637,11 +637,11 @@ public final class QuotaController extends StateController { jobs.add(jobStatus); jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA); final boolean isWithinQuota = isWithinQuotaLocked(jobStatus); - setConstraintSatisfied(jobStatus, isWithinQuota); + setConstraintSatisfied(jobStatus, nowElapsed, isWithinQuota); final boolean outOfEJQuota; if (jobStatus.isRequestedExpeditedJob()) { final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus); - setExpeditedConstraintSatisfied(jobStatus, isWithinEJQuota); + setExpeditedConstraintSatisfied(jobStatus, nowElapsed, isWithinEJQuota); outOfEJQuota = !isWithinEJQuota; } else { outOfEJQuota = false; @@ -770,18 +770,38 @@ public final class QuotaController extends StateController { /** Returns the maximum amount of time this job could run for. */ public long getMaxJobExecutionTimeMsLocked(@NonNull final JobStatus jobStatus) { - // If quota is currently "free", then the job can run for the full amount of time. - if (mChargeTracker.isCharging() - || isTopStartedJobLocked(jobStatus) - || isUidInForeground(jobStatus.getSourceUid())) { - return JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS; - } - if (jobStatus.shouldTreatAsExpeditedJob()) { - return jobStatus.getStandbyBucket() == RESTRICTED_INDEX - ? JobServiceContext.DEFAULT_RESTRICTED_EXPEDITED_JOB_EXECUTING_TIMESLICE_MILLIS - : JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS; - } - return getRemainingExecutionTimeLocked(jobStatus); + // Need to look at current proc state as well in the case where the job hasn't started yet. + final boolean isTop = mActivityManagerInternal + .getUidProcessState(jobStatus.getSourceUid()) <= ActivityManager.PROCESS_STATE_TOP; + + if (!jobStatus.shouldTreatAsExpeditedJob()) { + // If quota is currently "free", then the job can run for the full amount of time. + if (mChargeTracker.isCharging() + || isTop + || isTopStartedJobLocked(jobStatus) + || isUidInForeground(jobStatus.getSourceUid())) { + return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; + } + return getTimeUntilQuotaConsumedLocked( + jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()); + } + + // Expedited job. + if (mChargeTracker.isCharging()) { + return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; + } + if (isTop || isTopStartedJobLocked(jobStatus)) { + return Math.max(mEJLimitsMs[ACTIVE_INDEX] / 2, + getTimeUntilEJQuotaConsumedLocked( + jobStatus.getSourceUserId(), jobStatus.getSourcePackageName())); + } + if (isUidInForeground(jobStatus.getSourceUid())) { + return Math.max(mEJLimitsMs[WORKING_INDEX] / 2, + getTimeUntilEJQuotaConsumedLocked( + jobStatus.getSourceUserId(), jobStatus.getSourcePackageName())); + } + return getTimeUntilEJQuotaConsumedLocked( + jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()); } /** @return true if the job is within expedited job quota. */ @@ -808,8 +828,8 @@ public final class QuotaController extends StateController { } @NonNull - private ShrinkableDebits getEJQuotaLocked(final int userId, - @NonNull final String packageName) { + @VisibleForTesting + ShrinkableDebits getEJDebitsLocked(final int userId, @NonNull final String packageName) { ShrinkableDebits debits = mEJStats.get(userId, packageName); if (debits == null) { debits = new ShrinkableDebits( @@ -911,7 +931,7 @@ public final class QuotaController extends StateController { @VisibleForTesting long getRemainingEJExecutionTimeLocked(final int userId, @NonNull final String packageName) { - ShrinkableDebits quota = getEJQuotaLocked(userId, packageName); + ShrinkableDebits quota = getEJDebitsLocked(userId, packageName); if (quota.getStandbyBucketLocked() == NEVER_INDEX) { return 0; } @@ -928,7 +948,7 @@ public final class QuotaController extends StateController { if (ts.endTimeElapsed < windowStartTimeElapsed) { final long duration = ts.endTimeElapsed - ts.startTimeElapsed; remainingMs += duration; - quota.transactOnDebitsLocked(-duration); + quota.transactLocked(-duration); timingSessions.remove(0); } else if (ts.startTimeElapsed < windowStartTimeElapsed) { remainingMs += windowStartTimeElapsed - ts.startTimeElapsed; @@ -940,15 +960,16 @@ public final class QuotaController extends StateController { } } + TopAppTimer topAppTimer = mTopAppTrackers.get(userId, packageName); + if (topAppTimer != null && topAppTimer.isActive()) { + remainingMs += topAppTimer.getPendingReward(nowElapsed); + } + Timer timer = mEJPkgTimers.get(userId, packageName); if (timer == null) { return remainingMs; } - // There's a case where the debits tally is 0 but a currently running HPJ still counts - // towards quota. If the app gets a reward in this case, the reward is lost and the HPJ - // run is still fully counted. - // TODO(171305774)/STOPSHIP: make sure getting rewards while HPJ currently executing isn't - // treated negatively + return remainingMs - timer.getCurrentDuration(sElapsedRealtimeClock.millis()); } @@ -974,12 +995,18 @@ public final class QuotaController extends StateController { if (standbyBucket == NEVER_INDEX) { return 0; } + List<TimingSession> sessions = mTimingSessions.get(userId, packageName); + final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket); if (sessions == null || sessions.size() == 0) { + // Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can + // essentially run until they reach the maximum limit. + if (stats.windowSizeMs == mAllowedTimePerPeriodMs) { + return mMaxExecutionTimeMs; + } return mAllowedTimePerPeriodMs; } - final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket); final long startWindowElapsed = nowElapsed - stats.windowSizeMs; final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS; final long allowedTimeRemainingMs = mAllowedTimePerPeriodMs - stats.executionTimeInWindowMs; @@ -1061,7 +1088,7 @@ public final class QuotaController extends StateController { } final long nowElapsed = sElapsedRealtimeClock.millis(); - ShrinkableDebits quota = getEJQuotaLocked(userId, packageName); + ShrinkableDebits quota = getEJDebitsLocked(userId, packageName); final long limitMs = getEJLimitMsLocked(packageName, quota.getStandbyBucketLocked()); final long startWindowElapsed = Math.max(0, nowElapsed - mEJLimitWindowSizeMs); long remainingDeadSpaceMs = remainingExecutionTimeMs; @@ -1352,6 +1379,11 @@ public final class QuotaController extends StateController { @VisibleForTesting void saveTimingSession(final int userId, @NonNull final String packageName, @NonNull final TimingSession session, boolean isExpedited) { + saveTimingSession(userId, packageName, session, isExpedited, 0); + } + + private void saveTimingSession(final int userId, @NonNull final String packageName, + @NonNull final TimingSession session, boolean isExpedited, long debitAdjustment) { synchronized (mLock) { final SparseArrayMap<String, List<TimingSession>> sessionMap = isExpedited ? mEJTimingSessions : mTimingSessions; @@ -1362,8 +1394,9 @@ public final class QuotaController extends StateController { } sessions.add(session); if (isExpedited) { - final ShrinkableDebits quota = getEJQuotaLocked(userId, packageName); - quota.transactOnDebitsLocked(session.endTimeElapsed - session.startTimeElapsed); + final ShrinkableDebits quota = getEJDebitsLocked(userId, packageName); + quota.transactLocked(session.endTimeElapsed - session.startTimeElapsed + + debitAdjustment); } else { // Adding a new session means that the current stats are now incorrect. invalidateAllExecutionStatsLocked(userId, packageName); @@ -1376,14 +1409,34 @@ public final class QuotaController extends StateController { private void grantRewardForInstantEvent( final int userId, @NonNull final String packageName, final long credit) { synchronized (mLock) { - final ShrinkableDebits quota = getEJQuotaLocked(userId, packageName); - quota.transactOnDebitsLocked(-credit); - if (maybeUpdateConstraintForPkgLocked(userId, packageName)) { + final long nowElapsed = sElapsedRealtimeClock.millis(); + final ShrinkableDebits quota = getEJDebitsLocked(userId, packageName); + if (transactQuotaLocked(userId, packageName, nowElapsed, quota, credit) + && maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName)) { mStateChangedListener.onControllerStateChanged(); } } } + private boolean transactQuotaLocked(final int userId, @NonNull final String packageName, + final long nowElapsed, @NonNull ShrinkableDebits debits, final long credit) { + final long oldTally = debits.getTallyLocked(); + final long leftover = debits.transactLocked(-credit); + if (DEBUG) { + Slog.d(TAG, "debits overflowed by " + leftover); + } + boolean changed = oldTally != debits.getTallyLocked(); + if (leftover != 0) { + // Only adjust timer if its active. + final Timer ejTimer = mEJPkgTimers.get(userId, packageName); + if (ejTimer != null && ejTimer.isActive()) { + ejTimer.updateDebitAdjustment(nowElapsed, leftover); + changed = true; + } + } + return changed; + } + private final class EarliestEndTimeFunctor implements Consumer<List<TimingSession>> { public long earliestEndElapsed = Long.MAX_VALUE; @@ -1480,11 +1533,12 @@ public final class QuotaController extends StateController { private void maybeUpdateAllConstraintsLocked() { boolean changed = false; + final long nowElapsed = sElapsedRealtimeClock.millis(); for (int u = 0; u < mTrackedJobs.numMaps(); ++u) { final int userId = mTrackedJobs.keyAt(u); for (int p = 0; p < mTrackedJobs.numElementsForKey(userId); ++p) { final String packageName = mTrackedJobs.keyAt(u, p); - changed |= maybeUpdateConstraintForPkgLocked(userId, packageName); + changed |= maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName); } } if (changed) { @@ -1497,7 +1551,7 @@ public final class QuotaController extends StateController { * * @return true if at least one job had its bit changed */ - private boolean maybeUpdateConstraintForPkgLocked(final int userId, + private boolean maybeUpdateConstraintForPkgLocked(final long nowElapsed, final int userId, @NonNull final String packageName) { ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName); if (jobs == null || jobs.size() == 0) { @@ -1514,21 +1568,21 @@ public final class QuotaController extends StateController { if (isTopStartedJobLocked(js)) { // Job was started while the app was in the TOP state so we should allow it to // finish. - changed |= js.setQuotaConstraintSatisfied(true); + changed |= js.setQuotaConstraintSatisfied(nowElapsed, true); } else if (realStandbyBucket != ACTIVE_INDEX && realStandbyBucket == js.getEffectiveStandbyBucket()) { // An app in the ACTIVE bucket may be out of quota while the job could be in quota // for some reason. Therefore, avoid setting the real value here and check each job // individually. - changed |= setConstraintSatisfied(js, realInQuota); + changed |= setConstraintSatisfied(js, nowElapsed, realInQuota); } else { // This job is somehow exempted. Need to determine its own quota status. - changed |= setConstraintSatisfied(js, isWithinQuotaLocked(js)); + changed |= setConstraintSatisfied(js, nowElapsed, isWithinQuotaLocked(js)); } if (js.isRequestedExpeditedJob()) { boolean isWithinEJQuota = isWithinEJQuotaLocked(js); - changed |= setExpeditedConstraintSatisfied(js, isWithinEJQuota); + changed |= setExpeditedConstraintSatisfied(js, nowElapsed, isWithinEJQuota); outOfEJQuota |= !isWithinEJQuota; } } @@ -1547,14 +1601,21 @@ public final class QuotaController extends StateController { private final SparseArrayMap<String, Integer> mToScheduleStartAlarms = new SparseArrayMap<>(); public boolean wasJobChanged; + long mUpdateTimeElapsed = 0; + + void prepare() { + mUpdateTimeElapsed = sElapsedRealtimeClock.millis(); + } @Override public void accept(JobStatus jobStatus) { - wasJobChanged |= setConstraintSatisfied(jobStatus, isWithinQuotaLocked(jobStatus)); + wasJobChanged |= setConstraintSatisfied( + jobStatus, mUpdateTimeElapsed, isWithinQuotaLocked(jobStatus)); final boolean outOfEJQuota; if (jobStatus.isRequestedExpeditedJob()) { final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus); - wasJobChanged |= setExpeditedConstraintSatisfied(jobStatus, isWithinEJQuota); + wasJobChanged |= setExpeditedConstraintSatisfied( + jobStatus, mUpdateTimeElapsed, isWithinEJQuota); outOfEJQuota = !isWithinEJQuota; } else { outOfEJQuota = false; @@ -1592,6 +1653,7 @@ public final class QuotaController extends StateController { private final UidConstraintUpdater mUpdateUidConstraints = new UidConstraintUpdater(); private boolean maybeUpdateConstraintForUidLocked(final int uid) { + mUpdateUidConstraints.prepare(); mService.getJobStore().forEachJobForSourceUid(uid, mUpdateUidConstraints); mUpdateUidConstraints.postProcess(); @@ -1697,21 +1759,22 @@ public final class QuotaController extends StateController { mInQuotaAlarmListener.addAlarmLocked(userId, packageName, inQuotaTimeElapsed); } - private boolean setConstraintSatisfied(@NonNull JobStatus jobStatus, boolean isWithinQuota) { + private boolean setConstraintSatisfied(@NonNull JobStatus jobStatus, long nowElapsed, + boolean isWithinQuota) { if (!isWithinQuota && jobStatus.getWhenStandbyDeferred() == 0) { // Mark that the job is being deferred due to buckets. - jobStatus.setWhenStandbyDeferred(sElapsedRealtimeClock.millis()); + jobStatus.setWhenStandbyDeferred(nowElapsed); } - return jobStatus.setQuotaConstraintSatisfied(isWithinQuota); + return jobStatus.setQuotaConstraintSatisfied(nowElapsed, isWithinQuota); } /** * If the satisfaction changes, this will tell connectivity & background jobs controller to * also re-evaluate their state. */ - private boolean setExpeditedConstraintSatisfied(@NonNull JobStatus jobStatus, + private boolean setExpeditedConstraintSatisfied(@NonNull JobStatus jobStatus, long nowElapsed, boolean isWithinQuota) { - if (jobStatus.setExpeditedJobQuotaConstraintSatisfied(isWithinQuota)) { + if (jobStatus.setExpeditedJobQuotaConstraintSatisfied(nowElapsed, isWithinQuota)) { mBackgroundJobsController.evaluateStateLocked(jobStatus); mConnectivityController.evaluateStateLocked(jobStatus); if (isWithinQuota && jobStatus.isReady()) { @@ -1844,7 +1907,8 @@ public final class QuotaController extends StateController { } } - private static final class ShrinkableDebits { + @VisibleForTesting + static final class ShrinkableDebits { /** The amount of quota remaining. Can be negative if limit changes. */ private long mDebitTally; private int mStandbyBucket; @@ -1862,8 +1926,11 @@ public final class QuotaController extends StateController { * Negative if the tally should decrease (therefore increasing available quota); * or positive if the tally should increase (therefore decreasing available quota). */ - void transactOnDebitsLocked(final long amount) { + long transactLocked(final long amount) { + final long leftover = amount < 0 && Math.abs(amount) > mDebitTally + ? mDebitTally + amount : 0; mDebitTally = Math.max(0, mDebitTally + amount); + return leftover; } void setStandbyBucketLocked(int standbyBucket) { @@ -1896,6 +1963,7 @@ public final class QuotaController extends StateController { private final ArraySet<JobStatus> mRunningBgJobs = new ArraySet<>(); private long mStartTimeElapsed; private int mBgJobCount; + private long mDebitAdjustment; Timer(int uid, int userId, String packageName, boolean regularJobTimer) { mPkg = new Package(userId, packageName); @@ -1926,6 +1994,7 @@ public final class QuotaController extends StateController { if (mRunningBgJobs.size() == 1) { // Started tracking the first job. mStartTimeElapsed = sElapsedRealtimeClock.millis(); + mDebitAdjustment = 0; if (mRegularJobTimer) { // Starting the timer means that all cached execution stats are now // incorrect. @@ -1957,6 +2026,11 @@ public final class QuotaController extends StateController { } } + void updateDebitAdjustment(long nowElapsed, long debit) { + // Make sure we don't have a credit larger than the expected session. + mDebitAdjustment = Math.max(mDebitAdjustment + debit, mStartTimeElapsed - nowElapsed); + } + /** * Stops tracking all jobs and cancels any pending alarms. This should only be called if * the Timer is not going to be used anymore. @@ -1972,7 +2046,8 @@ public final class QuotaController extends StateController { return; } TimingSession ts = new TimingSession(mStartTimeElapsed, nowElapsed, mBgJobCount); - saveTimingSession(mPkg.userId, mPkg.packageName, ts, !mRegularJobTimer); + saveTimingSession(mPkg.userId, mPkg.packageName, ts, !mRegularJobTimer, + mDebitAdjustment); mBgJobCount = 0; // Don't reset the tracked jobs list as we need to keep tracking the current number // of jobs. @@ -1999,7 +2074,7 @@ public final class QuotaController extends StateController { long getCurrentDuration(long nowElapsed) { synchronized (mLock) { - return !isActive() ? 0 : nowElapsed - mStartTimeElapsed; + return !isActive() ? 0 : nowElapsed - mStartTimeElapsed + mDebitAdjustment; } } @@ -2028,6 +2103,7 @@ public final class QuotaController extends StateController { // Start timing from unplug. if (mRunningBgJobs.size() > 0) { mStartTimeElapsed = nowElapsed; + mDebitAdjustment = 0; // NOTE: this does have the unfortunate consequence that if the device is // repeatedly plugged in and unplugged, or an app changes foreground state // very frequently, the job count for a package may be artificially high. @@ -2097,6 +2173,11 @@ public final class QuotaController extends StateController { pw.print(", "); pw.print(mBgJobCount); pw.print(" running bg jobs"); + if (!mRegularJobTimer) { + pw.print(" (debit adj="); + pw.print(mDebitAdjustment); + pw.print(")"); + } pw.println(); pw.increaseIndent(); for (int i = 0; i < mRunningBgJobs.size(); i++) { @@ -2140,6 +2221,21 @@ public final class QuotaController extends StateController { mPkg = new Package(userId, packageName); } + private int calculateTimeChunks(final long nowElapsed) { + final long totalTopTimeMs = nowElapsed - mStartTimeElapsed; + int numTimeChunks = (int) (totalTopTimeMs / mEJTopAppTimeChunkSizeMs); + final long remainderMs = totalTopTimeMs % mEJTopAppTimeChunkSizeMs; + if (remainderMs >= SECOND_IN_MILLIS) { + // "Round up" + numTimeChunks++; + } + return numTimeChunks; + } + + long getPendingReward(final long nowElapsed) { + return mEJRewardTopAppMs * calculateTimeChunks(nowElapsed); + } + void processEventLocked(@NonNull UsageEvents.Event event) { final long nowElapsed = sElapsedRealtimeClock.millis(); switch (event.getEventType()) { @@ -2155,21 +2251,17 @@ public final class QuotaController extends StateController { final UsageEvents.Event existingEvent = mActivities.removeReturnOld(event.mInstanceId); if (existingEvent != null && mActivities.size() == 0) { - final long totalTopTimeMs = nowElapsed - mStartTimeElapsed; - int numTimeChunks = (int) (totalTopTimeMs / mEJTopAppTimeChunkSizeMs); - final long remainderMs = totalTopTimeMs % mEJTopAppTimeChunkSizeMs; - if (remainderMs >= SECOND_IN_MILLIS) { - // "Round up" - numTimeChunks++; - } + final long pendingReward = getPendingReward(nowElapsed); if (DEBUG) { - Slog.d(TAG, - "Crediting " + mPkg + " for " + numTimeChunks + " time chunks"); + Slog.d(TAG, "Crediting " + mPkg + " " + pendingReward + "ms" + + " for " + calculateTimeChunks(nowElapsed) + " time chunks"); } - final ShrinkableDebits quota = - getEJQuotaLocked(mPkg.userId, mPkg.packageName); - quota.transactOnDebitsLocked(-mEJRewardTopAppMs * numTimeChunks); - if (maybeUpdateConstraintForPkgLocked(mPkg.userId, mPkg.packageName)) { + final ShrinkableDebits debits = + getEJDebitsLocked(mPkg.userId, mPkg.packageName); + if (transactQuotaLocked(mPkg.userId, mPkg.packageName, + nowElapsed, debits, pendingReward) + && maybeUpdateConstraintForPkgLocked(nowElapsed, + mPkg.userId, mPkg.packageName)) { mStateChangedListener.onControllerStateChanged(); } } @@ -2273,7 +2365,8 @@ public final class QuotaController extends StateController { if (timer != null && timer.isActive()) { timer.rescheduleCutoff(); } - if (maybeUpdateConstraintForPkgLocked(userId, packageName)) { + if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(), + userId, packageName)) { mStateChangedListener.onControllerStateChanged(); } } @@ -2288,7 +2381,7 @@ public final class QuotaController extends StateController { */ @Override public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) { - mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event); + mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event).sendToTarget(); } } @@ -2375,7 +2468,6 @@ public final class QuotaController extends StateController { } private class QcHandler extends Handler { - private boolean mIsProcessing; QcHandler(Looper looper) { super(looper); @@ -2384,8 +2476,6 @@ public final class QuotaController extends StateController { @Override public void handleMessage(Message msg) { synchronized (mLock) { - mIsProcessing = true; - switch (msg.what) { case MSG_REACHED_QUOTA: { Package pkg = (Package) msg.obj; @@ -2398,7 +2488,8 @@ public final class QuotaController extends StateController { if (timeRemainingMs <= 50) { // Less than 50 milliseconds left. Start process of shutting down jobs. if (DEBUG) Slog.d(TAG, pkg + " has reached its quota."); - if (maybeUpdateConstraintForPkgLocked(pkg.userId, pkg.packageName)) { + if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(), + pkg.userId, pkg.packageName)) { mStateChangedListener.onControllerStateChanged(); } } else { @@ -2425,7 +2516,8 @@ public final class QuotaController extends StateController { pkg.userId, pkg.packageName); if (timeRemainingMs <= 0) { if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota."); - if (maybeUpdateConstraintForPkgLocked(pkg.userId, pkg.packageName)) { + if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(), + pkg.userId, pkg.packageName)) { mStateChangedListener.onControllerStateChanged(); } } else { @@ -2456,7 +2548,8 @@ public final class QuotaController extends StateController { if (DEBUG) { Slog.d(TAG, "Checking pkg " + string(userId, packageName)); } - if (maybeUpdateConstraintForPkgLocked(userId, packageName)) { + if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(), + userId, packageName)) { mStateChangedListener.onControllerStateChanged(); } break; @@ -2503,6 +2596,10 @@ public final class QuotaController extends StateController { final int userId = msg.arg1; final UsageEvents.Event event = (UsageEvents.Event) msg.obj; final String pkgName = event.getPackageName(); + if (DEBUG) { + Slog.d(TAG, "Processing event " + event.getEventType() + + " for " + string(userId, pkgName)); + } switch (event.getEventType()) { case UsageEvents.Event.ACTIVITY_RESUMED: case UsageEvents.Event.ACTIVITY_PAUSED: @@ -2568,8 +2665,6 @@ public final class QuotaController extends StateController { } } } - - mIsProcessing = false; } } @@ -3577,8 +3672,8 @@ public final class QuotaController extends StateController { mEJLimitsMs[RARE_INDEX] = newRareLimitMs; mShouldReevaluateConstraints = true; } - // The limit must be in the range [0 minutes, rare limit]. - long newRestrictedLimitMs = Math.max(0, + // The limit must be in the range [5 minutes, rare limit]. + long newRestrictedLimitMs = Math.max(5 * MINUTE_IN_MILLIS, Math.min(newRareLimitMs, EJ_LIMIT_RESTRICTED_MS)); if (mEJLimitsMs[RESTRICTED_INDEX] != newRestrictedLimitMs) { mEJLimitsMs[RESTRICTED_INDEX] = newRestrictedLimitMs; @@ -3847,11 +3942,6 @@ public final class QuotaController extends StateController { return mQcConstants; } - @VisibleForTesting - boolean isActiveBackgroundProcessing() { - return mHandler.mIsProcessing; - } - //////////////////////////// DATA DUMP ////////////////////////////// @Override diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java index 0731918d83a1..867891363912 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java @@ -61,9 +61,11 @@ public final class StorageController extends StateController { @Override public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { if (taskStatus.hasStorageNotLowConstraint()) { + final long nowElapsed = sElapsedRealtimeClock.millis(); mTrackedTasks.add(taskStatus); taskStatus.setTrackingController(JobStatus.TRACKING_STORAGE); - taskStatus.setStorageNotLowConstraintSatisfied(mStorageTracker.isStorageNotLow()); + taskStatus.setStorageNotLowConstraintSatisfied( + nowElapsed, mStorageTracker.isStorageNotLow()); } } @@ -76,12 +78,13 @@ public final class StorageController extends StateController { } private void maybeReportNewStorageState() { + final long nowElapsed = sElapsedRealtimeClock.millis(); final boolean storageNotLow = mStorageTracker.isStorageNotLow(); boolean reportChange = false; synchronized (mLock) { for (int i = mTrackedTasks.size() - 1; i >= 0; i--) { final JobStatus ts = mTrackedTasks.valueAt(i); - reportChange |= ts.setStorageNotLowConstraintSatisfied(storageNotLow); + reportChange |= ts.setStorageNotLowConstraintSatisfied(nowElapsed, storageNotLow); } } if (storageNotLow) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java index ede14ec06c71..e8ebfb53fde8 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java @@ -258,9 +258,9 @@ public final class TimeController extends StateController { if (jobDeadline <= nowElapsedMillis) { if (job.hasTimingDelayConstraint()) { - job.setTimingDelayConstraintSatisfied(true); + job.setTimingDelayConstraintSatisfied(nowElapsedMillis, true); } - job.setDeadlineConstraintSatisfied(true); + job.setDeadlineConstraintSatisfied(nowElapsedMillis, true); return true; } return false; @@ -332,7 +332,7 @@ public final class TimeController extends StateController { private boolean evaluateTimingDelayConstraint(JobStatus job, long nowElapsedMillis) { final long jobDelayTime = job.getEarliestRunTime(); if (jobDelayTime <= nowElapsedMillis) { - job.setTimingDelayConstraintSatisfied(true); + job.setTimingDelayConstraintSatisfied(nowElapsedMillis, true); return true; } return false; diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java index 40c8ce0d5c89..954a5b8bdaa8 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java +++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java @@ -64,7 +64,7 @@ public class ThermalStatusRestriction extends JobRestriction { @Override public void dumpConstants(IndentingPrintWriter pw) { pw.print("In thermal throttling?: "); - pw.print(mIsThermalRestricted); + pw.println(mIsThermalRestricted); } @Override diff --git a/apex/jobscheduler/service/jni/Android.bp b/apex/jobscheduler/service/jni/Android.bp index 4bcc165e9eea..c630217387ce 100644 --- a/apex/jobscheduler/service/jni/Android.bp +++ b/apex/jobscheduler/service/jni/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_library_shared { name: "libalarm_jni", diff --git a/apex/media/Android.bp b/apex/media/Android.bp index d6666f521b17..a75f1aed4ade 100644 --- a/apex/media/Android.bp +++ b/apex/media/Android.bp @@ -17,6 +17,12 @@ package { "//frameworks/av/apex", "//frameworks/av/apex/testing", ], + // 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"], } sdk { diff --git a/apex/media/aidl/Android.bp b/apex/media/aidl/Android.bp index c2b00d5849c4..545a0cd884dc 100644 --- a/apex/media/aidl/Android.bp +++ b/apex/media/aidl/Android.bp @@ -14,6 +14,15 @@ // 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: "stable-media-aidl-srcs", srcs: ["stable/**/*.aidl"], diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl index 3d50d14e1b83..fb3172b8c764 100644 --- a/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl +++ b/apex/media/aidl/private/android/media/IMediaCommunicationService.aidl @@ -15,7 +15,17 @@ */ package android.media; +import android.media.Session2Token; +import android.media.IMediaCommunicationServiceCallback; +import android.media.MediaParceledListSlice; + /** {@hide} */ interface IMediaCommunicationService { + void notifySession2Created(in Session2Token sessionToken); + boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid); + MediaParceledListSlice getSession2Tokens(int userId); + + void registerCallback(IMediaCommunicationServiceCallback callback, String packageName); + void unregisterCallback(IMediaCommunicationServiceCallback callback); } diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl new file mode 100644 index 000000000000..3d5321c9d7d8 --- /dev/null +++ b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl @@ -0,0 +1,26 @@ +/** + * 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.media; + +import android.media.Session2Token; +import android.media.MediaParceledListSlice; + +/** {@hide} */ +interface IMediaCommunicationServiceCallback { + void onSession2Created(in Session2Token token); + void onSession2Changed(in MediaParceledListSlice tokens); +} + diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index 5773e4de3f4e..b18a22b408f5 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -12,6 +12,15 @@ // 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: "updatable-media", diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt index a2366df0660a..1beef40b9e4f 100644 --- a/apex/media/framework/api/current.txt +++ b/apex/media/framework/api/current.txt @@ -26,6 +26,7 @@ package android.media { } public class MediaCommunicationManager { + method @NonNull public java.util.List<android.media.Session2Token> getSession2Tokens(); method @IntRange(from=1) public int getVersion(); } diff --git a/apex/media/framework/api/module-lib-current.txt b/apex/media/framework/api/module-lib-current.txt index ad9114fa23cf..eb6397a1826b 100644 --- a/apex/media/framework/api/module-lib-current.txt +++ b/apex/media/framework/api/module-lib-current.txt @@ -1,6 +1,16 @@ // Signature format: 2.0 package android.media { + public class MediaCommunicationManager { + method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaCommunicationManager.SessionCallback); + method public void unregisterSessionCallback(@NonNull android.media.MediaCommunicationManager.SessionCallback); + } + + public static interface MediaCommunicationManager.SessionCallback { + method public default void onSession2TokenCreated(@NonNull android.media.Session2Token); + method public default void onSession2TokensChanged(@NonNull java.util.List<android.media.Session2Token>); + } + public class MediaFrameworkInitializer { method public static void registerServiceWrappers(); method public static void setMediaServiceManager(@NonNull android.media.MediaServiceManager); diff --git a/apex/media/framework/java/android/media/Controller2Link.java b/apex/media/framework/java/android/media/Controller2Link.java index 04185e79b0ad..8eefec73194c 100644 --- a/apex/media/framework/java/android/media/Controller2Link.java +++ b/apex/media/framework/java/android/media/Controller2Link.java @@ -26,7 +26,7 @@ import android.os.ResultReceiver; import java.util.Objects; /** - * Handles incoming commands from {@link MediaSession2} to both {@link MediaController2}. + * Handles incoming commands from {@link MediaSession2} to {@link MediaController2}. * @hide */ // @SystemApi diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java index e686076c871c..9ec25fe48a2e 100644 --- a/apex/media/framework/java/android/media/MediaCommunicationManager.java +++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java @@ -15,18 +15,36 @@ */ package android.media; +import static android.Manifest.permission.MEDIA_CONTENT_CONTROL; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; + +import android.annotation.CallbackExecutor; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; +import android.media.session.MediaSession; +import android.media.session.MediaSessionManager; +import android.os.RemoteException; +import android.os.UserHandle; +import android.service.media.MediaBrowserService; +import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.modules.utils.build.SdkLevel; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; + /** * Provides support for interacting with {@link android.media.MediaSession2 MediaSession2s} * that applications have published to express their ongoing media playback state. */ -// TODO: Add notifySession2Created() and sendMessage(). @SystemService(Context.MEDIA_COMMUNICATION_SERVICE) public class MediaCommunicationManager { private static final String TAG = "MediaCommunicationManager"; @@ -44,6 +62,13 @@ public class MediaCommunicationManager { private final Context mContext; private final IMediaCommunicationService mService; + private final Object mLock = new Object(); + private final CopyOnWriteArrayList<SessionCallbackRecord> mTokenCallbackRecords = + new CopyOnWriteArrayList<>(); + + @GuardedBy("mLock") + private MediaCommunicationServiceCallbackStub mCallbackStub; + /** * @hide */ @@ -64,4 +89,197 @@ public class MediaCommunicationManager { public @IntRange(from = 1) int getVersion() { return CURRENT_VERSION; } + + /** + * Notifies that a new {@link MediaSession2} with type {@link Session2Token#TYPE_SESSION} is + * created. + * @param token newly created session2 token + * @hide + */ + public void notifySession2Created(@NonNull Session2Token token) { + Objects.requireNonNull(token, "token shouldn't be null"); + if (token.getType() != Session2Token.TYPE_SESSION) { + throw new IllegalArgumentException("token's type should be TYPE_SESSION"); + } + try { + mService.notifySession2Created(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Checks whether the remote user is a trusted app. + * <p> + * An app is trusted if the app holds the + * {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission or has an enabled + * notification listener. + * + * @param userInfo The remote user info from either + * {@link MediaSession#getCurrentControllerInfo()} or + * {@link MediaBrowserService#getCurrentBrowserInfo()}. + * @return {@code true} if the remote user is trusted or {@code false} otherwise. + * @hide + */ + public boolean isTrustedForMediaControl(@NonNull MediaSessionManager.RemoteUserInfo userInfo) { + Objects.requireNonNull(userInfo, "userInfo shouldn't be null"); + if (userInfo.getPackageName() == null) { + return false; + } + try { + return mService.isTrusted( + userInfo.getPackageName(), userInfo.getPid(), userInfo.getUid()); + } catch (RemoteException e) { + Log.w(TAG, "Cannot communicate with the service.", e); + } + return false; + } + + /** + * This API is not generally intended for third party application developers. + * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a> + * <a href="{@docRoot}reference/androidx/media2/session/package-summary.html">Media2 session + * Library</a> for consistent behavior across all devices. + * <p> + * Gets a list of {@link Session2Token} with type {@link Session2Token#TYPE_SESSION} for the + * current user. + * <p> + * Although this API can be used without any restriction, each session owners can accept or + * reject your uses of {@link MediaSession2}. + * + * @return A list of {@link Session2Token}. + */ + @NonNull + public List<Session2Token> getSession2Tokens() { + return getSession2Tokens(UserHandle.myUserId()); + } + + /** + * Adds a callback to be notified when the list of active sessions changes. + * <p> + * This requires the {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be + * held by the calling app. + * </p> + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + @RequiresPermission(MEDIA_CONTENT_CONTROL) + public void registerSessionCallback(@CallbackExecutor @NonNull Executor executor, + @NonNull SessionCallback callback) { + Objects.requireNonNull(executor, "executor must not be null"); + Objects.requireNonNull(callback, "callback must not be null"); + + if (!mTokenCallbackRecords.addIfAbsent( + new SessionCallbackRecord(executor, callback))) { + Log.w(TAG, "registerSession2TokenCallback: Ignoring the same callback"); + return; + } + synchronized (mLock) { + if (mCallbackStub == null) { + MediaCommunicationServiceCallbackStub callbackStub = + new MediaCommunicationServiceCallbackStub(); + try { + mService.registerCallback(callbackStub, mContext.getPackageName()); + mCallbackStub = callbackStub; + } catch (RemoteException ex) { + Log.e(TAG, "Failed to register callback.", ex); + } + } + } + } + + /** + * Stops receiving active sessions updates on the specified callback. + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public void unregisterSessionCallback(@NonNull SessionCallback callback) { + if (!mTokenCallbackRecords.remove( + new SessionCallbackRecord(null, callback))) { + Log.w(TAG, "unregisterSession2TokenCallback: Ignoring an unknown callback."); + return; + } + synchronized (mLock) { + if (mCallbackStub != null && mTokenCallbackRecords.isEmpty()) { + try { + mService.unregisterCallback(mCallbackStub); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to unregister callback.", ex); + } + mCallbackStub = null; + } + } + } + + private List<Session2Token> getSession2Tokens(int userId) { + try { + MediaParceledListSlice slice = mService.getSession2Tokens(userId); + return slice == null ? Collections.emptyList() : slice.getList(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get session tokens", e); + } + return Collections.emptyList(); + } + + /** + * Callback for listening to changes to the sessions. + * @see #registerSessionCallback(Executor, SessionCallback) + * @hide + */ + @SystemApi(client = MODULE_LIBRARIES) + public interface SessionCallback { + /** + * Called when a new {@link MediaSession2 media session2} is created. + * @param token the newly created token + */ + default void onSession2TokenCreated(@NonNull Session2Token token) {} + + /** + * Called when {@link #getSession2Tokens() session tokens} are changed. + */ + default void onSession2TokensChanged(@NonNull List<Session2Token> tokens) {} + } + + private static final class SessionCallbackRecord { + public final Executor executor; + public final SessionCallback callback; + + SessionCallbackRecord(Executor executor, SessionCallback callback) { + this.executor = executor; + this.callback = callback; + } + + @Override + public int hashCode() { + return Objects.hash(callback); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof SessionCallbackRecord)) { + return false; + } + return Objects.equals(this.callback, ((SessionCallbackRecord) obj).callback); + } + } + + class MediaCommunicationServiceCallbackStub extends IMediaCommunicationServiceCallback.Stub { + @Override + public void onSession2Created(Session2Token token) throws RemoteException { + for (SessionCallbackRecord record : mTokenCallbackRecords) { + record.executor.execute(() -> record.callback.onSession2TokenCreated(token)); + } + } + + @Override + public void onSession2Changed(MediaParceledListSlice tokens) throws RemoteException { + List<Session2Token> tokenList = tokens.getList(); + for (SessionCallbackRecord record : mTokenCallbackRecords) { + record.executor.execute(() -> record.callback.onSession2TokensChanged(tokenList)); + } + } + } } diff --git a/apex/media/framework/java/android/media/MediaSession2.java b/apex/media/framework/java/android/media/MediaSession2.java index 6560afedab0f..6397ba3996f3 100644 --- a/apex/media/framework/java/android/media/MediaSession2.java +++ b/apex/media/framework/java/android/media/MediaSession2.java @@ -32,7 +32,6 @@ import android.annotation.Nullable; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.media.session.MediaSessionManager; import android.media.session.MediaSessionManager.RemoteUserInfo; import android.os.BadParcelableException; import android.os.Bundle; @@ -87,7 +86,7 @@ public class MediaSession2 implements AutoCloseable { private final String mSessionId; private final PendingIntent mSessionActivity; private final Session2Token mSessionToken; - private final MediaSessionManager mSessionManager; + private final MediaCommunicationManager mCommunicationManager; private final Handler mResultHandler; //@GuardedBy("mLock") @@ -115,8 +114,7 @@ public class MediaSession2 implements AutoCloseable { mSessionStub = new Session2Link(this); mSessionToken = new Session2Token(Process.myUid(), TYPE_SESSION, context.getPackageName(), mSessionStub, tokenExtras); - mSessionManager = (MediaSessionManager) mContext.getSystemService( - Context.MEDIA_SESSION_SERVICE); + mCommunicationManager = mContext.getSystemService(MediaCommunicationManager.class); // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked. mResultHandler = new Handler(context.getMainLooper()); mClosed = false; @@ -352,7 +350,7 @@ public class MediaSession2 implements AutoCloseable { final ControllerInfo controllerInfo = new ControllerInfo( remoteUserInfo, - mSessionManager.isTrustedForMediaControl(remoteUserInfo), + mCommunicationManager.isTrustedForMediaControl(remoteUserInfo), controller, connectionHints); mCallbackExecutor.execute(() -> { @@ -608,8 +606,8 @@ public class MediaSession2 implements AutoCloseable { // Notify framework about the newly create session after the constructor is finished. // Otherwise, framework may access the session before the initialization is finished. try { - MediaSessionManager manager = (MediaSessionManager) mContext.getSystemService( - Context.MEDIA_SESSION_SERVICE); + MediaCommunicationManager manager = + mContext.getSystemService(MediaCommunicationManager.class); manager.notifySession2Created(session2.getToken()); } catch (Exception e) { session2.close(); diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java index c924d9a309d2..ca5aeb88bf33 100644 --- a/apex/media/framework/java/android/media/MediaTranscodeManager.java +++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java @@ -295,8 +295,8 @@ public final class MediaTranscodeManager { /* ignore */ } } - - throw new UnsupportedOperationException("Failed to connect to MediaTranscoding service"); + Log.w(TAG, "Failed to get service"); + return null; } /* @@ -463,8 +463,7 @@ public final class MediaTranscodeManager { } }; - private ITranscodingClient registerClient(IMediaTranscodingService service) - throws UnsupportedOperationException { + private ITranscodingClient registerClient(IMediaTranscodingService service) { synchronized (mLock) { try { // Registers the client with MediaTranscoding service. @@ -476,13 +475,12 @@ public final class MediaTranscodeManager { if (mTranscodingClient != null) { mTranscodingClient.asBinder().linkToDeath(() -> onClientDied(), /* flags */ 0); } - return mTranscodingClient; - } catch (RemoteException re) { - Log.e(TAG, "Failed to register new client due to exception " + re); + } catch (Exception ex) { + Log.e(TAG, "Failed to register new client due to exception " + ex); mTranscodingClient = null; } } - throw new UnsupportedOperationException("Failed to register new client"); + return mTranscodingClient; } /** @@ -495,7 +493,9 @@ public final class MediaTranscodeManager { mUid = Os.getuid(); mPid = Os.getpid(); IMediaTranscodingService service = getService(false /*retry*/); - mTranscodingClient = registerClient(service); + if (service != null) { + mTranscodingClient = registerClient(service); + } } public static final class TranscodingRequest { @@ -1567,6 +1567,10 @@ public final class MediaTranscodeManager { if (mTranscodingClient == null) { // Try to register with the service again. IMediaTranscodingService service = getService(false /*retry*/); + if (service == null) { + throw new MediaTranscodingException.ServiceNotAvailableException( + "Service rebooting. Try again later"); + } mTranscodingClient = registerClient(service); // If still fails, throws an exception to tell client to try later. if (mTranscodingClient == null) { diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp index 5b24cfa4219b..9b3399e8b0e1 100644 --- a/apex/media/service/Android.bp +++ b/apex/media/service/Android.bp @@ -11,6 +11,15 @@ // WITHOUT WARRANTIES 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: "service-media-s-sources", srcs: [ @@ -38,4 +47,3 @@ java_sdk_library { "com.android.media", ], } - diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java index 0468fdf30ba8..06de3f8d27d0 100644 --- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java +++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java @@ -15,27 +15,538 @@ */ package com.android.server.media; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.os.UserHandle.ALL; +import static android.os.UserHandle.getUserHandleForUid; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.NotificationManager; import android.content.Context; +import android.content.pm.PackageManager; import android.media.IMediaCommunicationService; +import android.media.IMediaCommunicationServiceCallback; +import android.media.MediaController2; +import android.media.MediaParceledListSlice; +import android.media.Session2CommandGroup; +import android.media.Session2Token; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Process; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Log; +import android.util.SparseArray; +import android.util.SparseIntArray; +import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; + /** - * A system service that managers {@link android.media.MediaSession2} creations + * A system service that manages {@link android.media.MediaSession2} creations * and their ongoing media playback state. * @hide */ public class MediaCommunicationService extends SystemService { + private static final String TAG = "MediaCommunicationService"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + final Context mContext; + + private final Object mLock = new Object(); + private final Handler mHandler = new Handler(Looper.getMainLooper()); + + @GuardedBy("mLock") + private final SparseIntArray mFullUserIds = new SparseIntArray(); + @GuardedBy("mLock") + private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<>(); + + private final Executor mRecordExecutor = Executors.newSingleThreadExecutor(); + @GuardedBy("mLock") + private final List<CallbackRecord> mCallbackRecords = new ArrayList<>(); + final NotificationManager mNotificationManager; public MediaCommunicationService(Context context) { super(context); + mContext = context; + mNotificationManager = context.getSystemService(NotificationManager.class); } @Override public void onStart() { publishBinderService(Context.MEDIA_COMMUNICATION_SERVICE, new Stub()); + updateUser(); + } + + @Override + public void onUserStarting(@NonNull TargetUser user) { + if (DEBUG) Log.d(TAG, "onUserStarting: " + user); + updateUser(); + } + + @Override + public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { + if (DEBUG) Log.d(TAG, "onUserSwitching: " + to); + updateUser(); + } + + @Override + public void onUserStopped(@NonNull TargetUser targetUser) { + int userId = targetUser.getUserHandle().getIdentifier(); + + if (DEBUG) Log.d(TAG, "onUserStopped: " + userId); + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(userId); + if (user != null) { + if (user.getFullUserId() == userId) { + user.destroySessionsForUserLocked(UserHandle.ALL.getIdentifier()); + mUserRecords.remove(userId); + } else { + user.destroySessionsForUserLocked(userId); + } + } + updateUser(); + } + } + + @Nullable + CallbackRecord findCallbackRecordLocked(@Nullable IMediaCommunicationServiceCallback callback) { + if (callback == null) { + return null; + } + for (CallbackRecord record : mCallbackRecords) { + if (Objects.equals(callback.asBinder(), record.mCallback.asBinder())) { + return record; + } + } + return null; + } + + private FullUserRecord getFullUserRecordLocked(int userId) { + int fullUserId = mFullUserIds.get(userId, -1); + if (fullUserId < 0) { + return null; + } + return mUserRecords.get(fullUserId); + } + + private boolean hasMediaControlPermission(int pid, int uid) { + // Check if it's system server or has MEDIA_CONTENT_CONTROL. + // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra + // check here. + if (uid == Process.SYSTEM_UID || mContext.checkPermission( + android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) + == PackageManager.PERMISSION_GRANTED) { + return true; + } else if (DEBUG) { + Log.d(TAG, "uid(" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL"); + } + return false; + } + + private void updateUser() { + UserManager manager = mContext.getSystemService(UserManager.class); + List<UserHandle> allUsers = manager.getUserHandles(/*excludeDying=*/false); + + synchronized (mLock) { + mFullUserIds.clear(); + if (allUsers != null) { + for (UserHandle user : allUsers) { + UserHandle parent = manager.getProfileParent(user); + if (parent != null) { + mFullUserIds.put(user.getIdentifier(), parent.getIdentifier()); + } else { + mFullUserIds.put(user.getIdentifier(), user.getIdentifier()); + if (mUserRecords.get(user.getIdentifier()) == null) { + mUserRecords.put(user.getIdentifier(), + new FullUserRecord(user.getIdentifier())); + } + } + } + } + // Ensure that the current full user exists. + int currentFullUserId = ActivityManager.getCurrentUser(); + FullUserRecord currentFullUserRecord = mUserRecords.get(currentFullUserId); + if (currentFullUserRecord == null) { + Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId); + currentFullUserRecord = new FullUserRecord(currentFullUserId); + mUserRecords.put(currentFullUserId, currentFullUserRecord); + } + mFullUserIds.put(currentFullUserId, currentFullUserId); + } + } + + void dispatchSessionCreated(Session2Token token) { + for (CallbackRecord record : mCallbackRecords) { + if (record.mUserId != ALL.getIdentifier() + && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) { + continue; + } + try { + record.mCallback.onSession2Created(token); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + + void onSessionDied(Session2Record record) { + synchronized (mLock) { + destroySessionLocked(record); + } + } + + private void destroySessionLocked(Session2Record session) { + if (DEBUG) { + Log.d(TAG, "Destroying " + session); + } + if (session.isClosed()) { + Log.w(TAG, "Destroying already destroyed session. Ignoring."); + return; + } + + FullUserRecord user = getFullUserRecordLocked(session.getUserId()); + + if (user != null) { + user.removeSession(session); + } + + session.close(); } private class Stub extends IMediaCommunicationService.Stub { + @Override + public void notifySession2Created(Session2Token sessionToken) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + + try { + if (DEBUG) { + Log.d(TAG, "Session2 is created " + sessionToken); + } + if (uid != sessionToken.getUid()) { + throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid + + " but actually=" + sessionToken.getUid()); + } + synchronized (mLock) { + int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier(); + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null) { + Log.w(TAG, "notifySession2Created: Ignore session of an unknown user"); + return; + } + user.addSession(new Session2Record(MediaCommunicationService.this, + sessionToken, mRecordExecutor)); + mHandler.post(() -> dispatchSessionCreated(sessionToken)); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL + * permission or an enabled notification listener) + * + * @param controllerPackageName package name of the controller app + * @param controllerPid pid of the controller app + * @param controllerUid uid of the controller app + */ + @Override + public boolean isTrusted(String controllerPackageName, int controllerPid, + int controllerUid) { + final int uid = Binder.getCallingUid(); + final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); + final long token = Binder.clearCallingIdentity(); + try { + // Don't perform check between controllerPackageName and controllerUid. + // When an (activity|service) runs on the another apps process by specifying + // android:process in the AndroidManifest.xml, then PID and UID would have the + // running process' information instead of the (activity|service) that has created + // MediaController. + // Note that we can use Context#getOpPackageName() instead of + // Context#getPackageName() for getting package name that matches with the PID/UID, + // but it doesn't tell which package has created the MediaController, so useless. + return hasMediaControlPermission(controllerPid, controllerUid) + || hasEnabledNotificationListener( + userId, controllerPackageName, controllerUid); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public MediaParceledListSlice getSession2Tokens(int userId) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + + try { + // Check that they can make calls on behalf of the user and get the final user id + int resolvedUserId = handleIncomingUser(pid, uid, userId, null); + List<Session2Token> result; + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(userId); + result = user.getSession2Tokens(resolvedUserId); + } + return new MediaParceledListSlice(result); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void registerCallback(IMediaCommunicationServiceCallback callback, + String packageName) throws RemoteException { + Objects.requireNonNull(callback, "callback should not be null"); + Objects.requireNonNull(packageName, "packageName should not be null"); + + synchronized (mLock) { + if (findCallbackRecordLocked(callback) == null) { + + CallbackRecord record = new CallbackRecord(callback, packageName, + Binder.getCallingUid(), Binder.getCallingPid()); + mCallbackRecords.add(record); + try { + callback.asBinder().linkToDeath(record, 0); + } catch (RemoteException e) { + Log.w(TAG, "Failed to register callback", e); + mCallbackRecords.remove(record); + } + } else { + Log.e(TAG, "registerCallback is called with already registered callback. " + + "packageName=" + packageName); + } + } + } + + @Override + public void unregisterCallback(IMediaCommunicationServiceCallback callback) + throws RemoteException { + synchronized (mLock) { + CallbackRecord existingRecord = findCallbackRecordLocked(callback); + if (existingRecord != null) { + mCallbackRecords.remove(existingRecord); + callback.asBinder().unlinkToDeath(existingRecord, 0); + } else { + Log.e(TAG, "unregisterCallback is called with unregistered callback."); + } + } + } + + private boolean hasEnabledNotificationListener(int callingUserId, + String controllerPackageName, int controllerUid) { + int controllerUserId = UserHandle.getUserHandleForUid(controllerUid).getIdentifier(); + if (callingUserId != controllerUserId) { + // Enabled notification listener only works within the same user. + return false; + } + + if (mNotificationManager.hasEnabledNotificationListener(controllerPackageName, + UserHandle.getUserHandleForUid(controllerUid))) { + return true; + } + if (DEBUG) { + Log.d(TAG, controllerPackageName + " (uid=" + controllerUid + + ") doesn't have an enabled notification listener"); + } + return false; + } + + // Handles incoming user by checking whether the caller has permission to access the + // given user id's information or not. Permission is not necessary if the given user id is + // equal to the caller's user id, but if not, the caller needs to have the + // INTERACT_ACROSS_USERS_FULL permission. Otherwise, a security exception will be thrown. + // The return value will be the given user id, unless the given user id is + // UserHandle.CURRENT, which will return the ActivityManager.getCurrentUser() value instead. + private int handleIncomingUser(int pid, int uid, int userId, String packageName) { + int callingUserId = UserHandle.getUserHandleForUid(uid).getIdentifier(); + if (userId == callingUserId) { + return userId; + } + + boolean canInteractAcrossUsersFull = mContext.checkPermission( + INTERACT_ACROSS_USERS_FULL, pid, uid) == PackageManager.PERMISSION_GRANTED; + if (canInteractAcrossUsersFull) { + if (userId == UserHandle.CURRENT.getIdentifier()) { + return ActivityManager.getCurrentUser(); + } + return userId; + } + + throw new SecurityException("Permission denied while calling from " + packageName + + " with user id: " + userId + "; Need to run as either the calling user id (" + + callingUserId + "), or with " + INTERACT_ACROSS_USERS_FULL + " permission"); + } + } + + final class CallbackRecord implements IBinder.DeathRecipient { + private final IMediaCommunicationServiceCallback mCallback; + private final String mPackageName; + private final int mUid; + private int mPid; + private final int mUserId; + + CallbackRecord(IMediaCommunicationServiceCallback callback, + String packageName, int uid, int pid) { + mCallback = callback; + mPackageName = packageName; + mUid = uid; + mPid = pid; + mUserId = (mContext.checkPermission( + INTERACT_ACROSS_USERS_FULL, pid, uid) == PackageManager.PERMISSION_GRANTED) + ? ALL.getIdentifier() : UserHandle.getUserHandleForUid(mUid).getIdentifier(); + } + + @Override + public String toString() { + return "CallbackRecord[callback=" + mCallback + ", pkg=" + mPackageName + + ", uid=" + mUid + ", pid=" + mPid + "]"; + } + + @Override + public void binderDied() { + synchronized (mLock) { + mCallbackRecords.remove(this); + } + } + } + + final class FullUserRecord { + private final int mFullUserId; + /** Sorted list of media sessions */ + private final List<Session2Record> mSessionRecords = new ArrayList<>(); + + FullUserRecord(int fullUserId) { + mFullUserId = fullUserId; + } + + public void addSession(Session2Record record) { + mSessionRecords.add(record); + } + + public void removeSession(Session2Record record) { + mSessionRecords.remove(record); + //TODO: Handle if the removed session was the media button session. + } + + public int getFullUserId() { + return mFullUserId; + } + + public List<Session2Token> getSession2Tokens(int userId) { + return mSessionRecords.stream() + .filter(record -> record.isActive() + && (userId == UserHandle.ALL.getIdentifier() + || record.getUserId() == userId)) + .map(Session2Record::getSessionToken) + .collect(Collectors.toList()); + } + + public void destroySessionsForUserLocked(int userId) { + synchronized (mLock) { + for (Session2Record record : mSessionRecords) { + if (userId == UserHandle.ALL.getIdentifier() + || record.getUserId() == userId) { + destroySessionLocked(record); + } + } + } + } + } + + static final class Session2Record { + private final Session2Token mSessionToken; + private final Object mLock = new Object(); + private final WeakReference<MediaCommunicationService> mServiceRef; + @GuardedBy("mLock") + private final MediaController2 mController; + + @GuardedBy("mLock") + private boolean mIsConnected; + @GuardedBy("mLock") + private boolean mIsClosed; + + Session2Record(MediaCommunicationService service, Session2Token token, + Executor controllerExecutor) { + mServiceRef = new WeakReference<>(service); + mSessionToken = token; + mController = new MediaController2.Builder(service.getContext(), token) + .setControllerCallback(controllerExecutor, new Controller2Callback()) + .build(); + } + + public int getUserId() { + return UserHandle.getUserHandleForUid(mSessionToken.getUid()).getIdentifier(); + } + + public boolean isActive() { + synchronized (mLock) { + return mIsConnected; + } + } + + public boolean isClosed() { + synchronized (mLock) { + return mIsClosed; + } + } + + public void close() { + synchronized (mLock) { + mIsClosed = true; + // Call close regardless of the mIsConnected. This may be called when it's not yet + // connected. + mController.close(); + } + } + + public Session2Token getSessionToken() { + return mSessionToken; + } + + private class Controller2Callback extends MediaController2.ControllerCallback { + @Override + public void onConnected(MediaController2 controller, + Session2CommandGroup allowedCommands) { + if (DEBUG) { + Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands); + } + synchronized (mLock) { + mIsConnected = true; + } + MediaCommunicationService service = mServiceRef.get(); + if (service != null) { + //TODO: notify session state changed + } + } + + @Override + public void onDisconnected(MediaController2 controller) { + if (DEBUG) { + Log.d(TAG, "disconnected from " + mSessionToken); + } + synchronized (mLock) { + mIsConnected = false; + } + MediaCommunicationService service = mServiceRef.get(); + if (service != null) { + service.onSessionDied(Session2Record.this); + } + } + } } } diff --git a/api/Android.bp b/api/Android.bp index 5466bd2833a9..1d4698e7c512 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -14,6 +14,14 @@ package { default_visibility: ["//visibility:private"], + // 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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], } genrule { @@ -32,6 +40,7 @@ genrule { ":art.module.public.api{.public.api.txt}", ":conscrypt.module.public.api{.public.api.txt}", ":framework-appsearch{.public.api.txt}", + ":framework-connectivity{.public.api.txt}", ":framework-graphics{.public.api.txt}", ":framework-media{.public.api.txt}", ":framework-mediaprovider{.public.api.txt}", @@ -87,6 +96,7 @@ genrule { ":art.module.public.api{.public.stubs.source}", ":conscrypt.module.public.api{.public.stubs.source}", ":framework-appsearch{.public.stubs.source}", + ":framework-connectivity{.public.stubs.source}", ":framework-graphics{.public.stubs.source}", ":framework-media{.public.stubs.source}", ":framework-mediaprovider{.public.stubs.source}", @@ -112,6 +122,7 @@ genrule { ":art.module.public.api{.public.removed-api.txt}", ":conscrypt.module.public.api{.public.removed-api.txt}", ":framework-appsearch{.public.removed-api.txt}", + ":framework-connectivity{.public.removed-api.txt}", ":framework-graphics{.public.removed-api.txt}", ":framework-media{.public.removed-api.txt}", ":framework-mediaprovider{.public.removed-api.txt}", @@ -147,6 +158,7 @@ genrule { srcs: [ ":android.net.ipsec.ike{.system.api.txt}", ":framework-appsearch{.system.api.txt}", + ":framework-connectivity{.system.api.txt}", ":framework-graphics{.system.api.txt}", ":framework-media{.system.api.txt}", ":framework-mediaprovider{.system.api.txt}", @@ -200,6 +212,7 @@ genrule { srcs: [ ":android.net.ipsec.ike{.system.removed-api.txt}", ":framework-appsearch{.system.removed-api.txt}", + ":framework-connectivity{.system.removed-api.txt}", ":framework-graphics{.system.removed-api.txt}", ":framework-media{.system.removed-api.txt}", ":framework-mediaprovider{.system.removed-api.txt}", @@ -235,6 +248,7 @@ genrule { srcs: [ ":android.net.ipsec.ike{.module-lib.api.txt}", ":framework-appsearch{.module-lib.api.txt}", + ":framework-connectivity{.module-lib.api.txt}", ":framework-graphics{.module-lib.api.txt}", ":framework-media{.module-lib.api.txt}", ":framework-mediaprovider{.module-lib.api.txt}", @@ -290,6 +304,7 @@ genrule { srcs: [ ":android.net.ipsec.ike{.module-lib.removed-api.txt}", ":framework-appsearch{.module-lib.removed-api.txt}", + ":framework-connectivity{.module-lib.removed-api.txt}", ":framework-graphics{.module-lib.removed-api.txt}", ":framework-media{.module-lib.removed-api.txt}", ":framework-mediaprovider{.module-lib.removed-api.txt}", diff --git a/boot/Android.bp b/boot/Android.bp index dd4066a7d151..4f7c44eca818 100644 --- a/boot/Android.bp +++ b/boot/Android.bp @@ -12,6 +12,17 @@ // 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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], +} + boot_image { name: "framework-boot-image", image_name: "boot", diff --git a/cmds/abx/Android.bp b/cmds/abx/Android.bp index 333acedfadad..50a0b75b3276 100644 --- a/cmds/abx/Android.bp +++ b/cmds/abx/Android.bp @@ -1,4 +1,21 @@ +package { + default_applicable_licenses: ["frameworks_base_cmds_abx_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_abx_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "abx", wrapper: "abx", diff --git a/cmds/am/Android.bp b/cmds/am/Android.bp index ed73d5596859..7bb9675ede37 100644 --- a/cmds/am/Android.bp +++ b/cmds/am/Android.bp @@ -1,6 +1,23 @@ // Copyright 2008 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_am_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_am_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_library_host_static { name: "libinstrumentation", srcs: ["**/*.proto"], diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp index 14ebb713b6ae..4e5b3bac5713 100644 --- a/cmds/app_process/Android.bp +++ b/cmds/app_process/Android.bp @@ -1,3 +1,20 @@ +package { + default_applicable_licenses: ["frameworks_base_cmds_app_process_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_app_process_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_binary { name: "app_process", diff --git a/cmds/appops/Android.bp b/cmds/appops/Android.bp index 9f330fa3bb51..80f8ec679247 100644 --- a/cmds/appops/Android.bp +++ b/cmds/appops/Android.bp @@ -1,5 +1,22 @@ // Copyright 2014 The Android Open Source Project +package { + default_applicable_licenses: ["frameworks_base_cmds_appops_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_appops_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + sh_binary { name: "appops", src: "appops", diff --git a/cmds/appwidget/Android.bp b/cmds/appwidget/Android.bp index 487d3e1fc25f..8049038de809 100644 --- a/cmds/appwidget/Android.bp +++ b/cmds/appwidget/Android.bp @@ -1,5 +1,22 @@ // Copyright 2014 The Android Open Source Project +package { + default_applicable_licenses: ["frameworks_base_cmds_appwidget_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_appwidget_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "appwidget", wrapper: "appwidget", diff --git a/cmds/backup/Android.bp b/cmds/backup/Android.bp index 287c23b27fbb..5e2a56e6a9cd 100644 --- a/cmds/backup/Android.bp +++ b/cmds/backup/Android.bp @@ -1,5 +1,22 @@ // Copyright 2009 The Android Open Source Project +package { + default_applicable_licenses: ["frameworks_base_cmds_backup_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_backup_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_binary { name: "btool", diff --git a/cmds/bmgr/Android.bp b/cmds/bmgr/Android.bp index b64923bcbe1b..14beb5561dba 100644 --- a/cmds/bmgr/Android.bp +++ b/cmds/bmgr/Android.bp @@ -1,6 +1,23 @@ // Copyright 2007 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_bmgr_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_bmgr_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "bmgr", wrapper: "bmgr", diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 4b7eda096e54..ed717c491467 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -19,7 +19,6 @@ package com.android.commands.bmgr; import android.annotation.IntDef; import android.annotation.UserIdInt; import android.app.backup.BackupManager; -import android.app.backup.BackupManager.OperationType; import android.app.backup.BackupManagerMonitor; import android.app.backup.BackupProgress; import android.app.backup.BackupTransport; @@ -667,7 +666,7 @@ public class Bmgr { // The rest of the 'list' options work with a restore session on the current transport try { - mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP); + mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null); if (mRestore == null) { System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId); return; @@ -822,7 +821,7 @@ public class Bmgr { try { boolean didRestore = false; - mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP); + mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null); if (mRestore == null) { System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId); return; diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp index 0f569971ff62..b2b66c27f795 100644 --- a/cmds/bootanimation/Android.bp +++ b/cmds/bootanimation/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_defaults { name: "bootanimation_defaults", diff --git a/cmds/bu/Android.bp b/cmds/bu/Android.bp index 0866ee053107..5b4ec3197cdd 100644 --- a/cmds/bu/Android.bp +++ b/cmds/bu/Android.bp @@ -1,6 +1,23 @@ // Copyright 2011 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_bu_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_bu_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "bu", wrapper: "bu", diff --git a/cmds/content/Android.bp b/cmds/content/Android.bp index 96d14696d0a2..c70d01ec8f0b 100644 --- a/cmds/content/Android.bp +++ b/cmds/content/Android.bp @@ -1,5 +1,22 @@ // Copyright 2012 The Android Open Source Project +package { + default_applicable_licenses: ["frameworks_base_cmds_content_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_content_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "content", wrapper: "content", diff --git a/cmds/device_config/Android.bp b/cmds/device_config/Android.bp index 67e014a20506..69572d8690c4 100644 --- a/cmds/device_config/Android.bp +++ b/cmds/device_config/Android.bp @@ -1,6 +1,17 @@ // Copyright 2018 The Android Open Source Project // +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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], +} + sh_binary { name: "device_config", src: "device_config", diff --git a/cmds/dpm/Android.bp b/cmds/dpm/Android.bp index 753121e6d886..665abcd7314d 100644 --- a/cmds/dpm/Android.bp +++ b/cmds/dpm/Android.bp @@ -1,6 +1,23 @@ // Copyright 2014 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_dpm_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_dpm_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "dpm", wrapper: "dpm", diff --git a/cmds/hid/Android.bp b/cmds/hid/Android.bp index 54c8bf3ef34c..295c71c65368 100644 --- a/cmds/hid/Android.bp +++ b/cmds/hid/Android.bp @@ -1,6 +1,23 @@ // Copyright 2015 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_hid_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_hid_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "hid", wrapper: "hid", diff --git a/cmds/hid/OWNERS b/cmds/hid/OWNERS new file mode 100644 index 000000000000..d701f23cb9b8 --- /dev/null +++ b/cmds/hid/OWNERS @@ -0,0 +1 @@ +include /core/java/android/hardware/input/OWNERS diff --git a/cmds/hid/jni/Android.bp b/cmds/hid/jni/Android.bp index 2c07de04b6a7..11d92909e44a 100644 --- a/cmds/hid/jni/Android.bp +++ b/cmds/hid/jni/Android.bp @@ -1,3 +1,12 @@ +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_cmds_hid_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_cmds_hid_license"], +} + cc_library_shared { name: "libhidcommand_jni", diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp index 437a87e8df54..2cda57dd67e9 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.cpp +++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp @@ -48,6 +48,7 @@ static const char* UHID_PATH = "/dev/uhid"; static struct { jmethodID onDeviceOpen; jmethodID onDeviceGetReport; + jmethodID onDeviceSetReport; jmethodID onDeviceOutput; jmethodID onDeviceError; } gDeviceCallbackClassInfo; @@ -113,7 +114,16 @@ void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) { checkAndClearException(env, "onDeviceGetReport"); } -void DeviceCallback::onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data) { +void DeviceCallback::onDeviceSetReport(uint8_t rType, + const std::vector<uint8_t>& data) { + JNIEnv* env = getJNIEnv(); + env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceSetReport, rType, + toJbyteArray(env, data).get()); + checkAndClearException(env, "onDeviceSetReport"); +} + +void DeviceCallback::onDeviceOutput(uint8_t rType, + const std::vector<uint8_t>& data) { JNIEnv* env = getJNIEnv(); env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, rType, toJbyteArray(env, data).get()); @@ -261,6 +271,7 @@ int Device::handleEvents(int events) { ALOGD("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id, set_report.rnum, toString(data).c_str()); } + mDeviceCallback->onDeviceSetReport(set_report.rtype, data); break; } case UHID_OUTPUT: { @@ -364,6 +375,8 @@ int register_com_android_commands_hid_Device(JNIEnv* env) { env->GetMethodID(clazz, "onDeviceOpen", "()V"); uhid::gDeviceCallbackClassInfo.onDeviceGetReport = env->GetMethodID(clazz, "onDeviceGetReport", "(II)V"); + uhid::gDeviceCallbackClassInfo.onDeviceSetReport = + env->GetMethodID(clazz, "onDeviceSetReport", "(B[B)V"); uhid::gDeviceCallbackClassInfo.onDeviceOutput = env->GetMethodID(clazz, "onDeviceOutput", "(B[B)V"); uhid::gDeviceCallbackClassInfo.onDeviceError = diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h index 5483b40831a0..d10a9aa3680c 100644 --- a/cmds/hid/jni/com_android_commands_hid_Device.h +++ b/cmds/hid/jni/com_android_commands_hid_Device.h @@ -31,6 +31,7 @@ public: void onDeviceOpen(); void onDeviceGetReport(uint32_t requestId, uint8_t reportId); + void onDeviceSetReport(uint8_t rType, const std::vector<uint8_t>& data); void onDeviceOutput(uint8_t rType, const std::vector<uint8_t>& data); void onDeviceError(); diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java index 20b4bd86baec..95b1e9a7b57f 100644 --- a/cmds/hid/src/com/android/commands/hid/Device.java +++ b/cmds/hid/src/com/android/commands/hid/Device.java @@ -46,7 +46,8 @@ public class Device { // Sync with linux uhid_event_type::UHID_OUTPUT private static final byte UHID_EVENT_TYPE_UHID_OUTPUT = 6; - + // Sync with linux uhid_event_type::UHID_SET_REPORT + private static final byte UHID_EVENT_TYPE_SET_REPORT = 13; private final int mId; private final HandlerThread mThread; private final DeviceHandler mHandler; @@ -198,11 +199,11 @@ public class Device { mHandler.sendMessageAtTime(msg, mTimeToSend); } - // native callback - public void onDeviceOutput(byte rtype, byte[] data) { + // Send out the report to HID command output + private void sendReportOutput(byte eventId, byte rtype, byte[] data) { JSONObject json = new JSONObject(); try { - json.put("eventId", UHID_EVENT_TYPE_UHID_OUTPUT); + json.put("eventId", eventId); json.put("deviceId", mId); json.put("reportType", rtype); JSONArray dataArray = new JSONArray(); @@ -220,6 +221,18 @@ public class Device { throw new RuntimeException(e); } + } + + // native callback + public void onDeviceSetReport(byte rtype, byte[] data) { + // We don't need to reply for the SET_REPORT but just send it to HID output for test + // verification. + sendReportOutput(UHID_EVENT_TYPE_SET_REPORT, rtype, data); + } + + // native callback + public void onDeviceOutput(byte rtype, byte[] data) { + sendReportOutput(UHID_EVENT_TYPE_UHID_OUTPUT, rtype, data); if (mOutputs == null) { Log.e(TAG, "Received OUTPUT request, but 'outputs' section is not found"); return; diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 5d64579f080e..de1bcaea079c 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + cc_defaults { name: "idmap2_defaults", tidy: true, diff --git a/cmds/ime/Android.bp b/cmds/ime/Android.bp index 76a16c81ca38..6dd3ba1ca7ef 100644 --- a/cmds/ime/Android.bp +++ b/cmds/ime/Android.bp @@ -1,6 +1,23 @@ // Copyright 2007 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_ime_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_ime_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + sh_binary { name: "ime", src: "ime", diff --git a/cmds/incident/Android.bp b/cmds/incident/Android.bp index 94855aa0311f..147885f029c2 100644 --- a/cmds/incident/Android.bp +++ b/cmds/incident/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + cc_binary { name: "incident", diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp index f07743ec2ee6..5b819ebc3dfd 100644 --- a/cmds/incident_helper/Android.bp +++ b/cmds/incident_helper/Android.bp @@ -12,6 +12,15 @@ // 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_binary { name: "incident-helper-cmd", wrapper: "incident_helper_cmd", diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp index c47526abad53..b0b23f569664 100644 --- a/cmds/incidentd/Android.bp +++ b/cmds/incidentd/Android.bp @@ -16,6 +16,15 @@ // incidentd // ========= +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"], +} + cc_binary { name: "incidentd", diff --git a/cmds/input/Android.bp b/cmds/input/Android.bp index 1ee9dd355290..2e30176971d7 100644 --- a/cmds/input/Android.bp +++ b/cmds/input/Android.bp @@ -1,6 +1,23 @@ // Copyright 2008 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_input_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_input_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + sh_binary { name: "input", src: "input", diff --git a/cmds/interrupter/Android.bp b/cmds/interrupter/Android.bp index d68e7fe37535..d7f744d0834e 100644 --- a/cmds/interrupter/Android.bp +++ b/cmds/interrupter/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_library_shared { name: "interrupter", host_supported: true, diff --git a/cmds/locksettings/Android.bp b/cmds/locksettings/Android.bp index 59ccc5cd082e..3869c8fcdabc 100644 --- a/cmds/locksettings/Android.bp +++ b/cmds/locksettings/Android.bp @@ -12,6 +12,15 @@ // 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_binary { name: "locksettings", wrapper: "locksettings", diff --git a/cmds/pm/Android.bp b/cmds/pm/Android.bp index 0644f6efcb8a..847dbabdf4c0 100644 --- a/cmds/pm/Android.bp +++ b/cmds/pm/Android.bp @@ -1,6 +1,23 @@ // Copyright 2007 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_pm_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_pm_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + sh_binary { name: "pm", src: "pm", diff --git a/cmds/requestsync/Android.bp b/cmds/requestsync/Android.bp index ef2a8a6be3ca..57e8dd355b26 100644 --- a/cmds/requestsync/Android.bp +++ b/cmds/requestsync/Android.bp @@ -1,6 +1,23 @@ // Copyright 2012 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_requestsync_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_requestsync_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "requestsync", wrapper: "requestsync", diff --git a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java index a0361d0a39d3..ca9df39f83bf 100644 --- a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java +++ b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java @@ -182,6 +182,8 @@ public class RequestSync { mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); } else if (opt.equals("--rc") || opt.equals("--require-charging")) { mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, true); + } else if (opt.equals("--ej") || opt.equals("--schedule-as-ej")) { + mExtras.putBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true); } else if (opt.equals("-e") || opt.equals("--es") || opt.equals("--extra-string")) { final String key = nextArgRequired(); final String value = nextArgRequired(); diff --git a/cmds/screencap/Android.bp b/cmds/screencap/Android.bp index fc628a696afa..c009c1f5b08b 100644 --- a/cmds/screencap/Android.bp +++ b/cmds/screencap/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_binary { name: "screencap", diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index 5c08704a6623..d4da5e554591 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -34,7 +34,6 @@ #include <gui/SurfaceComposerClient.h> #include <gui/SyncScreenCaptureListener.h> -#include <ui/DisplayInfo.h> #include <ui/GraphicTypes.h> #include <ui/PixelFormat.h> diff --git a/cmds/settings/Android.bp b/cmds/settings/Android.bp index 8a78e5420a5f..cc730062f9df 100644 --- a/cmds/settings/Android.bp +++ b/cmds/settings/Android.bp @@ -1,6 +1,17 @@ // Copyright 2011 The Android Open Source Project // +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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], +} + sh_binary { name: "settings", src: "settings", diff --git a/cmds/sm/Android.bp b/cmds/sm/Android.bp index 11e4e72b435b..ecfacaeecfd9 100644 --- a/cmds/sm/Android.bp +++ b/cmds/sm/Android.bp @@ -1,6 +1,23 @@ // Copyright 2015 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_sm_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_sm_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "sm", wrapper: "sm", diff --git a/cmds/svc/Android.bp b/cmds/svc/Android.bp index 68b48f11e5a6..41a3ebd2f514 100644 --- a/cmds/svc/Android.bp +++ b/cmds/svc/Android.bp @@ -1,6 +1,23 @@ // Copyright 2007 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_svc_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_svc_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "svc", wrapper: "svc", diff --git a/cmds/telecom/Android.bp b/cmds/telecom/Android.bp index 56e147c5c8ca..4da79c54477b 100644 --- a/cmds/telecom/Android.bp +++ b/cmds/telecom/Android.bp @@ -1,6 +1,23 @@ // Copyright 2015 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_telecom_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_telecom_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "telecom", wrapper: "telecom", diff --git a/cmds/uiautomator/Android.bp b/cmds/uiautomator/Android.bp index f9cb3dd38897..a0bf624c6455 100644 --- a/cmds/uiautomator/Android.bp +++ b/cmds/uiautomator/Android.bp @@ -1,3 +1,12 @@ +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: "uiautomator-last-released-api", srcs: ["api/*.txt"], diff --git a/cmds/uiautomator/cmds/uiautomator/Android.bp b/cmds/uiautomator/cmds/uiautomator/Android.bp index 68cc5a339107..56e2e70958fa 100644 --- a/cmds/uiautomator/cmds/uiautomator/Android.bp +++ b/cmds/uiautomator/cmds/uiautomator/Android.bp @@ -14,6 +14,15 @@ // 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_binary { name: "uiautomator", wrapper: "uiautomator", diff --git a/cmds/uiautomator/instrumentation/Android.bp b/cmds/uiautomator/instrumentation/Android.bp index 477f0d1f6e10..50e223425f14 100644 --- a/cmds/uiautomator/instrumentation/Android.bp +++ b/cmds/uiautomator/instrumentation/Android.bp @@ -14,6 +14,15 @@ // 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_test { name: "uiautomator-instrumentation", diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp index 14b74da0c616..469b45201337 100644 --- a/cmds/uiautomator/library/Android.bp +++ b/cmds/uiautomator/library/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + droidstubs { name: "uiautomator-stubs", srcs: [ diff --git a/cmds/uinput/Android.bp b/cmds/uinput/Android.bp index 0d7fed2a15c7..260cfc781ebc 100644 --- a/cmds/uinput/Android.bp +++ b/cmds/uinput/Android.bp @@ -1,6 +1,23 @@ // Copyright 2020 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_uinput_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_uinput_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "uinput", wrapper: "uinput", @@ -15,4 +32,4 @@ filegroup { srcs: [ "src/com/android/commands/uinput/InputAbsInfo.aidl", ], -}
\ No newline at end of file +} diff --git a/cmds/uinput/jni/Android.bp b/cmds/uinput/jni/Android.bp index 199bbbd35274..c56adc35b580 100644 --- a/cmds/uinput/jni/Android.bp +++ b/cmds/uinput/jni/Android.bp @@ -1,3 +1,12 @@ +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_cmds_uinput_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_cmds_uinput_license"], +} + cc_library_shared { name: "libuinputcommand_jni", diff --git a/cmds/vr/Android.bp b/cmds/vr/Android.bp index cb129bdf6853..893649155fd8 100644 --- a/cmds/vr/Android.bp +++ b/cmds/vr/Android.bp @@ -1,6 +1,23 @@ // Copyright 2017 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_vr_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_vr_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_binary { name: "vr", wrapper: "vr", diff --git a/cmds/wm/Android.bp b/cmds/wm/Android.bp index 609f84b89bd8..cf6b0193fe87 100644 --- a/cmds/wm/Android.bp +++ b/cmds/wm/Android.bp @@ -1,6 +1,23 @@ // Copyright 2013 The Android Open Source Project // +package { + default_applicable_licenses: ["frameworks_base_cmds_wm_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_cmds_wm_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + sh_binary { name: "wm", src: "wm", diff --git a/config/Android.bp b/config/Android.bp index 8dd409b51eee..6a6f848b7d52 100644 --- a/config/Android.bp +++ b/config/Android.bp @@ -12,6 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_base_config_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_config_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "copyright-header", + ], +} + filegroup { name: "preloaded-classes-denylist", srcs: ["preloaded-classes-denylist"], diff --git a/config/dirty-image-objects b/config/dirty-image-objects index 2dfe0197d898..dfd091cc1aff 100644 --- a/config/dirty-image-objects +++ b/config/dirty-image-objects @@ -28,359 +28,359 @@ # Then, grep for lines containing "Private dirty object" from the output. # This particular file was generated by dumping systemserver and systemui. # -android.accounts.Account -android.accounts.OnAccountsUpdateListener -android.animation.LayoutTransition -android.app.ActivityManager -android.app.ActivityManager$OnUidImportanceListener -android.app.ActivityTaskManager -android.app.ActivityThread -android.app.admin.DevicePolicyManager -android.app.AlarmManager -android.app.Application -android.app.AppOpsManager -android.app.backup.BackupManager -android.app.ContextImpl -android.app.INotificationManager -android.app.Notification$BigPictureStyle -android.app.Notification$BigTextStyle -android.app.Notification$InboxStyle -android.app.NotificationChannel -android.app.NotificationChannelGroup -android.app.NotificationManager -android.app.PendingIntent -android.app.PendingIntent$OnFinished -android.app.QueuedWork -android.app.ResourcesManager -android.app.WallpaperManager -android.app.WindowConfiguration -android.bluetooth.BluetoothAdapter -android.bluetooth.BluetoothDevice -android.bluetooth.BluetoothProfile -android.bluetooth.IBluetoothA2dp -android.bluetooth.IBluetoothHeadsetPhone -android.bluetooth.IBluetoothHidDevice -android.bluetooth.IBluetoothHidHost -android.bluetooth.IBluetoothMap -android.bluetooth.IBluetoothPan -android.bluetooth.IBluetoothPbap -android.bluetooth.IBluetoothSap -android.content.ClipboardManager$OnPrimaryClipChangedListener -android.content.ComponentName -android.content.ContentProvider$PipeDataWriter -android.content.ContentResolver -android.content.Context -android.content.Intent -android.content.pm.PackageManager$OnPermissionsChangedListener -android.content.pm.VersionedPackage -android.content.res.Configuration -android.content.SharedPreferences$OnSharedPreferenceChangeListener -android.database.CursorWindow -android.database.sqlite.SQLiteCompatibilityWalFlags -android.database.sqlite.SQLiteDatabase$CursorFactory -android.database.sqlite.SQLiteGlobal -android.database.sqlite.SQLiteTransactionListener -android.ddm.DdmHandleAppName -android.graphics.Bitmap -android.graphics.Canvas -android.graphics.drawable.AdaptiveIconDrawable -android.graphics.drawable.ColorDrawable -android.graphics.drawable.GradientDrawable -android.graphics.drawable.Icon -android.graphics.drawable.InsetDrawable -android.graphics.drawable.RippleDrawable -android.graphics.drawable.VectorDrawable$VGroup -android.graphics.ImageDecoder -android.graphics.Rect -android.graphics.TemporaryBuffer -android.hardware.biometrics.BiometricSourceType -android.hardware.display.ColorDisplayManager$ColorDisplayManagerInternal -android.hardware.display.DisplayManagerGlobal -android.hardware.display.NightDisplayListener$Callback -android.hardware.input.InputManager -android.hardware.input.InputManager$InputDeviceListener -android.hardware.SensorPrivacyManager -android.hardware.SystemSensorManager -android.icu.impl.OlsonTimeZone -android.icu.text.BreakIterator -android.icu.text.Collator -android.icu.text.DateFormat$BooleanAttribute -android.icu.text.DateTimePatternGenerator$DTPGflags -android.icu.text.PluralRules$Operand -android.icu.util.TimeZone -android.location.GpsStatus$Listener -android.location.LocationListener -android.media.AudioManager -android.media.MediaRouter -android.media.PlayerBase -android.media.session.MediaSessionManager -android.net.apf.ApfCapabilities -android.net.ConnectivityManager -android.net.ConnectivityManager$OnNetworkActiveListener -android.net.ConnectivityThread$Singleton -android.net.IpConfiguration$IpAssignment -android.net.IpConfiguration$ProxySettings -android.net.IpPrefix -android.net.LinkAddress -android.net.LinkProperties -android.net.Network -android.net.NetworkCapabilities -android.net.NetworkInfo -android.net.NetworkInfo$State -android.net.NetworkRequest -android.net.NetworkRequest$Type -android.net.RouteInfo -android.net.StringNetworkSpecifier -android.net.TrafficStats -android.net.UidRange -android.net.Uri$HierarchicalUri -android.net.Uri$StringUri -android.net.wifi.WifiManager -android.net.wifi.WifiManager$SoftApCallback -android.os.AsyncResult -android.os.AsyncTask -android.os.BinderProxy -android.os.Bundle -android.os.DeadObjectException -android.os.Environment -android.os.FileObserver -android.os.Handler -android.os.IDeviceIdleController -android.os.LocaleList -android.os.Looper -android.os.Message -android.os.ParcelUuid -android.os.Process -android.os.RecoverySystem -android.os.ServiceManager -android.os.storage.StorageManager -android.os.StrictMode -android.os.Trace -android.os.WorkSource -android.os.WorkSource$WorkChain -android.permission.PermissionManager -android.provider.FontsContract -android.provider.Settings$SettingNotFoundException -android.renderscript.RenderScriptCacheDir -android.security.IKeyChainService -android.security.keystore.AndroidKeyStoreProvider -android.security.net.config.ApplicationConfig -android.security.net.config.SystemCertificateSource$NoPreloadHolder -android.telecom.PhoneAccountHandle -android.telephony.AnomalyReporter -android.telephony.CellSignalStrengthCdma -android.telephony.CellSignalStrengthGsm -android.telephony.CellSignalStrengthLte -android.telephony.CellSignalStrengthNr -android.telephony.CellSignalStrengthTdscdma -android.telephony.CellSignalStrengthWcdma -android.telephony.DataSpecificRegistrationInfo -android.telephony.emergency.EmergencyNumber -android.telephony.ims.ImsMmTelManager$CapabilityCallback$CapabilityBinder -android.telephony.ims.ImsMmTelManager$RegistrationCallback$RegistrationBinder -android.telephony.ims.ImsReasonInfo -android.telephony.ims.ProvisioningManager$Callback$CallbackBinder -android.telephony.ModemActivityInfo -android.telephony.ModemInfo -android.telephony.NetworkRegistrationInfo -android.telephony.NetworkService -android.telephony.TelephonyManager -android.telephony.VoiceSpecificRegistrationInfo -android.text.format.DateFormat -android.text.method.SingleLineTransformationMethod -android.text.Selection$MemoryTextWatcher -android.text.SpanWatcher -android.text.style.AlignmentSpan -android.text.style.CharacterStyle -android.text.style.LeadingMarginSpan -android.text.style.LineBackgroundSpan -android.text.style.LineHeightSpan -android.text.style.MetricAffectingSpan -android.text.style.ReplacementSpan -android.text.style.SuggestionSpan -android.text.style.TabStopSpan -android.text.TextUtils -android.text.TextWatcher -android.transition.ChangeClipBounds -android.transition.ChangeImageTransform -android.transition.ChangeTransform -android.util.ArrayMap -android.util.ArraySet -android.util.DisplayMetrics -android.util.EventLog -android.util.Log -android.util.Patterns -android.view.AbsSavedState$1 -android.view.accessibility.AccessibilityManager -android.view.accessibility.AccessibilityManager$AccessibilityServicesStateChangeListener -android.view.accessibility.AccessibilityManager$TouchExplorationStateChangeListener -android.view.accessibility.AccessibilityNodeIdManager -android.view.autofill.AutofillManager -android.view.autofill.Helper -android.view.Choreographer -android.view.inputmethod.InputMethodManager -android.view.IWindowManager -android.view.PointerIcon -android.view.RemoteAnimationAdapter -android.view.ThreadedRenderer -android.view.View -android.view.View$OnHoverListener -android.view.ViewRootImpl -android.view.ViewStub -android.view.ViewStub$OnInflateListener -android.view.ViewTreeObserver -android.view.WindowManager$LayoutParams -android.view.WindowManagerGlobal -android.widget.ActionMenuPresenter$OverflowMenuButton -android.widget.ActionMenuView -android.widget.Button -android.widget.CheckBox -android.widget.FrameLayout -android.widget.ImageButton -android.widget.ImageView -android.widget.LinearLayout -android.widget.RelativeLayout -android.widget.SeekBar -android.widget.Space -android.widget.TextView -android.widget.Toolbar -byte[] -com.android.ims.ImsManager -com.android.internal.logging.MetricsLogger -com.android.internal.os.BackgroundThread -com.android.internal.os.BinderInternal -com.android.internal.os.BinderInternal$BinderProxyLimitListener -com.android.internal.os.RuntimeInit -com.android.internal.os.SomeArgs -com.android.internal.policy.DecorView -com.android.internal.statusbar.IStatusBarService -com.android.internal.telephony.AppSmsManager -android.telephony.CallerInfoAsyncQuery$OnQueryCompleteListener -com.android.internal.telephony.CarrierActionAgent -com.android.internal.telephony.cat.CatService -com.android.internal.telephony.cat.IconLoader -com.android.internal.telephony.cat.RilMessageDecoder -com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager -com.android.internal.telephony.cdma.EriManager -com.android.internal.telephony.CellularNetworkValidator -com.android.internal.telephony.CommandException -com.android.internal.telephony.dataconnection.DataConnection$DcActivatingState -com.android.internal.telephony.dataconnection.DataConnection$DcActiveState -com.android.internal.telephony.dataconnection.DataConnection$DcInactiveState -com.android.internal.telephony.dataconnection.DataEnabledSettings -com.android.internal.telephony.dataconnection.DcTracker -com.android.internal.telephony.euicc.EuiccCardController -com.android.internal.telephony.euicc.EuiccController -com.android.internal.telephony.GsmAlphabet -com.android.internal.telephony.GsmCdmaCallTracker -com.android.internal.telephony.GsmCdmaPhone -com.android.internal.telephony.IccPhoneBookInterfaceManager -com.android.internal.telephony.IccSmsInterfaceManager -com.android.internal.telephony.ims.ImsResolver -com.android.internal.telephony.imsphone.ImsExternalCallTracker -com.android.internal.telephony.imsphone.ImsPhone -com.android.internal.telephony.imsphone.ImsPhoneCallTracker -com.android.internal.telephony.ims.RcsMessageStoreController -com.android.internal.telephony.IntentBroadcaster -com.android.internal.telephony.ITelephonyRegistry$Stub$Proxy -com.android.internal.telephony.metrics.TelephonyMetrics -com.android.internal.telephony.MultiSimSettingController -com.android.internal.telephony.nano.CarrierIdProto$CarrierAttribute -com.android.internal.telephony.nano.CarrierIdProto$CarrierId -com.android.internal.telephony.nano.TelephonyProto$RilDataCall -com.android.internal.telephony.nano.TelephonyProto$SmsSession$Event -com.android.internal.telephony.nano.TelephonyProto$TelephonyCallSession$Event$RilCall -com.android.internal.telephony.NitzStateMachine -com.android.internal.telephony.PhoneConfigurationManager -com.android.internal.telephony.PhoneFactory -com.android.internal.telephony.PhoneSwitcher -com.android.internal.telephony.ProxyController -com.android.internal.telephony.RadioConfig -com.android.internal.telephony.RIL -com.android.internal.telephony.RILRequest -com.android.internal.telephony.RilWakelockInfo -com.android.internal.telephony.ServiceStateTracker -com.android.internal.telephony.SimActivationTracker -com.android.internal.telephony.SmsApplication -com.android.internal.telephony.SmsBroadcastUndelivered -com.android.internal.telephony.SmsStorageMonitor -com.android.internal.telephony.SmsUsageMonitor -com.android.internal.telephony.SubscriptionController -com.android.internal.telephony.SubscriptionInfoUpdater -com.android.internal.telephony.TelephonyComponentFactory -com.android.internal.telephony.TelephonyDevController -com.android.internal.telephony.TelephonyTester -com.android.internal.telephony.uicc.AdnRecordCache -com.android.internal.telephony.uicc.UiccCardApplication -com.android.internal.telephony.uicc.UiccController -com.android.internal.telephony.uicc.UiccProfile -com.android.internal.telephony.uicc.UiccStateChangedLauncher -com.android.internal.telephony.uicc.UsimFileHandler -com.android.internal.telephony.uicc.VoiceMailConstants -com.android.internal.util.LatencyTracker -com.android.internal.util.StateMachine$SmHandler -com.android.okhttp.OkHttpClient -com.android.okhttp.okio.AsyncTimeout -com.android.okhttp.okio.SegmentPool -com.android.phone.ecc.nano.ProtobufEccData$CountryInfo -com.android.phone.ecc.nano.ProtobufEccData$EccInfo -com.android.server.sip.SipWakeupTimer -com.android.server.SystemConfig -dalvik.system.BaseDexClassLoader -dalvik.system.BlockGuard -dalvik.system.CloseGuard -dalvik.system.RuntimeHooks -dalvik.system.SocketTagger -java.io.BufferedReader -java.lang.AssertionError -java.lang.Boolean -java.lang.Byte -java.lang.Character -java.lang.CharSequence -java.lang.Class -java.lang.IllegalAccessException -java.lang.IllegalStateException -java.lang.NoSuchMethodException -java.lang.NullPointerException -java.lang.Object -java.lang.Object[] -java.lang.ref.FinalizerReference -java.lang.Runnable -java.lang.SecurityException -java.lang.Short -java.lang.String[] -java.lang.System -java.lang.Thread -java.lang.Throwable -java.lang.UnsatisfiedLinkError -java.net.Inet6Address -java.net.Socket -java.net.SocketException -java.nio.Bits -java.nio.charset.Charset -java.security.interfaces.RSAPrivateKey -java.security.Provider -java.util.Collections -java.util.concurrent.Executor -java.util.GregorianCalendar -java.util.Locale -java.util.Locale$NoImagePreloadHolder -java.util.Scanner -java.util.Set -java.util.TimeZone -javax.net.SocketFactory -javax.net.ssl.HttpsURLConnection -javax.net.ssl.HttpsURLConnection$NoPreloadHolder -javax.net.ssl.SSLContext -javax.net.ssl.SSLSessionContext -javax.net.ssl.SSLSocketFactory -libcore.io.Libcore -libcore.io.Memory -libcore.net.NetworkSecurityPolicy -libcore.timezone.TimeZoneFinder -org.apache.http.params.HttpParams -sun.misc.Cleaner -sun.nio.ch.FileChannelImpl -sun.nio.ch.FileChannelImpl$Unmapper -sun.nio.fs.UnixChannelFactory -sun.security.jca.Providers +Landroid/accounts/Account; +Landroid/accounts/OnAccountsUpdateListener; +Landroid/animation/LayoutTransition; +Landroid/app/ActivityManager; +Landroid/app/ActivityManager$OnUidImportanceListener; +Landroid/app/ActivityTaskManager; +Landroid/app/ActivityThread; +Landroid/app/admin/DevicePolicyManager; +Landroid/app/AlarmManager; +Landroid/app/Application; +Landroid/app/AppOpsManager; +Landroid/app/backup/BackupManager; +Landroid/app/ContextImpl; +Landroid/app/INotificationManager; +Landroid/app/Notification$BigPictureStyle; +Landroid/app/Notification$BigTextStyle; +Landroid/app/Notification$InboxStyle; +Landroid/app/NotificationChannel; +Landroid/app/NotificationChannelGroup; +Landroid/app/NotificationManager; +Landroid/app/PendingIntent; +Landroid/app/PendingIntent$OnFinished; +Landroid/app/QueuedWork; +Landroid/app/ResourcesManager; +Landroid/app/WallpaperManager; +Landroid/app/WindowConfiguration; +Landroid/bluetooth/BluetoothAdapter; +Landroid/bluetooth/BluetoothDevice; +Landroid/bluetooth/BluetoothProfile; +Landroid/bluetooth/IBluetoothA2dp; +Landroid/bluetooth/IBluetoothHeadsetPhone; +Landroid/bluetooth/IBluetoothHidDevice; +Landroid/bluetooth/IBluetoothHidHost; +Landroid/bluetooth/IBluetoothMap; +Landroid/bluetooth/IBluetoothPan; +Landroid/bluetooth/IBluetoothPbap; +Landroid/bluetooth/IBluetoothSap; +Landroid/content/ClipboardManager$OnPrimaryClipChangedListener; +Landroid/content/ComponentName; +Landroid/content/ContentProvider$PipeDataWriter; +Landroid/content/ContentResolver; +Landroid/content/Context; +Landroid/content/Intent; +Landroid/content/pm/PackageManager$OnPermissionsChangedListener; +Landroid/content/pm/VersionedPackage; +Landroid/content/res/Configuration; +Landroid/content/SharedPreferences$OnSharedPreferenceChangeListener; +Landroid/database/CursorWindow; +Landroid/database/sqlite/SQLiteCompatibilityWalFlags; +Landroid/database/sqlite/SQLiteDatabase$CursorFactory; +Landroid/database/sqlite/SQLiteGlobal; +Landroid/database/sqlite/SQLiteTransactionListener; +Landroid/ddm/DdmHandleAppName; +Landroid/graphics/Bitmap; +Landroid/graphics/Canvas; +Landroid/graphics/drawable/AdaptiveIconDrawable; +Landroid/graphics/drawable/ColorDrawable; +Landroid/graphics/drawable/GradientDrawable; +Landroid/graphics/drawable/Icon; +Landroid/graphics/drawable/InsetDrawable; +Landroid/graphics/drawable/RippleDrawable; +Landroid/graphics/drawable/VectorDrawable$VGroup; +Landroid/graphics/ImageDecoder; +Landroid/graphics/Rect; +Landroid/graphics/TemporaryBuffer; +Landroid/hardware/biometrics/BiometricSourceType; +Landroid/hardware/display/ColorDisplayManager$ColorDisplayManagerInternal; +Landroid/hardware/display/DisplayManagerGlobal; +Landroid/hardware/display/NightDisplayListener$Callback; +Landroid/hardware/input/InputManager; +Landroid/hardware/input/InputManager$InputDeviceListener; +Landroid/hardware/SensorPrivacyManager; +Landroid/hardware/SystemSensorManager; +Landroid/icu/impl/OlsonTimeZone; +Landroid/icu/text/BreakIterator; +Landroid/icu/text/Collator; +Landroid/icu/text/DateFormat$BooleanAttribute; +Landroid/icu/text/DateTimePatternGenerator$DTPGflags; +Landroid/icu/text/PluralRules$Operand; +Landroid/icu/util/TimeZone; +Landroid/location/GpsStatus$Listener; +Landroid/location/LocationListener; +Landroid/media/AudioManager; +Landroid/media/MediaRouter; +Landroid/media/PlayerBase; +Landroid/media/session/MediaSessionManager; +Landroid/net/apf/ApfCapabilities; +Landroid/net/ConnectivityManager; +Landroid/net/ConnectivityManager$OnNetworkActiveListener; +Landroid/net/ConnectivityThread$Singleton; +Landroid/net/IpConfiguration$IpAssignment; +Landroid/net/IpConfiguration$ProxySettings; +Landroid/net/IpPrefix; +Landroid/net/LinkAddress; +Landroid/net/LinkProperties; +Landroid/net/Network; +Landroid/net/NetworkCapabilities; +Landroid/net/NetworkInfo; +Landroid/net/NetworkInfo$State; +Landroid/net/NetworkRequest; +Landroid/net/NetworkRequest$Type; +Landroid/net/RouteInfo; +Landroid/net/StringNetworkSpecifier; +Landroid/net/TrafficStats; +Landroid/net/UidRange; +Landroid/net/Uri$HierarchicalUri; +Landroid/net/Uri$StringUri; +Landroid/net/wifi/WifiManager; +Landroid/net/wifi/WifiManager$SoftApCallback; +Landroid/os/AsyncResult; +Landroid/os/AsyncTask; +Landroid/os/BinderProxy; +Landroid/os/Bundle; +Landroid/os/DeadObjectException; +Landroid/os/Environment; +Landroid/os/FileObserver; +Landroid/os/Handler; +Landroid/os/IDeviceIdleController; +Landroid/os/LocaleList; +Landroid/os/Looper; +Landroid/os/Message; +Landroid/os/ParcelUuid; +Landroid/os/Process; +Landroid/os/RecoverySystem; +Landroid/os/ServiceManager; +Landroid/os/storage/StorageManager; +Landroid/os/StrictMode; +Landroid/os/Trace; +Landroid/os/WorkSource; +Landroid/os/WorkSource$WorkChain; +Landroid/permission/PermissionManager; +Landroid/provider/FontsContract; +Landroid/provider/Settings$SettingNotFoundException; +Landroid/renderscript/RenderScriptCacheDir; +Landroid/security/IKeyChainService; +Landroid/security/keystore/AndroidKeyStoreProvider; +Landroid/security/net/config/ApplicationConfig; +Landroid/security/net/config/SystemCertificateSource$NoPreloadHolder; +Landroid/telecom/PhoneAccountHandle; +Landroid/telephony/AnomalyReporter; +Landroid/telephony/CellSignalStrengthCdma; +Landroid/telephony/CellSignalStrengthGsm; +Landroid/telephony/CellSignalStrengthLte; +Landroid/telephony/CellSignalStrengthNr; +Landroid/telephony/CellSignalStrengthTdscdma; +Landroid/telephony/CellSignalStrengthWcdma; +Landroid/telephony/DataSpecificRegistrationInfo; +Landroid/telephony/emergency/EmergencyNumber; +Landroid/telephony/ims/ImsMmTelManager$CapabilityCallback$CapabilityBinder; +Landroid/telephony/ims/ImsMmTelManager$RegistrationCallback$RegistrationBinder; +Landroid/telephony/ims/ImsReasonInfo; +Landroid/telephony/ims/ProvisioningManager$Callback$CallbackBinder; +Landroid/telephony/ModemActivityInfo; +Landroid/telephony/ModemInfo; +Landroid/telephony/NetworkRegistrationInfo; +Landroid/telephony/NetworkService; +Landroid/telephony/TelephonyManager; +Landroid/telephony/VoiceSpecificRegistrationInfo; +Landroid/text/format/DateFormat; +Landroid/text/method/SingleLineTransformationMethod; +Landroid/text/Selection$MemoryTextWatcher; +Landroid/text/SpanWatcher; +Landroid/text/style/AlignmentSpan; +Landroid/text/style/CharacterStyle; +Landroid/text/style/LeadingMarginSpan; +Landroid/text/style/LineBackgroundSpan; +Landroid/text/style/LineHeightSpan; +Landroid/text/style/MetricAffectingSpan; +Landroid/text/style/ReplacementSpan; +Landroid/text/style/SuggestionSpan; +Landroid/text/style/TabStopSpan; +Landroid/text/TextUtils; +Landroid/text/TextWatcher; +Landroid/transition/ChangeClipBounds; +Landroid/transition/ChangeImageTransform; +Landroid/transition/ChangeTransform; +Landroid/util/ArrayMap; +Landroid/util/ArraySet; +Landroid/util/DisplayMetrics; +Landroid/util/EventLog; +Landroid/util/Log; +Landroid/util/Patterns; +Landroid/view/AbsSavedState$1; +Landroid/view/accessibility/AccessibilityManager; +Landroid/view/accessibility/AccessibilityManager$AccessibilityServicesStateChangeListener; +Landroid/view/accessibility/AccessibilityManager$TouchExplorationStateChangeListener; +Landroid/view/accessibility/AccessibilityNodeIdManager; +Landroid/view/autofill/AutofillManager; +Landroid/view/autofill/Helper; +Landroid/view/Choreographer; +Landroid/view/inputmethod/InputMethodManager; +Landroid/view/IWindowManager; +Landroid/view/PointerIcon; +Landroid/view/RemoteAnimationAdapter; +Landroid/view/ThreadedRenderer; +Landroid/view/View; +Landroid/view/View$OnHoverListener; +Landroid/view/ViewRootImpl; +Landroid/view/ViewStub; +Landroid/view/ViewStub$OnInflateListener; +Landroid/view/ViewTreeObserver; +Landroid/view/WindowManager$LayoutParams; +Landroid/view/WindowManagerGlobal; +Landroid/widget/ActionMenuPresenter$OverflowMenuButton; +Landroid/widget/ActionMenuView; +Landroid/widget/Button; +Landroid/widget/CheckBox; +Landroid/widget/FrameLayout; +Landroid/widget/ImageButton; +Landroid/widget/ImageView; +Landroid/widget/LinearLayout; +Landroid/widget/RelativeLayout; +Landroid/widget/SeekBar; +Landroid/widget/Space; +Landroid/widget/TextView; +Landroid/widget/Toolbar; +[B +Lcom/android/ims/ImsManager; +Lcom/android/internal/logging/MetricsLogger; +Lcom/android/internal/os/BackgroundThread; +Lcom/android/internal/os/BinderInternal; +Lcom/android/internal/os/BinderInternal$BinderProxyLimitListener; +Lcom/android/internal/os/RuntimeInit; +Lcom/android/internal/os/SomeArgs; +Lcom/android/internal/policy/DecorView; +Lcom/android/internal/statusbar/IStatusBarService; +Lcom/android/internal/telephony/AppSmsManager; +Landroid/telephony/CallerInfoAsyncQuery$OnQueryCompleteListener; +Lcom/android/internal/telephony/CarrierActionAgent; +Lcom/android/internal/telephony/cat/CatService; +Lcom/android/internal/telephony/cat/IconLoader; +Lcom/android/internal/telephony/cat/RilMessageDecoder; +Lcom/android/internal/telephony/cdma/CdmaSubscriptionSourceManager; +Lcom/android/internal/telephony/cdma/EriManager; +Lcom/android/internal/telephony/CellularNetworkValidator; +Lcom/android/internal/telephony/CommandException; +Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState; +Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState; +Lcom/android/internal/telephony/dataconnection/DataConnection$DcInactiveState; +Lcom/android/internal/telephony/dataconnection/DataEnabledSettings; +Lcom/android/internal/telephony/dataconnection/DcTracker; +Lcom/android/internal/telephony/euicc/EuiccCardController; +Lcom/android/internal/telephony/euicc/EuiccController; +Lcom/android/internal/telephony/GsmAlphabet; +Lcom/android/internal/telephony/GsmCdmaCallTracker; +Lcom/android/internal/telephony/GsmCdmaPhone; +Lcom/android/internal/telephony/IccPhoneBookInterfaceManager; +Lcom/android/internal/telephony/IccSmsInterfaceManager; +Lcom/android/internal/telephony/ims/ImsResolver; +Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker; +Lcom/android/internal/telephony/imsphone/ImsPhone; +Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker; +Lcom/android/internal/telephony/ims/RcsMessageStoreController; +Lcom/android/internal/telephony/IntentBroadcaster; +Lcom/android/internal/telephony/ITelephonyRegistry$Stub$Proxy; +Lcom/android/internal/telephony/metrics/TelephonyMetrics; +Lcom/android/internal/telephony/MultiSimSettingController; +Lcom/android/internal/telephony/nano/CarrierIdProto$CarrierAttribute; +Lcom/android/internal/telephony/nano/CarrierIdProto$CarrierId; +Lcom/android/internal/telephony/nano/TelephonyProto$RilDataCall; +Lcom/android/internal/telephony/nano/TelephonyProto$SmsSession$Event; +Lcom/android/internal/telephony/nano/TelephonyProto$TelephonyCallSession$Event$RilCall; +Lcom/android/internal/telephony/NitzStateMachine; +Lcom/android/internal/telephony/PhoneConfigurationManager; +Lcom/android/internal/telephony/PhoneFactory; +Lcom/android/internal/telephony/PhoneSwitcher; +Lcom/android/internal/telephony/ProxyController; +Lcom/android/internal/telephony/RadioConfig; +Lcom/android/internal/telephony/RIL; +Lcom/android/internal/telephony/RILRequest; +Lcom/android/internal/telephony/RilWakelockInfo; +Lcom/android/internal/telephony/ServiceStateTracker; +Lcom/android/internal/telephony/SimActivationTracker; +Lcom/android/internal/telephony/SmsApplication; +Lcom/android/internal/telephony/SmsBroadcastUndelivered; +Lcom/android/internal/telephony/SmsStorageMonitor; +Lcom/android/internal/telephony/SmsUsageMonitor; +Lcom/android/internal/telephony/SubscriptionController; +Lcom/android/internal/telephony/SubscriptionInfoUpdater; +Lcom/android/internal/telephony/TelephonyComponentFactory; +Lcom/android/internal/telephony/TelephonyDevController; +Lcom/android/internal/telephony/TelephonyTester; +Lcom/android/internal/telephony/uicc/AdnRecordCache; +Lcom/android/internal/telephony/uicc/UiccCardApplication; +Lcom/android/internal/telephony/uicc/UiccController; +Lcom/android/internal/telephony/uicc/UiccProfile; +Lcom/android/internal/telephony/uicc/UiccStateChangedLauncher; +Lcom/android/internal/telephony/uicc/UsimFileHandler; +Lcom/android/internal/telephony/uicc/VoiceMailConstants; +Lcom/android/internal/util/LatencyTracker; +Lcom/android/internal/util/StateMachine$SmHandler; +Lcom/android/okhttp/OkHttpClient; +Lcom/android/okhttp/okio/AsyncTimeout; +Lcom/android/okhttp/okio/SegmentPool; +Lcom/android/phone/ecc/nano/ProtobufEccData$CountryInfo; +Lcom/android/phone/ecc/nano/ProtobufEccData$EccInfo; +Lcom/android/server/sip/SipWakeupTimer; +Lcom/android/server/SystemConfig; +Ldalvik/system/BaseDexClassLoader; +Ldalvik/system/BlockGuard; +Ldalvik/system/CloseGuard; +Ldalvik/system/RuntimeHooks; +Ldalvik/system/SocketTagger; +Ljava/io/BufferedReader; +Ljava/lang/AssertionError; +Ljava/lang/Boolean; +Ljava/lang/Byte; +Ljava/lang/Character; +Ljava/lang/CharSequence; +Ljava/lang/Class; +Ljava/lang/IllegalAccessException; +Ljava/lang/IllegalStateException; +Ljava/lang/NoSuchMethodException; +Ljava/lang/NullPointerException; +Ljava/lang/Object; +[Ljava/lang/Object; +Ljava/lang/ref/FinalizerReference; +Ljava/lang/Runnable; +Ljava/lang/SecurityException; +Ljava/lang/Short; +[Ljava/lang/String; +Ljava/lang/System; +Ljava/lang/Thread; +Ljava/lang/Throwable; +Ljava/lang/UnsatisfiedLinkError; +Ljava/net/Inet6Address; +Ljava/net/Socket; +Ljava/net/SocketException; +Ljava/nio/Bits; +Ljava/nio/charset/Charset; +Ljava/security/interfaces/RSAPrivateKey; +Ljava/security/Provider; +Ljava/util/Collections; +Ljava/util/concurrent/Executor; +Ljava/util/GregorianCalendar; +Ljava/util/Locale; +Ljava/util/Locale$NoImagePreloadHolder; +Ljava/util/Scanner; +Ljava/util/Set; +Ljava/util/TimeZone; +Ljavax/net/SocketFactory; +Ljavax/net/ssl/HttpsURLConnection; +Ljavax/net/ssl/HttpsURLConnection$NoPreloadHolder; +Ljavax/net/ssl/SSLContext; +Ljavax/net/ssl/SSLSessionContext; +Ljavax/net/ssl/SSLSocketFactory; +Llibcore/io/Libcore; +Llibcore/io/Memory; +Llibcore/net/NetworkSecurityPolicy; +Llibcore/timezone/TimeZoneFinder; +Lorg/apache/http/params/HttpParams; +Lsun/misc/Cleaner; +Lsun/nio/ch/FileChannelImpl; +Lsun/nio/ch/FileChannelImpl$Unmapper; +Lsun/nio/fs/UnixChannelFactory; +Lsun/security/jca/Providers; diff --git a/config/preloaded-classes b/config/preloaded-classes index 0c440e810ea8..c6ec37690d88 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -400,6 +400,9 @@ android.app.INotificationManager android.app.IProcessObserver$Stub$Proxy android.app.IProcessObserver$Stub android.app.IProcessObserver +android.app.IRequestFinishCallback$Stub$Proxy +android.app.IRequestFinishCallback$Stub +android.app.IRequestFinishCallback android.app.ISearchManager$Stub$Proxy android.app.ISearchManager$Stub android.app.ISearchManager diff --git a/core/api/Android.bp b/core/api/Android.bp index 00b901992b90..170febb4f766 100644 --- a/core/api/Android.bp +++ b/core/api/Android.bp @@ -14,6 +14,14 @@ package { default_visibility: ["//visibility:private"], + // 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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], } filegroup { diff --git a/core/api/current.txt b/core/api/current.txt index e72f943da520..ea624412f8f7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -21,7 +21,6 @@ package android { field public static final String ACTIVITY_RECOGNITION = "android.permission.ACTIVITY_RECOGNITION"; field public static final String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"; field public static final String ANSWER_PHONE_CALLS = "android.permission.ANSWER_PHONE_CALLS"; - field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA"; field public static final String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; @@ -134,7 +133,6 @@ package android { field public static final String RECEIVE_SMS = "android.permission.RECEIVE_SMS"; field public static final String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH"; field public static final String RECORD_AUDIO = "android.permission.RECORD_AUDIO"; - field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO"; field public static final String REORDER_TASKS = "android.permission.REORDER_TASKS"; field public static final String REQUEST_COMPANION_PROFILE_WATCH = "android.permission.REQUEST_COMPANION_PROFILE_WATCH"; field public static final String REQUEST_COMPANION_RUN_IN_BACKGROUND = "android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND"; @@ -145,6 +143,7 @@ package android { field public static final String REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE = "android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE"; field public static final String REQUEST_PASSWORD_COMPLEXITY = "android.permission.REQUEST_PASSWORD_COMPLEXITY"; field @Deprecated public static final String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES"; + field public static final String SCHEDULE_EXACT_ALARM = "android.permission.SCHEDULE_EXACT_ALARM"; field public static final String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE"; field public static final String SEND_SMS = "android.permission.SEND_SMS"; field public static final String SET_ALARM = "com.android.alarm.permission.SET_ALARM"; @@ -506,6 +505,7 @@ package android { field public static final int dashGap = 16843175; // 0x10101a7 field public static final int dashWidth = 16843174; // 0x10101a6 field public static final int data = 16842798; // 0x101002e + field public static final int dataExtractionRules = 16844350; // 0x101063e field public static final int datePickerDialogTheme = 16843948; // 0x10104ac field public static final int datePickerMode = 16843955; // 0x10104b3 field public static final int datePickerStyle = 16843612; // 0x101035c @@ -527,6 +527,8 @@ package android { field public static final int detailSocialSummary = 16843428; // 0x10102a4 field public static final int detailsElementBackground = 16843598; // 0x101034e field public static final int dial = 16843010; // 0x1010102 + field public static final int dialTint = 16844342; // 0x1010636 + field public static final int dialTintMode = 16844343; // 0x1010637 field public static final int dialogCornerRadius = 16844145; // 0x1010571 field public static final int dialogIcon = 16843252; // 0x10101f4 field public static final int dialogLayout = 16843255; // 0x10101f7 @@ -724,8 +726,14 @@ package android { field public static final int groupIndicator = 16843019; // 0x101010b field public static final int gwpAsanMode = 16844310; // 0x1010616 field public static final int hand_hour = 16843011; // 0x1010103 + field public static final int hand_hourTint = 16844344; // 0x1010638 + field public static final int hand_hourTintMode = 16844345; // 0x1010639 field public static final int hand_minute = 16843012; // 0x1010104 + field public static final int hand_minuteTint = 16844346; // 0x101063a + field public static final int hand_minuteTintMode = 16844347; // 0x101063b field public static final int hand_second = 16844323; // 0x1010623 + field public static final int hand_secondTint = 16844348; // 0x101063c + field public static final int hand_secondTintMode = 16844349; // 0x101063d field public static final int handle = 16843354; // 0x101025a field public static final int handleProfiling = 16842786; // 0x1010022 field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e @@ -4328,16 +4336,17 @@ package android.app { } public class AlarmManager { + method public boolean canScheduleExactAlarms(); method public void cancel(android.app.PendingIntent); method public void cancel(android.app.AlarmManager.OnAlarmListener); method public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock(); method public void set(int, long, android.app.PendingIntent); method public void set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler); - method public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent); + method @RequiresPermission(android.Manifest.permission.SCHEDULE_EXACT_ALARM) public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent); method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent); method public void setExact(int, long, android.app.PendingIntent); method public void setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler); - method public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent); + method @RequiresPermission(value=android.Manifest.permission.SCHEDULE_EXACT_ALARM, conditional=true) public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent); method public void setInexactRepeating(int, long, long, android.app.PendingIntent); method public void setRepeating(int, long, long, android.app.PendingIntent); method @RequiresPermission(android.Manifest.permission.SET_TIME) public void setTime(long); @@ -4733,6 +4742,13 @@ package android.app { field @NonNull public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR; } + public final class BackgroundServiceStartNotAllowedException extends android.app.ServiceStartNotAllowedException implements android.os.Parcelable { + ctor public BackgroundServiceStartNotAllowedException(@NonNull String); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.BackgroundServiceStartNotAllowedException> CREATOR; + } + public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener { ctor public DatePickerDialog(@NonNull android.content.Context); ctor public DatePickerDialog(@NonNull android.content.Context, @StyleRes int); @@ -4978,6 +4994,13 @@ package android.app { method @Deprecated public void setSelectedGroup(int); } + public final class ForegroundServiceStartNotAllowedException extends android.app.ServiceStartNotAllowedException implements android.os.Parcelable { + ctor public ForegroundServiceStartNotAllowedException(@NonNull String); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.ForegroundServiceStartNotAllowedException> CREATOR; + } + @Deprecated public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener { ctor @Deprecated public Fragment(); method @Deprecated public void dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]); @@ -5554,6 +5577,7 @@ package android.app { method public android.graphics.drawable.Icon getSmallIcon(); method public String getSortKey(); method public long getTimeoutAfter(); + method public boolean hasImage(); method public void writeToParcel(android.os.Parcel, int); field public static final android.media.AudioAttributes AUDIO_ATTRIBUTES_DEFAULT; field public static final int BADGE_ICON_LARGE = 2; // 0x2 @@ -5585,6 +5609,7 @@ package android.app { field public static final int DEFAULT_LIGHTS = 4; // 0x4 field public static final int DEFAULT_SOUND = 1; // 0x1 field public static final int DEFAULT_VIBRATE = 2; // 0x2 + field public static final String EXTRA_ANSWER_COLOR = "android.answerColor"; field public static final String EXTRA_ANSWER_INTENT = "android.answerIntent"; field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents"; field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri"; @@ -5596,6 +5621,7 @@ package android.app { field public static final String EXTRA_COLORIZED = "android.colorized"; field public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions"; field public static final String EXTRA_CONVERSATION_TITLE = "android.conversationTitle"; + field public static final String EXTRA_DECLINE_COLOR = "android.declineColor"; field public static final String EXTRA_DECLINE_INTENT = "android.declineIntent"; field public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent"; field public static final String EXTRA_HISTORIC_MESSAGES = "android.messages.historic"; @@ -5883,6 +5909,8 @@ package android.app { method @NonNull public static android.app.Notification.CallStyle forIncomingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent); method @NonNull public static android.app.Notification.CallStyle forOngoingCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent); method @NonNull public static android.app.Notification.CallStyle forScreeningCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent); + method @NonNull public android.app.Notification.CallStyle setAnswerButtonColorHint(@ColorInt int); + method @NonNull public android.app.Notification.CallStyle setDeclineButtonColorHint(@ColorInt int); method @NonNull public android.app.Notification.CallStyle setVerificationIcon(@Nullable android.graphics.drawable.Icon); method @NonNull public android.app.Notification.CallStyle setVerificationText(@Nullable CharSequence); } @@ -6240,14 +6268,14 @@ package android.app { method public static android.app.PendingIntent getActivities(android.content.Context, int, @NonNull android.content.Intent[], int, @Nullable android.os.Bundle); method public static android.app.PendingIntent getActivity(android.content.Context, int, android.content.Intent, int); method public static android.app.PendingIntent getActivity(android.content.Context, int, @NonNull android.content.Intent, int, @Nullable android.os.Bundle); - method public static android.app.PendingIntent getBroadcast(android.content.Context, int, android.content.Intent, int); - method @Nullable public String getCreatorPackage(); + method public static android.app.PendingIntent getBroadcast(android.content.Context, int, @NonNull android.content.Intent, int); + method @NonNull public String getCreatorPackage(); method public int getCreatorUid(); - method @Nullable public android.os.UserHandle getCreatorUserHandle(); + method @NonNull public android.os.UserHandle getCreatorUserHandle(); method public static android.app.PendingIntent getForegroundService(android.content.Context, int, @NonNull android.content.Intent, int); - method public android.content.IntentSender getIntentSender(); + method @NonNull public android.content.IntentSender getIntentSender(); method public static android.app.PendingIntent getService(android.content.Context, int, @NonNull android.content.Intent, int); - method @Deprecated public String getTargetPackage(); + method @Deprecated @NonNull public String getTargetPackage(); method public boolean isActivity(); method public boolean isBroadcast(); method public boolean isForegroundService(); @@ -6560,6 +6588,9 @@ package android.app { field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1 } + public abstract class ServiceStartNotAllowedException extends java.lang.IllegalStateException { + } + public abstract class SharedElementCallback { ctor public SharedElementCallback(); method public android.os.Parcelable onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix, android.graphics.RectF); @@ -6779,14 +6810,18 @@ package android.app { public final class WallpaperColors implements android.os.Parcelable { ctor public WallpaperColors(android.os.Parcel); ctor public WallpaperColors(@NonNull android.graphics.Color, @Nullable android.graphics.Color, @Nullable android.graphics.Color); + ctor public WallpaperColors(@NonNull android.graphics.Color, @Nullable android.graphics.Color, @Nullable android.graphics.Color, int); method public int describeContents(); method public static android.app.WallpaperColors fromBitmap(@NonNull android.graphics.Bitmap); method public static android.app.WallpaperColors fromDrawable(android.graphics.drawable.Drawable); + method public int getColorHints(); method @NonNull public android.graphics.Color getPrimaryColor(); method @Nullable public android.graphics.Color getSecondaryColor(); method @Nullable public android.graphics.Color getTertiaryColor(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR; + field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1 + field public static final int HINT_SUPPORTS_DARK_THEME = 2; // 0x2 } public final class WallpaperInfo implements android.os.Parcelable { @@ -8343,7 +8378,9 @@ package android.appwidget { method protected android.view.View getDefaultView(); method protected android.view.View getErrorView(); method protected void prepareView(android.view.View); + method public void resetColorResources(); method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo); + method public void setColorResources(@NonNull android.util.SparseIntArray); method public void setCurrentSize(@NonNull android.graphics.PointF); method public void setExecutor(java.util.concurrent.Executor); method public void setOnLightBackground(boolean); @@ -8424,7 +8461,7 @@ package android.appwidget { method public int describeContents(); method public final android.os.UserHandle getProfile(); method @NonNull public android.content.pm.ActivityInfo getProviderInfo(); - method @Nullable public final String loadDescription(@NonNull android.content.Context); + method @Nullable public final CharSequence loadDescription(@NonNull android.content.Context); method public final android.graphics.drawable.Drawable loadIcon(@NonNull android.content.Context, int); method public final String loadLabel(android.content.pm.PackageManager); method public final android.graphics.drawable.Drawable loadPreviewImage(@NonNull android.content.Context, int); @@ -8442,7 +8479,7 @@ package android.appwidget { field public static final int WIDGET_FEATURE_RECONFIGURABLE = 1; // 0x1 field public int autoAdvanceViewId; field public android.content.ComponentName configure; - field @IdRes public int descriptionResource; + field @IdRes public int descriptionRes; field public int icon; field public int initialKeyguardLayout; field public int initialLayout; @@ -9706,7 +9743,7 @@ package android.companion { public interface DeviceFilter<D extends android.os.Parcelable> extends android.os.Parcelable { } - public class DeviceNotAssociatedException extends java.lang.Exception { + public class DeviceNotAssociatedException extends java.lang.RuntimeException { } public final class WifiDeviceFilter implements android.companion.DeviceFilter<android.net.wifi.ScanResult> { @@ -10210,6 +10247,7 @@ package android.content { field public static final String SYNC_EXTRAS_MANUAL = "force"; field public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override"; field public static final String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging"; + field public static final String SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB = "schedule_as_expedited_job"; field public static final String SYNC_EXTRAS_UPLOAD = "upload"; field public static final int SYNC_OBSERVER_TYPE_ACTIVE = 4; // 0x4 field public static final int SYNC_OBSERVER_TYPE_PENDING = 2; // 0x2 @@ -10353,7 +10391,7 @@ package android.content { method public abstract void grantUriPermission(String, android.net.Uri, int); method public abstract boolean isDeviceProtectedStorage(); method public boolean isRestricted(); - method public static boolean isUiContext(@NonNull android.content.Context); + method public boolean isUiContext(); method public abstract boolean moveDatabaseFrom(android.content.Context, String); method public abstract boolean moveSharedPreferencesFrom(android.content.Context, String); method @NonNull public final android.content.res.TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[]); @@ -10442,6 +10480,7 @@ package android.content { field public static final int CONTEXT_RESTRICTED = 4; // 0x4 field public static final String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps"; field public static final String DEVICE_POLICY_SERVICE = "device_policy"; + field public static final String DISPLAY_HASH_SERVICE = "display_hash"; field public static final String DISPLAY_SERVICE = "display"; field public static final String DOWNLOAD_SERVICE = "download"; field public static final String DROPBOX_SERVICE = "dropbox"; @@ -11531,6 +11570,7 @@ package android.content { method public android.content.SyncRequest.Builder setManual(boolean); method public android.content.SyncRequest.Builder setNoRetry(boolean); method public android.content.SyncRequest.Builder setRequiresCharging(boolean); + method @NonNull public android.content.SyncRequest.Builder setScheduleAsExpeditedJob(boolean); method public android.content.SyncRequest.Builder setSyncAdapter(android.accounts.Account, String); method public android.content.SyncRequest.Builder syncOnce(); method public android.content.SyncRequest.Builder syncPeriodic(long, long); @@ -11782,6 +11822,8 @@ package android.content.pm { field public int category; field public String className; field public int compatibleWidthLimitDp; + field public int compileSdkVersion; + field @Nullable public String compileSdkVersionCodename; field public String dataDir; field public int descriptionRes; field public String deviceProtectedDataDir; @@ -12523,7 +12565,6 @@ package android.content.pm { field public static final String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct"; field public static final String FEATURE_WIFI_PASSPOINT = "android.hardware.wifi.passpoint"; field public static final String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt"; - field public static final int FLAG_PERMISSION_ALLOWLIST_ROLE = 8; // 0x8 field public static final int FLAG_PERMISSION_WHITELIST_INSTALLER = 2; // 0x2 field public static final int FLAG_PERMISSION_WHITELIST_SYSTEM = 1; // 0x1 field public static final int FLAG_PERMISSION_WHITELIST_UPGRADE = 4; // 0x4 @@ -12662,7 +12703,6 @@ package android.content.pm { field public static final int FLAG_HARD_RESTRICTED = 4; // 0x4 field public static final int FLAG_IMMUTABLY_RESTRICTED = 16; // 0x10 field public static final int FLAG_INSTALLED = 1073741824; // 0x40000000 - field public static final int FLAG_INSTALLER_EXEMPT_IGNORED = 32; // 0x20 field public static final int FLAG_SOFT_RESTRICTED = 8; // 0x8 field public static final int PROTECTION_DANGEROUS = 1; // 0x1 field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40 @@ -12862,6 +12902,7 @@ package android.content.pm { method public void reportShortcutUsed(String); method public boolean requestPinShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.content.IntentSender); method public boolean setDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>); + method public void updateShortcutVisibility(@NonNull String, @Nullable byte[], boolean); method public boolean updateShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>); field public static final int FLAG_MATCH_CACHED = 8; // 0x8 field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2 @@ -14611,11 +14652,6 @@ package android.graphics { enum_constant public static final android.graphics.BlurMaskFilter.Blur SOLID; } - public final class BlurShader extends android.graphics.Shader { - ctor public BlurShader(float, float, @Nullable android.graphics.Shader); - ctor public BlurShader(float, float, @Nullable android.graphics.Shader, @NonNull android.graphics.Shader.TileMode); - } - public class Camera { ctor public Camera(); method public void applyToCanvas(android.graphics.Canvas); @@ -18641,6 +18677,58 @@ package android.hardware.input { } +package android.hardware.lights { + + public final class Light implements android.os.Parcelable { + method public int describeContents(); + method public int getId(); + method @NonNull public String getName(); + method public int getOrdinal(); + method public int getType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR; + field public static final int LIGHT_TYPE_INPUT_PLAYER_ID = 10; // 0xa + field public static final int LIGHT_TYPE_INPUT_RGB = 11; // 0xb + field public static final int LIGHT_TYPE_INPUT_SINGLE = 9; // 0x9 + field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8 + } + + public final class LightState implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public static android.hardware.lights.LightState forColor(@ColorInt int); + method @NonNull public static android.hardware.lights.LightState forPlayerId(int); + method @ColorInt public int getColor(); + method public int getPlayerId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR; + } + + public abstract class LightsManager { + method @NonNull public abstract android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light); + method @NonNull public abstract java.util.List<android.hardware.lights.Light> getLights(); + method @NonNull public abstract android.hardware.lights.LightsManager.LightsSession openSession(); + } + + public abstract static class LightsManager.LightsSession implements java.lang.AutoCloseable { + ctor public LightsManager.LightsSession(); + method public abstract void close(); + method public abstract void requestLights(@NonNull android.hardware.lights.LightsRequest); + } + + public final class LightsRequest { + method @NonNull public java.util.List<android.hardware.lights.LightState> getLightStates(); + method @NonNull public java.util.List<java.lang.Integer> getLights(); + } + + public static final class LightsRequest.Builder { + ctor public LightsRequest.Builder(); + method @NonNull public android.hardware.lights.LightsRequest.Builder addLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState); + method @NonNull public android.hardware.lights.LightsRequest build(); + method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light); + } + +} + package android.hardware.usb { public class UsbAccessory implements android.os.Parcelable { @@ -18809,6 +18897,7 @@ package android.inputmethodservice { public abstract class AbstractInputMethodService extends android.app.Service implements android.view.KeyEvent.Callback { ctor public AbstractInputMethodService(); method public android.view.KeyEvent.DispatcherState getKeyDispatcherState(); + method public final boolean isUiContext(); method public final android.os.IBinder onBind(android.content.Intent); method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface(); method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface(); @@ -19205,8 +19294,8 @@ package android.location { method @NonNull public android.location.GnssAntennaInfo.Builder setSignalGainCorrections(@Nullable android.location.GnssAntennaInfo.SphericalCorrections); } - @Deprecated public static interface GnssAntennaInfo.Listener { - method @Deprecated public void onGnssAntennaInfoReceived(@NonNull java.util.List<android.location.GnssAntennaInfo>); + public static interface GnssAntennaInfo.Listener { + method public void onGnssAntennaInfoReceived(@NonNull java.util.List<android.location.GnssAntennaInfo>); } public static final class GnssAntennaInfo.PhaseCenterOffset implements android.os.Parcelable { @@ -19574,6 +19663,7 @@ package android.location { method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void addProximityAlert(double, double, float, long, @NonNull android.app.PendingIntent); method public void addTestProvider(@NonNull String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int); method public void addTestProvider(@NonNull String, @NonNull android.location.provider.ProviderProperties); + method public void addTestProvider(@NonNull String, @NonNull android.location.provider.ProviderProperties, @NonNull java.util.Set<java.lang.String>); method @Deprecated public void clearTestProviderEnabled(@NonNull String); method @Deprecated public void clearTestProviderLocation(@NonNull String); method @Deprecated public void clearTestProviderStatus(@NonNull String); @@ -19594,7 +19684,7 @@ package android.location { method public boolean hasProvider(@NonNull String); method public boolean isLocationEnabled(); method public boolean isProviderEnabled(@NonNull String); - method @Deprecated public boolean registerAntennaInfoListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssAntennaInfo.Listener); + method public boolean registerAntennaInfoListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssAntennaInfo.Listener); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback, @Nullable android.os.Handler); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback); @@ -19631,13 +19721,11 @@ package android.location { method public void setTestProviderEnabled(@NonNull String, boolean); method public void setTestProviderLocation(@NonNull String, @NonNull android.location.Location); method @Deprecated public void setTestProviderStatus(@NonNull String, int, @Nullable android.os.Bundle, long); - method @Deprecated public void unregisterAntennaInfoListener(@NonNull android.location.GnssAntennaInfo.Listener); + method public void unregisterAntennaInfoListener(@NonNull android.location.GnssAntennaInfo.Listener); method public void unregisterGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback); method public void unregisterGnssNavigationMessageCallback(@NonNull android.location.GnssNavigationMessage.Callback); method public void unregisterGnssStatusCallback(@NonNull android.location.GnssStatus.Callback); - field public static final String ACTION_GNSS_ANTENNA_INFOS_CHANGED = "android.location.action.GNSS_ANTENNA_INFOS_CHANGED"; field public static final String ACTION_GNSS_CAPABILITIES_CHANGED = "android.location.action.GNSS_CAPABILITIES_CHANGED"; - field public static final String EXTRA_GNSS_ANTENNA_INFOS = "android.location.extra.GNSS_ANTENNA_INFOS"; field public static final String EXTRA_GNSS_CAPABILITIES = "android.location.extra.GNSS_CAPABILITIES"; field public static final String EXTRA_LOCATION_ENABLED = "android.location.extra.LOCATION_ENABLED"; field public static final String EXTRA_PROVIDER_ENABLED = "android.location.extra.PROVIDER_ENABLED"; @@ -19780,7 +19868,6 @@ package android.media { method public int getFlags(); method public int getUsage(); method public int getVolumeControlStream(); - method @NonNull public static String usageToString(int); method public void writeToParcel(android.os.Parcel, int); field public static final int ALLOW_CAPTURE_BY_ALL = 1; // 0x1 field public static final int ALLOW_CAPTURE_BY_NONE = 3; // 0x3 @@ -21115,15 +21202,15 @@ package android.media { public static final class MediaCodec.CryptoException extends java.lang.RuntimeException { ctor public MediaCodec.CryptoException(int, @Nullable String); method public int getErrorCode(); - field public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8 - field public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4 - field public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7 - field public static final int ERROR_KEY_EXPIRED = 2; // 0x2 - field public static final int ERROR_LOST_STATE = 9; // 0x9 - field public static final int ERROR_NO_KEY = 1; // 0x1 - field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3 - field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5 - field public static final int ERROR_UNSUPPORTED_OPERATION = 6; // 0x6 + field @Deprecated public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8 + field @Deprecated public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4 + field @Deprecated public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7 + field @Deprecated public static final int ERROR_KEY_EXPIRED = 2; // 0x2 + field @Deprecated public static final int ERROR_LOST_STATE = 9; // 0x9 + field @Deprecated public static final int ERROR_NO_KEY = 1; // 0x1 + field @Deprecated public static final int ERROR_RESOURCE_BUSY = 3; // 0x3 + field @Deprecated public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5 + field @Deprecated public static final int ERROR_UNSUPPORTED_OPERATION = 6; // 0x6 } public static final class MediaCodec.CryptoInfo { @@ -21213,8 +21300,8 @@ package android.media { public static final class MediaCodecInfo.AudioCapabilities { method public android.util.Range<java.lang.Integer> getBitrateRange(); method @NonNull public android.util.Range<java.lang.Integer>[] getInputChannelCountRanges(); - method public int getMaxInputChannelCount(); - method public int getMinInputChannelCount(); + method @IntRange(from=1, to=255) public int getMaxInputChannelCount(); + method @IntRange(from=1, to=255) public int getMinInputChannelCount(); method public android.util.Range<java.lang.Integer>[] getSupportedSampleRateRanges(); method public int[] getSupportedSampleRates(); method public boolean isSampleRateSupported(int); @@ -21742,6 +21829,42 @@ package android.media { method public boolean verify(@NonNull byte[], @NonNull byte[], @NonNull byte[]); } + public static final class MediaDrm.ErrorCodes { + field public static final int ERROR_CERTIFICATE_MALFORMED = 10; // 0xa + field public static final int ERROR_CERTIFICATE_MISSING = 11; // 0xb + field public static final int ERROR_CRYPTO_LIBRARY = 12; // 0xc + field public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8 + field public static final int ERROR_GENERIC_OEM = 13; // 0xd + field public static final int ERROR_GENERIC_PLUGIN = 14; // 0xe + field public static final int ERROR_INIT_DATA = 15; // 0xf + field public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4 + field public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7 + field public static final int ERROR_KEY_EXPIRED = 2; // 0x2 + field public static final int ERROR_KEY_NOT_LOADED = 16; // 0x10 + field public static final int ERROR_LICENSE_PARSE = 17; // 0x11 + field public static final int ERROR_LICENSE_POLICY = 18; // 0x12 + field public static final int ERROR_LICENSE_RELEASE = 19; // 0x13 + field public static final int ERROR_LICENSE_REQUEST_REJECTED = 20; // 0x14 + field public static final int ERROR_LICENSE_RESTORE = 21; // 0x15 + field public static final int ERROR_LICENSE_STATE = 22; // 0x16 + field public static final int ERROR_LOST_STATE = 9; // 0x9 + field public static final int ERROR_MEDIA_FRAMEWORK = 23; // 0x17 + field public static final int ERROR_NO_KEY = 1; // 0x1 + field public static final int ERROR_PROVISIONING_CERTIFICATE = 24; // 0x18 + field public static final int ERROR_PROVISIONING_CONFIG = 25; // 0x19 + field public static final int ERROR_PROVISIONING_PARSE = 26; // 0x1a + field public static final int ERROR_PROVISIONING_RETRY = 27; // 0x1b + field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3 + field public static final int ERROR_RESOURCE_CONTENTION = 28; // 0x1c + field public static final int ERROR_SECURE_STOP_RELEASE = 29; // 0x1d + field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5 + field public static final int ERROR_STORAGE_READ = 30; // 0x1e + field public static final int ERROR_STORAGE_WRITE = 31; // 0x1f + field public static final int ERROR_UNKNOWN = 0; // 0x0 + field public static final int ERROR_UNSUPPORTED_OPERATION = 6; // 0x6 + field public static final int ERROR_ZERO_SUBSAMPLES = 32; // 0x20 + } + @Deprecated @IntDef({android.media.MediaDrm.HDCP_LEVEL_UNKNOWN, android.media.MediaDrm.HDCP_NONE, android.media.MediaDrm.HDCP_V1, android.media.MediaDrm.HDCP_V2, android.media.MediaDrm.HDCP_V2_1, android.media.MediaDrm.HDCP_V2_2, android.media.MediaDrm.HDCP_V2_3, android.media.MediaDrm.HDCP_NO_DIGITAL_OUTPUT}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MediaDrm.HdcpLevel { } @@ -21775,6 +21898,8 @@ package android.media { public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException { method @NonNull public String getDiagnosticInfo(); + method public int getErrorCode(); + method public boolean isTransient(); } public static final class MediaDrm.MetricsConstants { @@ -21841,9 +21966,10 @@ package android.media { public static final class MediaDrm.SessionException extends java.lang.RuntimeException { ctor public MediaDrm.SessionException(int, @Nullable String); - method public int getErrorCode(); - field public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1 - field public static final int ERROR_UNKNOWN = 0; // 0x0 + method @Deprecated public int getErrorCode(); + method public boolean isTransient(); + field @Deprecated public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1 + field @Deprecated public static final int ERROR_UNKNOWN = 0; // 0x0 } public class MediaDrmException extends java.lang.Exception { @@ -22023,6 +22149,14 @@ package android.media { field public static final String KEY_TILE_HEIGHT = "tile-height"; field public static final String KEY_TILE_WIDTH = "tile-width"; field public static final String KEY_TRACK_ID = "track-id"; + field public static final String KEY_VIDEO_QP_B_MAX = "video-qp-b-max"; + field public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min"; + field public static final String KEY_VIDEO_QP_I_MAX = "video-qp-i-max"; + field public static final String KEY_VIDEO_QP_I_MIN = "video-qp-i-min"; + field public static final String KEY_VIDEO_QP_MAX = "video-qp-max"; + field public static final String KEY_VIDEO_QP_MIN = "video-qp-min"; + field public static final String KEY_VIDEO_QP_P_MAX = "video-qp-p-max"; + field public static final String KEY_VIDEO_QP_P_MIN = "video-qp-p-min"; field public static final String KEY_WIDTH = "width"; field public static final String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; field public static final String MIMETYPE_AUDIO_AC3 = "audio/ac3"; @@ -24624,7 +24758,7 @@ package android.media.session { method @NonNull public java.util.List<android.media.session.MediaController> getActiveSessions(@Nullable android.content.ComponentName); method @NonNull public java.util.List<android.media.Session2Token> getSession2Tokens(); method public boolean isTrustedForMediaControl(@NonNull android.media.session.MediaSessionManager.RemoteUserInfo); - method public void notifySession2Created(@NonNull android.media.Session2Token); + method @Deprecated public void notifySession2Created(@NonNull android.media.Session2Token); method public void removeOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener); method public void removeOnSession2TokensChangedListener(@NonNull android.media.session.MediaSessionManager.OnSession2TokensChangedListener); } @@ -25691,161 +25825,6 @@ package android.mtp { package android.net { - public class CaptivePortal implements android.os.Parcelable { - method public int describeContents(); - method public void ignoreNetwork(); - method public void reportCaptivePortalDismissed(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR; - } - - public class ConnectivityDiagnosticsManager { - method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback); - method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback); - } - - public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback { - ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback(); - method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport); - method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport); - method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean); - } - - public static final class ConnectivityDiagnosticsManager.ConnectivityReport implements android.os.Parcelable { - ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle); - method public int describeContents(); - method @NonNull public android.os.PersistableBundle getAdditionalInfo(); - method @NonNull public android.net.LinkProperties getLinkProperties(); - method @NonNull public android.net.Network getNetwork(); - method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); - method public long getReportTimestamp(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR; - field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted"; - field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded"; - field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult"; - field public static final int NETWORK_PROBE_DNS = 4; // 0x4 - field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20 - field public static final int NETWORK_PROBE_HTTP = 8; // 0x8 - field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10 - field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40 - field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0 - field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2 - field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3 - field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1 - } - - public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable { - ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle); - method public int describeContents(); - method public int getDetectionMethod(); - method @NonNull public android.net.LinkProperties getLinkProperties(); - method @NonNull public android.net.Network getNetwork(); - method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); - method public long getReportTimestamp(); - method @NonNull public android.os.PersistableBundle getStallDetails(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR; - field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1 - field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2 - field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts"; - field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis"; - field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate"; - } - - public class ConnectivityManager { - method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); - method public boolean bindProcessToNetwork(@Nullable android.net.Network); - method @NonNull public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); - method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork(); - method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo(); - method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo(); - method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks(); - method @Deprecated public boolean getBackgroundDataSetting(); - method @Nullable public android.net.Network getBoundNetworkForProcess(); - method public int getConnectionOwnerUid(int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress); - method @Nullable public android.net.ProxyInfo getDefaultProxy(); - method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.LinkProperties getLinkProperties(@Nullable android.net.Network); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getMultipathPreference(@Nullable android.net.Network); - method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkCapabilities getNetworkCapabilities(@Nullable android.net.Network); - method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(int); - method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(@Nullable android.net.Network); - method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getNetworkPreference(); - method @Nullable public byte[] getNetworkWatchlistConfigHash(); - method @Deprecated @Nullable public static android.net.Network getProcessDefaultNetwork(); - method public int getRestrictBackgroundStatus(); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered(); - method public boolean isDefaultNetworkActive(); - method @Deprecated public static boolean isNetworkTypeValid(int); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent); - method public void releaseNetworkRequest(@NonNull android.app.PendingIntent); - method public void removeDefaultNetworkActiveListener(@NonNull android.net.ConnectivityManager.OnNetworkActiveListener); - method @Deprecated public void reportBadNetwork(@Nullable android.net.Network); - method public void reportNetworkConnectivity(@Nullable android.net.Network, boolean); - method public boolean requestBandwidthUpdate(@NonNull android.net.Network); - method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback); - method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); - method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int); - method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler, int); - method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent); - method @Deprecated public void setNetworkPreference(int); - method @Deprecated public static boolean setProcessDefaultNetwork(@Nullable android.net.Network); - method public void unregisterNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback); - method public void unregisterNetworkCallback(@NonNull android.app.PendingIntent); - field @Deprecated public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; - field public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL"; - field public static final String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED"; - field @Deprecated public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; - field @Deprecated public static final int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1 - field public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL"; - field public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL"; - field @Deprecated public static final String EXTRA_EXTRA_INFO = "extraInfo"; - field @Deprecated public static final String EXTRA_IS_FAILOVER = "isFailover"; - field public static final String EXTRA_NETWORK = "android.net.extra.NETWORK"; - field @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo"; - field public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST"; - field @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType"; - field public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity"; - field @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork"; - field public static final String EXTRA_REASON = "reason"; - field public static final int MULTIPATH_PREFERENCE_HANDOVER = 1; // 0x1 - field public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 4; // 0x4 - field public static final int MULTIPATH_PREFERENCE_RELIABILITY = 2; // 0x2 - field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1 - field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3 - field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2 - field @Deprecated public static final int TYPE_BLUETOOTH = 7; // 0x7 - field @Deprecated public static final int TYPE_DUMMY = 8; // 0x8 - field @Deprecated public static final int TYPE_ETHERNET = 9; // 0x9 - field @Deprecated public static final int TYPE_MOBILE = 0; // 0x0 - field @Deprecated public static final int TYPE_MOBILE_DUN = 4; // 0x4 - field @Deprecated public static final int TYPE_MOBILE_HIPRI = 5; // 0x5 - field @Deprecated public static final int TYPE_MOBILE_MMS = 2; // 0x2 - field @Deprecated public static final int TYPE_MOBILE_SUPL = 3; // 0x3 - field @Deprecated public static final int TYPE_VPN = 17; // 0x11 - field @Deprecated public static final int TYPE_WIFI = 1; // 0x1 - field @Deprecated public static final int TYPE_WIMAX = 6; // 0x6 - } - - public static class ConnectivityManager.NetworkCallback { - ctor public ConnectivityManager.NetworkCallback(); - method public void onAvailable(@NonNull android.net.Network); - method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean); - method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities); - method public void onLinkPropertiesChanged(@NonNull android.net.Network, @NonNull android.net.LinkProperties); - method public void onLosing(@NonNull android.net.Network, int); - method public void onLost(@NonNull android.net.Network); - method public void onUnavailable(); - } - - public static interface ConnectivityManager.OnNetworkActiveListener { - method public void onNetworkActive(); - } - public class Credentials { ctor public Credentials(int, int, int); method public int getGid(); @@ -25853,46 +25832,6 @@ package android.net { method public int getUid(); } - public class DhcpInfo implements android.os.Parcelable { - ctor public DhcpInfo(); - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpInfo> CREATOR; - field public int dns1; - field public int dns2; - field public int gateway; - field public int ipAddress; - field public int leaseDuration; - field public int netmask; - field public int serverAddress; - } - - public final class DnsResolver { - method @NonNull public static android.net.DnsResolver getInstance(); - method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>); - method public void query(@Nullable android.net.Network, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>); - method public void rawQuery(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>); - method public void rawQuery(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>); - field public static final int CLASS_IN = 1; // 0x1 - field public static final int ERROR_PARSE = 0; // 0x0 - field public static final int ERROR_SYSTEM = 1; // 0x1 - field public static final int FLAG_EMPTY = 0; // 0x0 - field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4 - field public static final int FLAG_NO_CACHE_STORE = 2; // 0x2 - field public static final int FLAG_NO_RETRY = 1; // 0x1 - field public static final int TYPE_A = 1; // 0x1 - field public static final int TYPE_AAAA = 28; // 0x1c - } - - public static interface DnsResolver.Callback<T> { - method public void onAnswer(@NonNull T, int); - method public void onError(@NonNull android.net.DnsResolver.DnsException); - } - - public static class DnsResolver.DnsException extends java.lang.Exception { - field public final int code; - } - public final class Ikev2VpnProfile extends android.net.PlatformVpnProfile { method @NonNull public java.util.List<java.lang.String> getAllowedAlgorithms(); method public int getMaxMtu(); @@ -25922,21 +25861,6 @@ package android.net { method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo); } - public class InetAddresses { - method public static boolean isNumericAddress(@NonNull String); - method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String); - } - - public final class IpPrefix implements android.os.Parcelable { - method public boolean contains(@NonNull java.net.InetAddress); - method public int describeContents(); - method @NonNull public java.net.InetAddress getAddress(); - method @IntRange(from=0, to=128) public int getPrefixLength(); - method @NonNull public byte[] getRawAddress(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR; - } - public final class IpSecAlgorithm implements android.os.Parcelable { ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[]); ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[], int); @@ -26005,45 +25929,6 @@ package android.net { method @NonNull public android.net.IpSecTransform.Builder setIpv4Encapsulation(@NonNull android.net.IpSecManager.UdpEncapsulationSocket, int); } - public class LinkAddress implements android.os.Parcelable { - method public int describeContents(); - method public java.net.InetAddress getAddress(); - method public int getFlags(); - method @IntRange(from=0, to=128) public int getPrefixLength(); - method public int getScope(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkAddress> CREATOR; - } - - public final class LinkProperties implements android.os.Parcelable { - ctor public LinkProperties(); - method public boolean addRoute(@NonNull android.net.RouteInfo); - method public void clear(); - method public int describeContents(); - method @Nullable public java.net.Inet4Address getDhcpServerAddress(); - method @NonNull public java.util.List<java.net.InetAddress> getDnsServers(); - method @Nullable public String getDomains(); - method @Nullable public android.net.ProxyInfo getHttpProxy(); - method @Nullable public String getInterfaceName(); - method @NonNull public java.util.List<android.net.LinkAddress> getLinkAddresses(); - method public int getMtu(); - method @Nullable public android.net.IpPrefix getNat64Prefix(); - method @Nullable public String getPrivateDnsServerName(); - method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(); - method public boolean isPrivateDnsActive(); - method public boolean isWakeOnLanSupported(); - method public void setDhcpServerAddress(@Nullable java.net.Inet4Address); - method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>); - method public void setDomains(@Nullable String); - method public void setHttpProxy(@Nullable android.net.ProxyInfo); - method public void setInterfaceName(@Nullable String); - method public void setLinkAddresses(@NonNull java.util.Collection<android.net.LinkAddress>); - method public void setMtu(int); - method public void setNat64Prefix(@Nullable android.net.IpPrefix); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR; - } - public class LocalServerSocket implements java.io.Closeable { ctor public LocalServerSocket(String) throws java.io.IOException; ctor public LocalServerSocket(java.io.FileDescriptor) throws java.io.IOException; @@ -26099,24 +25984,6 @@ package android.net { enum_constant public static final android.net.LocalSocketAddress.Namespace RESERVED; } - public final class MacAddress implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public static android.net.MacAddress fromBytes(@NonNull byte[]); - method @NonNull public static android.net.MacAddress fromString(@NonNull String); - method public int getAddressType(); - method @Nullable public java.net.Inet6Address getLinkLocalIpv6FromEui48Mac(); - method public boolean isLocallyAssigned(); - method public boolean matches(@NonNull android.net.MacAddress, @NonNull android.net.MacAddress); - method @NonNull public byte[] toByteArray(); - method @NonNull public String toOuiString(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.net.MacAddress BROADCAST_ADDRESS; - field @NonNull public static final android.os.Parcelable.Creator<android.net.MacAddress> CREATOR; - field public static final int TYPE_BROADCAST = 3; // 0x3 - field public static final int TYPE_MULTICAST = 2; // 0x2 - field public static final int TYPE_UNICAST = 1; // 0x1 - } - public class MailTo { method public String getBody(); method public String getCc(); @@ -26128,138 +25995,6 @@ package android.net { field public static final String MAILTO_SCHEME = "mailto:"; } - public class Network implements android.os.Parcelable { - method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException; - method public void bindSocket(java.net.Socket) throws java.io.IOException; - method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException; - method public int describeContents(); - method public static android.net.Network fromNetworkHandle(long); - method public java.net.InetAddress[] getAllByName(String) throws java.net.UnknownHostException; - method public java.net.InetAddress getByName(String) throws java.net.UnknownHostException; - method public long getNetworkHandle(); - method public javax.net.SocketFactory getSocketFactory(); - method public java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException; - method public java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException; - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.Network> CREATOR; - } - - public final class NetworkCapabilities implements android.os.Parcelable { - ctor public NetworkCapabilities(); - ctor public NetworkCapabilities(android.net.NetworkCapabilities); - method public int describeContents(); - method public int getLinkDownstreamBandwidthKbps(); - method public int getLinkUpstreamBandwidthKbps(); - method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier(); - method public int getOwnerUid(); - method public int getSignalStrength(); - method @Nullable public android.net.TransportInfo getTransportInfo(); - method public boolean hasCapability(int); - method public boolean hasTransport(int); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR; - field public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; // 0x11 - field public static final int NET_CAPABILITY_CBS = 5; // 0x5 - field public static final int NET_CAPABILITY_DUN = 2; // 0x2 - field public static final int NET_CAPABILITY_EIMS = 10; // 0xa - field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13 - field public static final int NET_CAPABILITY_FOTA = 3; // 0x3 - field public static final int NET_CAPABILITY_IA = 7; // 0x7 - field public static final int NET_CAPABILITY_IMS = 4; // 0x4 - field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc - field public static final int NET_CAPABILITY_MCX = 23; // 0x17 - field public static final int NET_CAPABILITY_MMS = 0; // 0x0 - field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14 - field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb - field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd - field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12 - field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15 - field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf - field public static final int NET_CAPABILITY_RCS = 8; // 0x8 - field public static final int NET_CAPABILITY_SUPL = 1; // 0x1 - field public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; // 0x19 - field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe - field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10 - field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6 - field public static final int NET_CAPABILITY_XCAP = 9; // 0x9 - field public static final int SIGNAL_STRENGTH_UNSPECIFIED = -2147483648; // 0x80000000 - field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2 - field public static final int TRANSPORT_CELLULAR = 0; // 0x0 - field public static final int TRANSPORT_ETHERNET = 3; // 0x3 - field public static final int TRANSPORT_LOWPAN = 6; // 0x6 - field public static final int TRANSPORT_VPN = 4; // 0x4 - field public static final int TRANSPORT_WIFI = 1; // 0x1 - field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5 - } - - @Deprecated public class NetworkInfo implements android.os.Parcelable { - ctor @Deprecated public NetworkInfo(int, int, @Nullable String, @Nullable String); - method @Deprecated public int describeContents(); - method @Deprecated @NonNull public android.net.NetworkInfo.DetailedState getDetailedState(); - method @Deprecated public String getExtraInfo(); - method @Deprecated public String getReason(); - method @Deprecated public android.net.NetworkInfo.State getState(); - method @Deprecated public int getSubtype(); - method @Deprecated public String getSubtypeName(); - method @Deprecated public int getType(); - method @Deprecated public String getTypeName(); - method @Deprecated public boolean isAvailable(); - method @Deprecated public boolean isConnected(); - method @Deprecated public boolean isConnectedOrConnecting(); - method @Deprecated public boolean isFailover(); - method @Deprecated public boolean isRoaming(); - method @Deprecated public void setDetailedState(@NonNull android.net.NetworkInfo.DetailedState, @Nullable String, @Nullable String); - method @Deprecated public void writeToParcel(android.os.Parcel, int); - field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR; - } - - @Deprecated public enum NetworkInfo.DetailedState { - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState AUTHENTICATING; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState BLOCKED; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CAPTIVE_PORTAL_CHECK; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTED; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTING; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTED; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTING; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState FAILED; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState IDLE; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState OBTAINING_IPADDR; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SCANNING; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SUSPENDED; - enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK; - } - - @Deprecated public enum NetworkInfo.State { - enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTED; - enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTING; - enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTED; - enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTING; - enum_constant @Deprecated public static final android.net.NetworkInfo.State SUSPENDED; - enum_constant @Deprecated public static final android.net.NetworkInfo.State UNKNOWN; - } - - public class NetworkRequest implements android.os.Parcelable { - method public boolean canBeSatisfiedBy(@Nullable android.net.NetworkCapabilities); - method public int describeContents(); - method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier(); - method public boolean hasCapability(int); - method public boolean hasTransport(int); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR; - } - - public static class NetworkRequest.Builder { - ctor public NetworkRequest.Builder(); - method public android.net.NetworkRequest.Builder addCapability(int); - method public android.net.NetworkRequest.Builder addTransportType(int); - method public android.net.NetworkRequest build(); - method @NonNull public android.net.NetworkRequest.Builder clearCapabilities(); - method public android.net.NetworkRequest.Builder removeCapability(int); - method public android.net.NetworkRequest.Builder removeTransportType(int); - method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String); - method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier); - } - public abstract class NetworkSpecifier { ctor public NetworkSpecifier(); } @@ -26276,44 +26011,6 @@ package android.net { field public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; // 0x6 } - public final class Proxy { - ctor public Proxy(); - method @Deprecated public static String getDefaultHost(); - method @Deprecated public static int getDefaultPort(); - method @Deprecated public static String getHost(android.content.Context); - method @Deprecated public static int getPort(android.content.Context); - field @Deprecated public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO"; - field public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE"; - } - - public class ProxyInfo implements android.os.Parcelable { - ctor public ProxyInfo(@Nullable android.net.ProxyInfo); - method public static android.net.ProxyInfo buildDirectProxy(String, int); - method public static android.net.ProxyInfo buildDirectProxy(String, int, java.util.List<java.lang.String>); - method public static android.net.ProxyInfo buildPacProxy(android.net.Uri); - method @NonNull public static android.net.ProxyInfo buildPacProxy(@NonNull android.net.Uri, int); - method public int describeContents(); - method public String[] getExclusionList(); - method public String getHost(); - method public android.net.Uri getPacFileUrl(); - method public int getPort(); - method public boolean isValid(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.ProxyInfo> CREATOR; - } - - public final class RouteInfo implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public android.net.IpPrefix getDestination(); - method @Nullable public java.net.InetAddress getGateway(); - method @Nullable public String getInterface(); - method public boolean hasGateway(); - method public boolean isDefaultRoute(); - method public boolean matches(java.net.InetAddress); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.RouteInfo> CREATOR; - } - @Deprecated public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory { ctor @Deprecated public SSLCertificateSocketFactory(int); method @Deprecated public java.net.Socket createSocket(java.net.Socket, String, int, boolean) throws java.io.IOException; @@ -26339,30 +26036,6 @@ package android.net { ctor public SSLSessionCache(android.content.Context); } - public abstract class SocketKeepalive implements java.lang.AutoCloseable { - method public final void close(); - method public final void start(@IntRange(from=0xa, to=0xe10) int); - method public final void stop(); - field public static final int ERROR_HARDWARE_ERROR = -31; // 0xffffffe1 - field public static final int ERROR_INSUFFICIENT_RESOURCES = -32; // 0xffffffe0 - field public static final int ERROR_INVALID_INTERVAL = -24; // 0xffffffe8 - field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb - field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9 - field public static final int ERROR_INVALID_NETWORK = -20; // 0xffffffec - field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea - field public static final int ERROR_INVALID_SOCKET = -25; // 0xffffffe7 - field public static final int ERROR_SOCKET_NOT_IDLE = -26; // 0xffffffe6 - field public static final int ERROR_UNSUPPORTED = -30; // 0xffffffe2 - } - - public static class SocketKeepalive.Callback { - ctor public SocketKeepalive.Callback(); - method public void onDataReceived(); - method public void onError(int); - method public void onStarted(); - method public void onStopped(); - } - public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { method public int describeContents(); method public int getSubscriptionId(); @@ -26420,9 +26093,6 @@ package android.net { field public static final int UNSUPPORTED = -1; // 0xffffffff } - public interface TransportInfo { - } - public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable { method public abstract android.net.Uri.Builder buildUpon(); method public int compareTo(android.net.Uri); @@ -26983,6 +26653,15 @@ package android.net.vcn { method @NonNull public android.net.vcn.VcnConfig build(); } + public abstract class VcnControlPlaneConfig { + } + + public final class VcnControlPlaneIkeConfig extends android.net.vcn.VcnControlPlaneConfig { + ctor public VcnControlPlaneIkeConfig(@NonNull android.net.ipsec.ike.IkeSessionParams, @NonNull android.net.ipsec.ike.TunnelModeChildSessionParams); + method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams getChildSessionParams(); + method @NonNull public android.net.ipsec.ike.IkeSessionParams getIkeSessionParams(); + } + public final class VcnGatewayConnectionConfig { method @NonNull public int[] getExposedCapabilities(); method @IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) public int getMaxMtu(); @@ -26991,7 +26670,7 @@ package android.net.vcn { } public static final class VcnGatewayConnectionConfig.Builder { - ctor public VcnGatewayConnectionConfig.Builder(); + ctor public VcnGatewayConnectionConfig.Builder(@NonNull android.net.vcn.VcnControlPlaneConfig); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addRequiredUnderlyingCapability(int); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build(); @@ -30520,8 +30199,8 @@ package android.os { } public final class BugreportManager { - method public void cancelBugreport(); - method public void startConnectivityBugreport(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); + method @WorkerThread public void cancelBugreport(); + method @WorkerThread public void startConnectivityBugreport(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); } public abstract static class BugreportManager.BugreportCallback { @@ -31786,10 +31465,10 @@ package android.os { method public static android.content.Intent createUserCreationIntent(@Nullable String, @Nullable String, @Nullable String, @Nullable android.os.PersistableBundle); method @WorkerThread public android.os.Bundle getApplicationRestrictions(String); method public long getSerialNumberForUser(android.os.UserHandle); - method @RequiresPermission("android.permission.MANAGE_USERS") public int getUserCount(); + method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS"}) public int getUserCount(); method public long getUserCreationTime(android.os.UserHandle); method public android.os.UserHandle getUserForSerialNumber(long); - method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}, conditional=true) public String getUserName(); + method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, "android.permission.CREATE_USERS"}, conditional=true) public String getUserName(); method public java.util.List<android.os.UserHandle> getUserProfiles(); method public android.os.Bundle getUserRestrictions(); method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle); @@ -34983,6 +34662,7 @@ package android.provider { field public static final String ACTION_QUICK_ACCESS_WALLET_SETTINGS = "android.settings.QUICK_ACCESS_WALLET_SETTINGS"; field public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS"; field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; + field public static final String ACTION_REQUEST_SCHEDULE_EXACT_ALARM = "android.settings.REQUEST_SCHEDULE_EXACT_ALARM"; field public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE = "android.settings.REQUEST_SET_AUTOFILL_SERVICE"; field public static final String ACTION_SEARCH_SETTINGS = "android.search.action.SEARCH_SETTINGS"; field public static final String ACTION_SECURITY_SETTINGS = "android.settings.SECURITY_SETTINGS"; @@ -37027,8 +36707,10 @@ package android.security { method @NonNull public static android.content.Intent createInstallIntent(); method @NonNull public static android.content.Intent createManageCredentialsIntent(@NonNull android.security.AppUriAuthenticationPolicy); method @Nullable @WorkerThread public static java.security.cert.X509Certificate[] getCertificateChain(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException; + method @NonNull public static android.security.AppUriAuthenticationPolicy getCredentialManagementAppPolicy(@NonNull android.content.Context) throws java.lang.SecurityException; method @Nullable @WorkerThread public static java.security.PrivateKey getPrivateKey(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException; method @Deprecated public static boolean isBoundKeyAlgorithm(@NonNull String); + method public static boolean isCredentialManagementApp(@NonNull android.content.Context); method public static boolean isKeyAlgorithmSupported(@NonNull String); field public static final String ACTION_KEYCHAIN_CHANGED = "android.security.action.KEYCHAIN_CHANGED"; field public static final String ACTION_KEY_ACCESS_CHANGED = "android.security.action.KEY_ACCESS_CHANGED"; @@ -38398,6 +38080,7 @@ package android.service.notification { field public static final int INTERRUPTION_FILTER_PRIORITY = 2; // 0x2 field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0 field public static final String META_DATA_DEFAULT_FILTER_TYPES = "android.service.notification.default_filter_types"; + field public static final String META_DATA_DISABLED_FILTER_TYPES = "android.service.notification.disabled_filter_types"; field public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1; // 0x1 field public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3; // 0x3 field public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2; // 0x2 @@ -39024,7 +38707,9 @@ package android.speech { field public static final int ERROR_NO_MATCH = 7; // 0x7 field public static final int ERROR_RECOGNIZER_BUSY = 8; // 0x8 field public static final int ERROR_SERVER = 4; // 0x4 + field public static final int ERROR_SERVER_DISCONNECTED = 11; // 0xb field public static final int ERROR_SPEECH_TIMEOUT = 6; // 0x6 + field public static final int ERROR_TOO_MANY_REQUESTS = 10; // 0xa field public static final String RESULTS_RECOGNITION = "results_recognition"; } @@ -40106,7 +39791,7 @@ package android.telecom { method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(android.telecom.PhoneAccountHandle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String, android.telecom.PhoneAccountHandle); - method public boolean hasCompanionInCallServiceAccess(); + method public boolean hasManageOngoingCallsPermission(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInCall(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInManagedCall(); method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle); @@ -40686,7 +40371,6 @@ package android.telephony { field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool"; field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool"; - field public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL = "store_sim_pin_for_unattended_reboot_bool"; field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool"; field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool"; field public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = "support_add_conference_participants_bool"; @@ -40734,6 +40418,7 @@ package android.telephony { } public static final class CarrierConfigManager.Apn { + field @Deprecated public static final String KEY_PREFIX = "apn."; field public static final String KEY_SETTINGS_DEFAULT_PROTOCOL_STRING = "apn.settings_default_protocol_string"; field public static final String KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING = "apn.settings_default_roaming_protocol_string"; field public static final String PROTOCOL_IPV4 = "IP"; @@ -40758,7 +40443,7 @@ package android.telephony { } public static final class CarrierConfigManager.ImsServiceEntitlement { - field public static final String KEY_AES_URL_STRING = "imsserviceentitlement.aes_url_string"; + field public static final String KEY_ENTITLEMENT_SERVER_URL_STRING = "imsserviceentitlement.entitlement_server_url_string"; field public static final String KEY_PREFIX = "imsserviceentitlement."; } @@ -40787,7 +40472,6 @@ package android.telephony { field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; // 0xe field public static final int INTEGRITY_ALGORITHM_NONE = 0; // 0x0 field public static final String KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL = "iwlan.add_ke_to_child_session_rekey_bool"; - field public static final String KEY_ADD_WIFI_MAC_ADDR_TO_NAI_BOOL = "iwlan.add_wifi_mac_addr_to_nai_bool"; field public static final String KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT = "iwlan.child_sa_rekey_hard_timer_sec_int"; field public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT = "iwlan.child_sa_rekey_soft_timer_sec_int"; field public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.child_session_aes_cbc_key_size_int_array"; @@ -41099,6 +40783,7 @@ package android.telephony { field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f field public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 65; // 0x41 + field public static final int ALL_MATCHING_RULES_FAILED = 2254; // 0x8ce field public static final int APN_DISABLED = 2045; // 0x7fd field public static final int APN_DISALLOWED_ON_ROAMING = 2059; // 0x80b field public static final int APN_MISMATCH = 2054; // 0x806 @@ -41248,6 +40933,7 @@ package android.telephony { field public static final int LTE_NAS_SERVICE_REQUEST_FAILED = 2117; // 0x845 field public static final int LTE_THROTTLING_NOT_REQUIRED = 2127; // 0x84f field public static final int MAC_FAILURE = 2183; // 0x887 + field public static final int MATCH_ALL_RULE_NOT_ALLOWED = 2253; // 0x8cd field public static final int MAXIMIUM_NSAPIS_EXCEEDED = 2157; // 0x86d field public static final int MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED = 2166; // 0x876 field public static final int MAX_ACCESS_PROBE = 2079; // 0x81f @@ -41985,7 +41671,6 @@ package android.telephony { public final class SignalStrengthUpdateRequest implements android.os.Parcelable { method public int describeContents(); - method @NonNull public android.os.IBinder getLiveToken(); method @NonNull public java.util.Collection<android.telephony.SignalThresholdInfo> getSignalThresholdInfos(); method public boolean isReportingRequestedWhileIdle(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -42033,6 +41718,7 @@ package android.telephony { method @NonNull public android.telephony.SmsManager createForSubscriptionId(int); method public java.util.ArrayList<java.lang.String> divideMessage(String); method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); + method public void downloadMultimediaMessage(@NonNull android.content.Context, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long); method @NonNull public android.os.Bundle getCarrierConfigValues(); method @Deprecated public static android.telephony.SmsManager getDefault(); method public static int getDefaultSmsSubscriptionId(); @@ -42044,6 +41730,7 @@ package android.telephony { method public void injectSmsPdu(byte[], String, android.app.PendingIntent); method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent); method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); + method public void sendMultimediaMessage(@NonNull android.content.Context, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long); method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>); method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, long); method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String, @Nullable String); @@ -42413,7 +42100,7 @@ package android.telephony { method @Deprecated public int getPhoneCount(); method public int getPhoneType(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription(); - method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState(); + method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState(); method @Nullable public android.telephony.SignalStrength getSignalStrength(); method public int getSimCarrierId(); method @Nullable public CharSequence getSimCarrierIdName(); @@ -46831,6 +46518,7 @@ package android.view { method public int getId(); method public android.view.KeyCharacterMap getKeyCharacterMap(); method public int getKeyboardType(); + method @NonNull public android.hardware.lights.LightsManager getLightsManager(); method public android.view.InputDevice.MotionRange getMotionRange(int); method public android.view.InputDevice.MotionRange getMotionRange(int, int); method public java.util.List<android.view.InputDevice.MotionRange> getMotionRanges(); @@ -47883,16 +47571,47 @@ package android.view { method public void onScaleEnd(android.view.ScaleGestureDetector); } + @UiThread public interface ScrollCaptureCallback { + method public void onScrollCaptureEnd(@NonNull Runnable); + method public void onScrollCaptureImageRequest(@NonNull android.view.ScrollCaptureSession, @NonNull android.os.CancellationSignal, @NonNull android.graphics.Rect, @NonNull java.util.function.Consumer<android.graphics.Rect>); + method public void onScrollCaptureSearch(@NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.graphics.Rect>); + method public void onScrollCaptureStart(@NonNull android.view.ScrollCaptureSession, @NonNull android.os.CancellationSignal, @NonNull Runnable); + } + + public class ScrollCaptureSession { + ctor public ScrollCaptureSession(@NonNull android.view.Surface, @NonNull android.graphics.Rect, @NonNull android.graphics.Point); + method @NonNull public android.graphics.Point getPositionInWindow(); + method @NonNull public android.graphics.Rect getScrollBounds(); + method @NonNull public android.view.Surface getSurface(); + } + + public final class ScrollCaptureTarget { + ctor public ScrollCaptureTarget(@NonNull android.view.View, @NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull android.view.ScrollCaptureCallback); + method @NonNull public android.view.ScrollCaptureCallback getCallback(); + method @NonNull public android.view.View getContainingView(); + method public int getHint(); + method @NonNull public android.graphics.Rect getLocalVisibleRect(); + method @NonNull public android.graphics.Point getPositionInWindow(); + method @Nullable public android.graphics.Rect getScrollBounds(); + method public void setScrollBounds(@Nullable android.graphics.Rect); + method @UiThread public void updatePositionInWindow(); + } + public class SearchEvent { ctor public SearchEvent(android.view.InputDevice); method public android.view.InputDevice getInputDevice(); } public class SoundEffectConstants { + method public static int getConstantForFocusDirection(int, boolean); method public static int getContantForFocusDirection(int); field public static final int CLICK = 0; // 0x0 field public static final int NAVIGATION_DOWN = 4; // 0x4 field public static final int NAVIGATION_LEFT = 1; // 0x1 + field public static final int NAVIGATION_REPEAT_DOWN = 8; // 0x8 + field public static final int NAVIGATION_REPEAT_LEFT = 5; // 0x5 + field public static final int NAVIGATION_REPEAT_RIGHT = 7; // 0x7 + field public static final int NAVIGATION_REPEAT_UP = 6; // 0x6 field public static final int NAVIGATION_RIGHT = 3; // 0x3 field public static final int NAVIGATION_UP = 2; // 0x2 } @@ -48204,6 +47923,7 @@ package android.view { method public void dispatchProvideStructure(android.view.ViewStructure); method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>); method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>); + method public void dispatchScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>); method protected void dispatchSetActivated(boolean); method protected void dispatchSetPressed(boolean); method protected void dispatchSetSelected(boolean); @@ -48231,6 +47951,7 @@ package android.view { method public android.view.View focusSearch(int); method public void forceHasOverlappingRendering(boolean); method public void forceLayout(); + method @Nullable public void generateDisplayHash(@NonNull String, @Nullable android.graphics.Rect, @NonNull java.util.concurrent.Executor, @NonNull android.view.displayhash.DisplayHashResultCallback); method public static int generateViewId(); method public CharSequence getAccessibilityClassName(); method public android.view.View.AccessibilityDelegate getAccessibilityDelegate(); @@ -48362,6 +48083,7 @@ package android.view { method public int getScrollBarFadeDuration(); method public int getScrollBarSize(); method public int getScrollBarStyle(); + method public int getScrollCaptureHint(); method public int getScrollIndicators(); method public final int getScrollX(); method public final int getScrollY(); @@ -48530,6 +48252,7 @@ package android.view { method public void onRtlPropertiesChanged(int); method @CallSuper @Nullable protected android.os.Parcelable onSaveInstanceState(); method public void onScreenStateChanged(int); + method public void onScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>); method protected void onScrollChanged(int, int, int, int); method protected boolean onSetAlpha(int); method protected void onSizeChanged(int, int, int, int); @@ -48712,6 +48435,8 @@ package android.view { method public void setScrollBarFadeDuration(int); method public void setScrollBarSize(int); method public void setScrollBarStyle(int); + method public final void setScrollCaptureCallback(@Nullable android.view.ScrollCaptureCallback); + method public void setScrollCaptureHint(int); method public void setScrollContainer(boolean); method public void setScrollIndicators(int); method public void setScrollIndicators(int, int); @@ -48890,6 +48615,10 @@ package android.view { field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1 field public static final int SCROLL_AXIS_NONE = 0; // 0x0 field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2 + field public static final int SCROLL_CAPTURE_HINT_AUTO = 0; // 0x0 + field public static final int SCROLL_CAPTURE_HINT_EXCLUDE = 1; // 0x1 + field public static final int SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS = 4; // 0x4 + field public static final int SCROLL_CAPTURE_HINT_INCLUDE = 2; // 0x2 field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2 field public static final int SCROLL_INDICATOR_END = 32; // 0x20 field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4 @@ -49674,6 +49403,7 @@ package android.view { method public abstract boolean performContextMenuIdentifierAction(int, int); method public abstract boolean performPanelIdentifierAction(int, int, int); method public abstract boolean performPanelShortcut(int, int, android.view.KeyEvent, int); + method public void registerScrollCaptureCallback(@NonNull android.view.ScrollCaptureCallback); method public final void removeOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener); method public boolean requestFeature(int); method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int); @@ -49753,6 +49483,7 @@ package android.view { method public abstract void takeKeyEvents(boolean); method public abstract void takeSurface(android.view.SurfaceHolder.Callback2); method public abstract void togglePanel(int, android.view.KeyEvent); + method public void unregisterScrollCaptureCallback(@NonNull android.view.ScrollCaptureCallback); field public static final int DECOR_CAPTION_SHADE_AUTO = 0; // 0x0 field public static final int DECOR_CAPTION_SHADE_DARK = 2; // 0x2 field public static final int DECOR_CAPTION_SHADE_LIGHT = 1; // 0x1 @@ -51186,6 +50917,41 @@ package android.view.contentcapture { } +package android.view.displayhash { + + public final class DisplayHash implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.displayhash.DisplayHash> CREATOR; + } + + public final class DisplayHashManager { + method @NonNull public java.util.Set<java.lang.String> getSupportedHashAlgorithms(); + method @Nullable public android.view.displayhash.VerifiedDisplayHash verifyDisplayHash(@NonNull android.view.displayhash.DisplayHash); + } + + public interface DisplayHashResultCallback { + method public void onDisplayHashError(int); + method public void onDisplayHashResult(@NonNull android.view.displayhash.DisplayHash); + field public static final int DISPLAY_HASH_ERROR_INVALID_BOUNDS = -2; // 0xfffffffe + field public static final int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3; // 0xfffffffd + field public static final int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4; // 0xfffffffc + field public static final int DISPLAY_HASH_ERROR_UNKNOWN = -1; // 0xffffffff + } + + public final class VerifiedDisplayHash implements android.os.Parcelable { + ctor public VerifiedDisplayHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[]); + method public int describeContents(); + method @NonNull public android.graphics.Rect getBoundsInWindow(); + method @NonNull public String getHashAlgorithm(); + method @NonNull public byte[] getImageHash(); + method public long getTimeMillis(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.displayhash.VerifiedDisplayHash> CREATOR; + } + +} + package android.view.inputmethod { public class BaseInputConnection implements android.view.inputmethod.InputConnection { @@ -53209,6 +52975,7 @@ package android.widget { method public int getCheckedItemPosition(); method public android.util.SparseBooleanArray getCheckedItemPositions(); method public int getChoiceMode(); + method public int getEdgeEffectType(); method public int getListPaddingBottom(); method public int getListPaddingLeft(); method public int getListPaddingRight(); @@ -53250,6 +53017,7 @@ package android.widget { method public void setChoiceMode(int); method public void setDrawSelectorOnTop(boolean); method public void setEdgeEffectColor(@ColorInt int); + method public void setEdgeEffectType(int); method public void setFastScrollAlwaysVisible(boolean); method public void setFastScrollEnabled(boolean); method public void setFastScrollStyle(int); @@ -53540,11 +53308,27 @@ package android.widget { ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet); ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int); ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int, int); + method @Deprecated @Nullable public android.graphics.BlendMode getDialTintBlendMode(); + method @Deprecated @Nullable public android.content.res.ColorStateList getDialTintList(); + method @Deprecated @Nullable public android.graphics.BlendMode getHourHandTintBlendMode(); + method @Deprecated @Nullable public android.content.res.ColorStateList getHourHandTintList(); + method @Deprecated @Nullable public android.graphics.BlendMode getMinuteHandTintBlendMode(); + method @Deprecated @Nullable public android.content.res.ColorStateList getMinuteHandTintList(); + method @Deprecated @Nullable public android.graphics.BlendMode getSecondHandTintBlendMode(); + method @Deprecated @Nullable public android.content.res.ColorStateList getSecondHandTintList(); method @Deprecated @Nullable public String getTimeZone(); method @Deprecated public void setDial(@NonNull android.graphics.drawable.Icon); + method @Deprecated public void setDialTintBlendMode(@Nullable android.graphics.BlendMode); + method @Deprecated public void setDialTintList(@Nullable android.content.res.ColorStateList); method @Deprecated public void setHourHand(@NonNull android.graphics.drawable.Icon); + method @Deprecated public void setHourHandTintBlendMode(@Nullable android.graphics.BlendMode); + method @Deprecated public void setHourHandTintList(@Nullable android.content.res.ColorStateList); method @Deprecated public void setMinuteHand(@NonNull android.graphics.drawable.Icon); + method @Deprecated public void setMinuteHandTintBlendMode(@Nullable android.graphics.BlendMode); + method @Deprecated public void setMinuteHandTintList(@Nullable android.content.res.ColorStateList); method @Deprecated public void setSecondHand(@Nullable android.graphics.drawable.Icon); + method @Deprecated public void setSecondHandTintBlendMode(@Nullable android.graphics.BlendMode); + method @Deprecated public void setSecondHandTintList(@Nullable android.content.res.ColorStateList); method @Deprecated public void setTimeZone(@Nullable String); } @@ -54242,6 +54026,7 @@ package android.widget { method public boolean executeKeyEvent(android.view.KeyEvent); method public void fling(int); method public boolean fullScroll(int); + method public int getEdgeEffectType(); method @ColorInt public int getLeftEdgeEffectColor(); method public int getMaxScrollAmount(); method @ColorInt public int getRightEdgeEffectColor(); @@ -54249,6 +54034,7 @@ package android.widget { method public boolean isSmoothScrollingEnabled(); method public boolean pageScroll(int); method public void setEdgeEffectColor(@ColorInt int); + method public void setEdgeEffectType(int); method public void setFillViewport(boolean); method public void setLeftEdgeEffectColor(@ColorInt int); method public void setRightEdgeEffectColor(@ColorInt int); @@ -54967,6 +54753,9 @@ package android.widget { method public void setChronometer(@IdRes int, long, String, boolean); method public void setChronometerCountDown(@IdRes int, boolean); method public void setColor(@IdRes int, @NonNull String, @ColorRes int); + method public void setColorInt(@IdRes int, @NonNull String, @ColorInt int, @ColorInt int); + method public void setColorStateList(@IdRes int, @NonNull String, @Nullable android.content.res.ColorStateList); + method public void setColorStateList(@IdRes int, @NonNull String, @Nullable android.content.res.ColorStateList, @Nullable android.content.res.ColorStateList); method public void setColorStateList(@IdRes int, @NonNull String, @ColorRes int); method public void setCompoundButtonChecked(@IdRes int, boolean); method public void setContentDescription(@IdRes int, CharSequence); @@ -54977,6 +54766,7 @@ package android.widget { method public void setFloatDimen(@IdRes int, @NonNull String, @DimenRes int); method public void setFloatDimen(@IdRes int, @NonNull String, float, int); method public void setIcon(@IdRes int, String, android.graphics.drawable.Icon); + method public void setIcon(@IdRes int, @NonNull String, @Nullable android.graphics.drawable.Icon, @Nullable android.graphics.drawable.Icon); method public void setImageViewBitmap(@IdRes int, android.graphics.Bitmap); method public void setImageViewIcon(@IdRes int, android.graphics.drawable.Icon); method public void setImageViewResource(@IdRes int, @DrawableRes int); @@ -54988,6 +54778,7 @@ package android.widget { method public void setLabelFor(@IdRes int, @IdRes int); method public void setLightBackgroundLayoutId(@LayoutRes int); method public void setLong(@IdRes int, String, long); + method public void setOnCheckedChangeResponse(@IdRes int, @NonNull android.widget.RemoteViews.RemoteResponse); method public void setOnClickFillInIntent(@IdRes int, android.content.Intent); method public void setOnClickPendingIntent(@IdRes int, android.app.PendingIntent); method public void setOnClickResponse(@IdRes int, @NonNull android.widget.RemoteViews.RemoteResponse); @@ -55006,6 +54797,12 @@ package android.widget { method public void setTextViewText(@IdRes int, CharSequence); method public void setTextViewTextSize(@IdRes int, int, float); method public void setUri(@IdRes int, String, android.net.Uri); + method public void setViewLayoutHeight(@IdRes int, float, int); + method public void setViewLayoutHeightDimen(@IdRes int, @DimenRes int); + method public void setViewLayoutMargin(@IdRes int, int, float, int); + method public void setViewLayoutMarginDimen(@IdRes int, int, @DimenRes int); + method public void setViewLayoutWidth(@IdRes int, float, int); + method public void setViewLayoutWidthDimen(@IdRes int, @DimenRes int); method public void setViewOutlinePreferredRadius(@IdRes int, float, int); method public void setViewOutlinePreferredRadiusDimen(@IdRes int, @DimenRes int); method public void setViewPadding(@IdRes int, @Px int, @Px int, @Px int, @Px int); @@ -55014,7 +54811,14 @@ package android.widget { method public void showPrevious(@IdRes int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.widget.RemoteViews> CREATOR; + field public static final String EXTRA_CHECKED = "android.widget.extra.CHECKED"; field public static final String EXTRA_SHARED_ELEMENT_BOUNDS = "android.widget.extra.SHARED_ELEMENT_BOUNDS"; + field public static final int MARGIN_BOTTOM = 3; // 0x3 + field public static final int MARGIN_END = 5; // 0x5 + field public static final int MARGIN_LEFT = 0; // 0x0 + field public static final int MARGIN_RIGHT = 2; // 0x2 + field public static final int MARGIN_START = 4; // 0x4 + field public static final int MARGIN_TOP = 1; // 0x1 } public static class RemoteViews.ActionException extends java.lang.RuntimeException { @@ -55084,6 +54888,7 @@ package android.widget { method public void fling(int); method public boolean fullScroll(int); method @ColorInt public int getBottomEdgeEffectColor(); + method public int getEdgeEffectType(); method public int getMaxScrollAmount(); method @ColorInt public int getTopEdgeEffectColor(); method public boolean isFillViewport(); @@ -55092,6 +54897,7 @@ package android.widget { method public void scrollToDescendant(@NonNull android.view.View); method public void setBottomEdgeEffectColor(@ColorInt int); method public void setEdgeEffectColor(@ColorInt int); + method public void setEdgeEffectType(int); method public void setFillViewport(boolean); method public void setSmoothScrollingEnabled(boolean); method public void setTopEdgeEffectColor(@ColorInt int); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index de02d0bb7e98..7ea7d61ac3c5 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -135,7 +135,7 @@ package android.media.session { } public final class MediaSessionManager { - method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.os.Handler); + method public void addOnActiveSessionsChangedListener(@Nullable android.content.ComponentName, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener); method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent); method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean); method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent); @@ -163,61 +163,12 @@ package android.media.session { package android.net { - public final class ConnectivityFrameworkInitializer { - method public static void registerServiceWrappers(); - } - - public class ConnectivityManager { - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); - method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); - } - public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable { method public int getResourceId(); } - public final class NetworkAgentConfig implements android.os.Parcelable { - method @Nullable public String getSubscriberId(); - } - - public static final class NetworkAgentConfig.Builder { - method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String); - } - - public final class NetworkCapabilities implements android.os.Parcelable { - field public static final int TRANSPORT_TEST = 7; // 0x7 - } - - public final class Proxy { - method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo); - } - - public final class TcpRepairWindow { - ctor public TcpRepairWindow(int, int, int, int, int, int); - field public final int maxWindow; - field public final int rcvWnd; - field public final int rcvWndScale; - field public final int rcvWup; - field public final int sndWl1; - field public final int sndWnd; - } - - public final class TestNetworkInterface implements android.os.Parcelable { - ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String); - method public int describeContents(); - method @NonNull public android.os.ParcelFileDescriptor getFileDescriptor(); - method @NonNull public String getInterfaceName(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR; - } - - public class TestNetworkManager { - method @NonNull public android.net.TestNetworkInterface createTapInterface(); - method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>); - method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder); - method public void teardownTestNetwork(@NonNull android.net.Network); - field public static final String TEST_TAP_PREFIX = "testtap"; + public class NetworkWatchlistManager { + method @Nullable public byte[] getWatchlistConfigHash(); } public final class UnderlyingNetworkInfo implements android.os.Parcelable { @@ -230,14 +181,6 @@ package android.net { field @NonNull public final java.util.List<java.lang.String> underlyingIfaces; } - public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo { - ctor public VpnTransportInfo(int); - method public int describeContents(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR; - field public final int type; - } - } package android.os { @@ -271,6 +214,16 @@ package android.os { } +package android.os.storage { + + public class StorageManager { + method public void notifyAppIoBlocked(@NonNull String, int, int, int); + method public void notifyAppIoResumed(@NonNull String, int, int, int); + field public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 0; // 0x0 + } + +} + package android.provider { public final class DeviceConfig { diff --git a/core/api/removed.txt b/core/api/removed.txt index 990388a54c85..a99178d51d77 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -241,12 +241,6 @@ package android.media.tv { package android.net { - public class ConnectivityManager { - method @Deprecated public boolean requestRouteToHost(int, int); - method @Deprecated public int startUsingNetworkFeature(int, String); - method @Deprecated public int stopUsingNetworkFeature(int, String); - } - @Deprecated public class NetworkBadging { method @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme); field public static final int BADGING_4K = 30; // 0x1e diff --git a/core/api/system-current.txt b/core/api/system-current.txt index deff7b38ea2a..12cc0fef399e 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -27,6 +27,7 @@ package android { field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"; field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER"; field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS"; + field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA"; field public static final String BACKUP = "android.permission.BACKUP"; field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION"; field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE"; @@ -176,6 +177,7 @@ package android { field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE"; field public static final String OBSERVE_NETWORK_POLICY = "android.permission.OBSERVE_NETWORK_POLICY"; field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS"; + field public static final String OBSERVE_SENSOR_PRIVACY = "android.permission.OBSERVE_SENSOR_PRIVACY"; field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS"; field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG"; field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT"; @@ -214,6 +216,7 @@ package android { field public static final String RECEIVE_DEVICE_CUSTOMIZATION_READY = "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"; field public static final String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST"; field public static final String RECEIVE_WIFI_CREDENTIAL_CHANGE = "android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE"; + field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO"; field public static final String RECOVERY = "android.permission.RECOVERY"; field public static final String RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE"; field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER"; @@ -333,6 +336,8 @@ package android { } public static final class R.string { + field public static final int config_customMediaKeyDispatcher = 17039404; // 0x104002c + field public static final int config_customMediaSessionPolicyProvider = 17039405; // 0x104002d field public static final int config_defaultAssistant = 17039393; // 0x1040021 field public static final int config_defaultBrowser = 17039394; // 0x1040022 field public static final int config_defaultCallRedirection = 17039397; // 0x1040025 @@ -350,6 +355,7 @@ package android { field public static final int config_systemContacts = 17039403; // 0x104002b field public static final int config_systemGallery = 17039399; // 0x1040027 field public static final int config_systemShell = 17039402; // 0x104002a + field public static final int config_systemSpeechRecognizer = 17039406; // 0x104002e } public static final class R.style { @@ -639,8 +645,8 @@ package android.app { method public static android.app.BroadcastOptions makeBasic(); method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean); method public void setDontSendToRestrictedApps(boolean); - method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long); - method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(int, long); + method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppAllowlist(long, int, int, @Nullable String); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long); method public android.os.Bundle toBundle(); } @@ -851,13 +857,6 @@ package android.app { method public void onVrStateChanged(boolean); } - public final class WallpaperColors implements android.os.Parcelable { - ctor public WallpaperColors(@NonNull android.graphics.Color, @Nullable android.graphics.Color, @Nullable android.graphics.Color, int); - method public int getColorHints(); - field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1 - field public static final int HINT_SUPPORTS_DARK_THEME = 2; // 0x2 - } - public final class WallpaperInfo implements android.os.Parcelable { method public boolean supportsAmbientMode(); } @@ -882,7 +881,7 @@ package android.app.admin { public class DevicePolicyManager { method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner(); - method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser(); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser(); method @Nullable public CharSequence getDeviceOwnerOrganizationName(); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getDeviceOwnerUser(); @@ -1824,7 +1823,7 @@ package android.bluetooth { method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public android.bluetooth.BufferConstraints getBufferConstraints(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getDynamicBufferSupport(); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferMillis(int, int); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferLengthMillis(int, int); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1; // 0x1 field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; // 0x2 @@ -1940,6 +1939,10 @@ package android.bluetooth { field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; } + public final class BluetoothMapClient implements android.bluetooth.BluetoothProfile { + method @RequiresPermission(android.Manifest.permission.SEND_SMS) public boolean sendMessage(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.Collection<android.net.Uri>, @NonNull String, @Nullable android.app.PendingIntent, @Nullable android.app.PendingIntent); + } + public final class BluetoothPan implements android.bluetooth.BluetoothProfile { method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); @@ -1972,6 +1975,7 @@ package android.bluetooth { field public static final int CONNECTION_POLICY_FORBIDDEN = 0; // 0x0 field public static final int CONNECTION_POLICY_UNKNOWN = -1; // 0xffffffff field public static final int HEADSET_CLIENT = 16; // 0x10 + field public static final int MAP_CLIENT = 18; // 0x12 field public static final int PAN = 5; // 0x5 field public static final int PBAP_CLIENT = 17; // 0x11 field @Deprecated public static final int PRIORITY_OFF = 0; // 0x0 @@ -2025,7 +2029,7 @@ package android.bluetooth { public final class BufferConstraints implements android.os.Parcelable { ctor public BufferConstraints(@NonNull java.util.List<android.bluetooth.BufferConstraint>); method public int describeContents(); - method @Nullable public android.bluetooth.BufferConstraint getCodec(int); + method @Nullable public android.bluetooth.BufferConstraint forCodec(int); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int BUFFER_CODEC_MAX_NUM = 32; // 0x20 field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraints> CREATOR; @@ -2190,6 +2194,7 @@ package android.content { field public static final String ACTION_PENDING_INCIDENT_REPORTS_CHANGED = "android.intent.action.PENDING_INCIDENT_REPORTS_CHANGED"; field public static final String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED"; field public static final String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART"; + field public static final String ACTION_REBOOT_READY = "android.intent.action.REBOOT_READY"; field public static final String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE"; field @RequiresPermission(android.Manifest.permission.REVIEW_ACCESSIBILITY_SERVICES) public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES"; field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_ONGOING_PERMISSION_USAGE = "android.intent.action.REVIEW_ONGOING_PERMISSION_USAGE"; @@ -2213,6 +2218,7 @@ package android.content { field public static final String EXTRA_INSTANT_APP_HOSTNAME = "android.intent.extra.INSTANT_APP_HOSTNAME"; field public static final String EXTRA_INSTANT_APP_SUCCESS = "android.intent.extra.INSTANT_APP_SUCCESS"; field public static final String EXTRA_INSTANT_APP_TOKEN = "android.intent.extra.INSTANT_APP_TOKEN"; + field public static final String EXTRA_IS_READY_TO_REBOOT = "android.intent.extra.IS_READY_TO_REBOOT"; field public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE"; field public static final String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID"; field public static final String EXTRA_PACKAGES = "android.intent.extra.PACKAGES"; @@ -2452,10 +2458,10 @@ package android.content.pm { } public static class PackageInstaller.Session implements java.io.Closeable { - method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]); + method public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void commitTransferred(@NonNull android.content.IntentSender); - method @Nullable @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public android.content.pm.DataLoaderParams getDataLoaderParams(); - method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void removeFile(int, @NonNull String); + method @Nullable public android.content.pm.DataLoaderParams getDataLoaderParams(); + method public void removeFile(int, @NonNull String); } public static class PackageInstaller.SessionInfo implements android.os.Parcelable { @@ -2476,7 +2482,7 @@ package android.content.pm { public static class PackageInstaller.SessionParams implements android.os.Parcelable { method @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public void setAllocateAggressive(boolean); method @Deprecated public void setAllowDowngrade(boolean); - method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.USE_INSTALLER_V2"}) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams); + method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams); method public void setDontKillApp(boolean); method public void setEnableRollback(boolean); method public void setEnableRollback(boolean, int); @@ -2541,10 +2547,12 @@ package android.content.pm { field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio"; + field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle"; field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub"; field public static final String FEATURE_CROSS_LAYER_BLUR = "android.software.cross_layer_blur"; field @Deprecated public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery"; field public static final String FEATURE_INCREMENTAL_DELIVERY_VERSION = "android.software.incremental_delivery_version"; + field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle"; field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow"; field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock"; field public static final String FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION = "android.hardware.telephony.ims.singlereg"; @@ -2556,7 +2564,6 @@ package android.content.pm { field public static final int FLAG_PERMISSION_ONE_TIME = 65536; // 0x10000 field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4 field public static final int FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT = 2048; // 0x800 - field public static final int FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT = 262144; // 0x40000 field public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 4096; // 0x1000 field public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT = 8192; // 0x2000 field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40 @@ -2742,6 +2749,15 @@ package android.content.pm.permission { package android.content.pm.verify.domain { + public final class DomainOwner implements android.os.Parcelable { + ctor public DomainOwner(@NonNull String, boolean); + method public int describeContents(); + method @NonNull public String getPackageName(); + method public boolean isOverrideable(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainOwner> CREATOR; + } + public final class DomainVerificationInfo implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getHostToStateMap(); @@ -2754,6 +2770,7 @@ package android.content.pm.verify.domain { public interface DomainVerificationManager { method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; method @Nullable @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public android.content.pm.verify.domain.DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; + method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public java.util.List<android.content.pm.verify.domain.DomainOwner> getOwnersForDomain(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> getValidVerificationPackageNames(); method public static boolean isStateModifiable(int); method public static boolean isStateVerified(int); @@ -2775,13 +2792,16 @@ package android.content.pm.verify.domain { public final class DomainVerificationUserSelection implements android.os.Parcelable { method public int describeContents(); - method @NonNull public java.util.Map<java.lang.String,java.lang.Boolean> getHostToUserSelectionMap(); + method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getHostToStateMap(); method @NonNull public java.util.UUID getIdentifier(); method @NonNull public String getPackageName(); method @NonNull public android.os.UserHandle getUser(); method @NonNull public boolean isLinkHandlingAllowed(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationUserSelection> CREATOR; + field public static final int DOMAIN_STATE_NONE = 0; // 0x0 + field public static final int DOMAIN_STATE_SELECTED = 1; // 0x1 + field public static final int DOMAIN_STATE_VERIFIED = 2; // 0x2 } } @@ -2865,7 +2885,7 @@ package android.graphics.fonts { } public class FontManager { - method @Nullable public android.text.FontConfig getFontConfig(); + method @NonNull public android.text.FontConfig getFontConfig(); method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int); method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.graphics.fonts.FontFileUpdateRequest, @IntRange(from=0) int); method @Deprecated @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[], @IntRange(from=0) int); @@ -2899,6 +2919,22 @@ package android.hardware { method public boolean injectSensorData(android.hardware.Sensor, float[], int, long); } + public final class SensorPrivacyManager { + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int); + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); + } + + public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener { + method public void onSensorPrivacyChanged(boolean); + } + + public static class SensorPrivacyManager.Sensors { + field public static final int CAMERA = 2; // 0x2 + field public static final int MICROPHONE = 1; // 0x1 + } + } package android.hardware.biometrics { @@ -3394,42 +3430,12 @@ package android.hardware.hdmi { package android.hardware.lights { - public final class Light implements android.os.Parcelable { - method public int describeContents(); - method public int getId(); - method public int getOrdinal(); - method public int getType(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR; - } - public final class LightState implements android.os.Parcelable { - ctor public LightState(@ColorInt int); - method public int describeContents(); - method @ColorInt public int getColor(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR; - } - - public final class LightsManager { - method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public java.util.List<android.hardware.lights.Light> getLights(); - method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightsManager.LightsSession openSession(); - field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8 + ctor @Deprecated public LightState(@ColorInt int); } - public final class LightsManager.LightsSession implements java.lang.AutoCloseable { - method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close(); - method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void requestLights(@NonNull android.hardware.lights.LightsRequest); - } - - public final class LightsRequest { - } - - public static final class LightsRequest.Builder { - ctor public LightsRequest.Builder(); - method @NonNull public android.hardware.lights.LightsRequest build(); - method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light); - method @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState); + public abstract class LightsManager { + field @Deprecated public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8 } } @@ -4709,6 +4715,7 @@ package android.location { } public class LocationManager { + method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener); method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); method @Nullable public String getExtraLocationControllerPackage(); @@ -4723,7 +4730,7 @@ package android.location { method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String); method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback); - method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerProviderRequestListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.Listener); + method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void removeProviderRequestChangedListener(@NonNull android.location.provider.ProviderRequest.ChangedListener); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent); @@ -4732,7 +4739,6 @@ package android.location { method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle); method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setProviderEnabledForUser(@NonNull String, boolean, @NonNull android.os.UserHandle); method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean unregisterGnssBatchedLocationCallback(@NonNull android.location.BatchedLocationCallback); - method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void unregisterProviderRequestListener(@NonNull android.location.provider.ProviderRequest.Listener); } public final class LocationRequest implements android.os.Parcelable { @@ -4882,7 +4888,7 @@ package android.location.provider { method @NonNull public android.location.provider.ProviderRequest.Builder setWorkSource(@NonNull android.os.WorkSource); } - public static interface ProviderRequest.Listener { + public static interface ProviderRequest.ChangedListener { method public void onProviderRequestChanged(@NonNull String, @NonNull android.location.provider.ProviderRequest); } @@ -5074,9 +5080,10 @@ package android.media { } public static class AudioTrack.TunerConfiguration { - ctor @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public AudioTrack.TunerConfiguration(@IntRange(from=1) int, @IntRange(from=1) int); + ctor @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public AudioTrack.TunerConfiguration(@IntRange(from=0) int, @IntRange(from=1) int); method @IntRange(from=1) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getContentId(); method @IntRange(from=1) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getSyncId(); + field public static final int CONTENT_ID_NONE = 0; // 0x0 } public class HwAudioSource { @@ -5093,7 +5100,7 @@ package android.media { } public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation { - method @RequiresPermission(android.Manifest.permission.BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener, @Nullable android.os.Handler); + method @RequiresPermission(android.Manifest.permission.BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener(@NonNull android.content.Context, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaPlayer.OnRtpRxNoticeListener); } public static interface MediaPlayer.OnRtpRxNoticeListener { @@ -5700,7 +5707,7 @@ package android.media.tv.tuner { method public void updateResourcePriority(int, int); field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff field public static final int INVALID_FILTER_ID = -1; // 0xffffffff - field public static final long INVALID_FILTER_ID_64BIT = -1L; // 0xffffffffffffffffL + field public static final long INVALID_FILTER_ID_LONG = -1L; // 0xffffffffffffffffL field public static final int INVALID_FIRST_MACROBLOCK_IN_SLICE = -1; // 0xffffffff field public static final int INVALID_FRONTEND_ID = -1; // 0xffffffff field public static final int INVALID_FRONTEND_SETTING_FREQUENCY = -1; // 0xffffffff @@ -5881,7 +5888,7 @@ package android.media.tv.tuner.filter { method public int getItemFragmentIndex(); method public int getItemId(); method public int getLastItemFragmentIndex(); - method public int getMpuSequenceNumber(); + method @IntRange(from=0) public int getMpuSequenceNumber(); } public class DownloadSettings extends android.media.tv.tuner.filter.Settings { @@ -5899,7 +5906,7 @@ package android.media.tv.tuner.filter { method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration); method public int flush(); method public int getId(); - method public long getId64Bit(); + method public long getIdLong(); method public int read(@NonNull byte[], long, long); method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter); method public int setMonitorEventMask(int); @@ -5990,7 +5997,7 @@ package android.media.tv.tuner.filter { method public long getDataLength(); method @Nullable public android.media.tv.tuner.filter.AudioDescriptor getExtraMetaData(); method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock(); - method public int getMpuSequenceNumber(); + method @IntRange(from=0) public int getMpuSequenceNumber(); method public long getOffset(); method public long getPts(); method public int getStreamId(); @@ -6015,7 +6022,7 @@ package android.media.tv.tuner.filter { public class MmtpRecordEvent extends android.media.tv.tuner.filter.FilterEvent { method public long getDataLength(); method public int getFirstMacroblockInSlice(); - method public int getMpuSequenceNumber(); + method @IntRange(from=0) public int getMpuSequenceNumber(); method public long getPts(); method public int getScHevcIndexMask(); method public int getTsIndexMask(); @@ -6023,7 +6030,7 @@ package android.media.tv.tuner.filter { public class PesEvent extends android.media.tv.tuner.filter.FilterEvent { method public int getDataLength(); - method public int getMpuSequenceNumber(); + method @IntRange(from=0) public int getMpuSequenceNumber(); method public int getStreamId(); } @@ -7084,102 +7091,6 @@ package android.metrics { package android.net { - public class CaptivePortal implements android.os.Parcelable { - method public void logEvent(int, @NonNull String); - method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork(); - method public void useNetwork(); - field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64 - field public static final int APP_RETURN_DISMISSED = 0; // 0x0 - field public static final int APP_RETURN_UNWANTED = 1; // 0x1 - field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2 - } - - public final class CaptivePortalData implements android.os.Parcelable { - method public int describeContents(); - method public long getByteLimit(); - method public long getExpiryTimeMillis(); - method public long getRefreshTimeMillis(); - method @Nullable public android.net.Uri getUserPortalUrl(); - method public int getUserPortalUrlSource(); - method @Nullable public String getVenueFriendlyName(); - method @Nullable public android.net.Uri getVenueInfoUrl(); - method public int getVenueInfoUrlSource(); - method public boolean isCaptive(); - method public boolean isSessionExtendable(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0 - field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1 - field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR; - } - - public static class CaptivePortalData.Builder { - ctor public CaptivePortalData.Builder(); - ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData); - method @NonNull public android.net.CaptivePortalData build(); - method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long); - method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean); - method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long); - method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); - method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); - method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); - method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int); - method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String); - method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); - method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int); - } - - public class ConnectivityManager { - method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); - method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); - method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl(); - method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener); - method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider); - method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor); - method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); - method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean); - method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi(); - method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle); - method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); - method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); - method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); - method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider); - method public void unregisterQosCallback(@NonNull android.net.QosCallback); - method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback); - field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; - field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; - field public static final int TETHERING_BLUETOOTH = 2; // 0x2 - field public static final int TETHERING_USB = 1; // 0x1 - field public static final int TETHERING_WIFI = 0; // 0x0 - field @Deprecated public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd - field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 - field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb - field public static final int TYPE_NONE = -1; // 0xffffffff - field @Deprecated public static final int TYPE_PROXY = 16; // 0x10 - field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd - } - - public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener { - method public void onComplete(); - } - - @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback { - ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback(); - method @Deprecated public void onTetheringFailed(); - method @Deprecated public void onTetheringStarted(); - } - - @Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener { - method @Deprecated public void onTetheringEntitlementResult(int); - } - - @Deprecated public abstract static class ConnectivityManager.OnTetheringEventCallback { - ctor @Deprecated public ConnectivityManager.OnTetheringEventCallback(); - method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network); - } - public class DnsResolverServiceManager { method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public static android.os.IBinder getService(@NonNull android.content.Context); } @@ -7197,48 +7108,6 @@ package android.net { method public void release(); } - public final class InvalidPacketException extends java.lang.Exception { - ctor public InvalidPacketException(int); - method public int getError(); - field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb - field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9 - field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea - } - - public final class IpConfiguration implements android.os.Parcelable { - ctor public IpConfiguration(); - ctor public IpConfiguration(@NonNull android.net.IpConfiguration); - method public int describeContents(); - method @Nullable public android.net.ProxyInfo getHttpProxy(); - method @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment(); - method @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings(); - method @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration(); - method public void setHttpProxy(@Nullable android.net.ProxyInfo); - method public void setIpAssignment(@NonNull android.net.IpConfiguration.IpAssignment); - method public void setProxySettings(@NonNull android.net.IpConfiguration.ProxySettings); - method public void setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.IpConfiguration> CREATOR; - } - - public enum IpConfiguration.IpAssignment { - enum_constant public static final android.net.IpConfiguration.IpAssignment DHCP; - enum_constant public static final android.net.IpConfiguration.IpAssignment STATIC; - enum_constant public static final android.net.IpConfiguration.IpAssignment UNASSIGNED; - } - - public enum IpConfiguration.ProxySettings { - enum_constant public static final android.net.IpConfiguration.ProxySettings NONE; - enum_constant public static final android.net.IpConfiguration.ProxySettings PAC; - enum_constant public static final android.net.IpConfiguration.ProxySettings STATIC; - enum_constant public static final android.net.IpConfiguration.ProxySettings UNASSIGNED; - } - - public final class IpPrefix implements android.os.Parcelable { - ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); - ctor public IpPrefix(@NonNull String); - } - public final class IpSecManager { method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException; method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException; @@ -7256,68 +7125,6 @@ package android.net { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecTransform buildTunnelModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException; } - public class KeepalivePacketData { - ctor protected KeepalivePacketData(@NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull byte[]) throws android.net.InvalidPacketException; - method @NonNull public java.net.InetAddress getDstAddress(); - method public int getDstPort(); - method @NonNull public byte[] getPacket(); - method @NonNull public java.net.InetAddress getSrcAddress(); - method public int getSrcPort(); - } - - public class LinkAddress implements android.os.Parcelable { - ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int); - ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long); - ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); - ctor public LinkAddress(@NonNull String); - ctor public LinkAddress(@NonNull String, int, int); - method public long getDeprecationTime(); - method public long getExpirationTime(); - method public boolean isGlobalPreferred(); - method public boolean isIpv4(); - method public boolean isIpv6(); - method public boolean isSameAddressAs(@Nullable android.net.LinkAddress); - field public static final long LIFETIME_PERMANENT = 9223372036854775807L; // 0x7fffffffffffffffL - field public static final long LIFETIME_UNKNOWN = -1L; // 0xffffffffffffffffL - } - - public final class LinkProperties implements android.os.Parcelable { - ctor public LinkProperties(@Nullable android.net.LinkProperties); - ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean); - method public boolean addDnsServer(@NonNull java.net.InetAddress); - method public boolean addLinkAddress(@NonNull android.net.LinkAddress); - method public boolean addPcscfServer(@NonNull java.net.InetAddress); - method @NonNull public java.util.List<java.net.InetAddress> getAddresses(); - method @NonNull public java.util.List<java.lang.String> getAllInterfaceNames(); - method @NonNull public java.util.List<android.net.LinkAddress> getAllLinkAddresses(); - method @NonNull public java.util.List<android.net.RouteInfo> getAllRoutes(); - method @Nullable public android.net.Uri getCaptivePortalApiUrl(); - method @Nullable public android.net.CaptivePortalData getCaptivePortalData(); - method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers(); - method @Nullable public String getTcpBufferSizes(); - method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers(); - method public boolean hasGlobalIpv6Address(); - method public boolean hasIpv4Address(); - method public boolean hasIpv4DefaultRoute(); - method public boolean hasIpv4DnsServer(); - method public boolean hasIpv6DefaultRoute(); - method public boolean hasIpv6DnsServer(); - method public boolean isIpv4Provisioned(); - method public boolean isIpv6Provisioned(); - method public boolean isProvisioned(); - method public boolean isReachable(@NonNull java.net.InetAddress); - method public boolean removeDnsServer(@NonNull java.net.InetAddress); - method public boolean removeLinkAddress(@NonNull android.net.LinkAddress); - method public boolean removeRoute(@NonNull android.net.RouteInfo); - method public void setCaptivePortalApiUrl(@Nullable android.net.Uri); - method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData); - method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>); - method public void setPrivateDnsServerName(@Nullable String); - method public void setTcpBufferSizes(@Nullable String); - method public void setUsePrivateDns(boolean); - method public void setValidatedPrivateDnsServers(@NonNull java.util.Collection<java.net.InetAddress>); - } - public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { ctor public MatchAllNetworkSpecifier(); method public int describeContents(); @@ -7325,104 +7132,6 @@ package android.net { field @NonNull public static final android.os.Parcelable.Creator<android.net.MatchAllNetworkSpecifier> CREATOR; } - public final class NattKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable { - ctor public NattKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[]) throws android.net.InvalidPacketException; - method public int describeContents(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NattKeepalivePacketData> CREATOR; - } - - public class Network implements android.os.Parcelable { - ctor public Network(@NonNull android.net.Network); - method public int getNetId(); - method @NonNull public android.net.Network getPrivateDnsBypassingCopy(); - } - - public abstract class NetworkAgent { - ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider); - method @Nullable public android.net.Network getNetwork(); - method public void markConnected(); - method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData); - method public void onAutomaticReconnectDisabled(); - method public void onNetworkUnwanted(); - method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter); - method public void onQosCallbackUnregistered(int); - method public void onRemoveKeepalivePacketFilter(int); - method public void onSaveAcceptUnvalidated(boolean); - method public void onSignalStrengthThresholdsUpdated(@NonNull int[]); - method public void onStartSocketKeepalive(int, @NonNull java.time.Duration, @NonNull android.net.KeepalivePacketData); - method public void onStopSocketKeepalive(int); - method public void onValidationStatus(int, @Nullable android.net.Uri); - method @NonNull public android.net.Network register(); - method public final void sendLinkProperties(@NonNull android.net.LinkProperties); - method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities); - method public final void sendNetworkScore(@IntRange(from=0, to=99) int); - method public final void sendQosCallbackError(int, int); - method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes); - method public final void sendQosSessionLost(int, int); - method public final void sendSocketKeepaliveEvent(int, int); - method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>); - method public void unregister(); - field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2 - field public static final int VALIDATION_STATUS_VALID = 1; // 0x1 - } - - public final class NetworkAgentConfig implements android.os.Parcelable { - method public int describeContents(); - method public int getLegacyType(); - method @NonNull public String getLegacyTypeName(); - method public boolean isExplicitlySelected(); - method public boolean isPartialConnectivityAcceptable(); - method public boolean isUnvalidatedConnectivityAcceptable(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR; - } - - public static final class NetworkAgentConfig.Builder { - ctor public NetworkAgentConfig.Builder(); - method @NonNull public android.net.NetworkAgentConfig build(); - method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean); - method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int); - method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String); - method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean); - method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean); - } - - public final class NetworkCapabilities implements android.os.Parcelable { - ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean); - method @NonNull public int[] getAdministratorUids(); - method @Nullable public String getSsid(); - method @NonNull public int[] getTransportTypes(); - method public boolean isPrivateDnsBroken(); - method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); - field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c - field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 - field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a - field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18 - field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b - } - - public static final class NetworkCapabilities.Builder { - ctor public NetworkCapabilities.Builder(); - ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities); - method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int); - method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int); - method @NonNull public android.net.NetworkCapabilities build(); - method @NonNull public android.net.NetworkCapabilities.Builder clearAll(); - method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int); - method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]); - method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int); - method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int); - method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String); - method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo); - } - public class NetworkKey implements android.os.Parcelable { ctor public NetworkKey(android.net.WifiKey); method @Nullable public static android.net.NetworkKey createFromScanResult(@NonNull android.net.wifi.ScanResult); @@ -7434,15 +7143,6 @@ package android.net { field public final android.net.WifiKey wifiKey; } - public class NetworkProvider { - ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String); - method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest); - method public int getProviderId(); - method public void onNetworkRequestWithdrawn(@NonNull android.net.NetworkRequest); - method public void onNetworkRequested(@NonNull android.net.NetworkRequest, @IntRange(from=0, to=99) int, int); - field public static final int ID_NONE = -1; // 0xffffffff - } - public abstract class NetworkRecommendationProvider { ctor public NetworkRecommendationProvider(android.content.Context, java.util.concurrent.Executor); method public final android.os.IBinder getBinder(); @@ -7452,15 +7152,6 @@ package android.net { public class NetworkReleasedException extends java.lang.Exception { } - public class NetworkRequest implements android.os.Parcelable { - method @Nullable public String getRequestorPackageName(); - method public int getRequestorUid(); - } - - public static class NetworkRequest.Builder { - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int); - } - public class NetworkScoreManager { method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean clearScores() throws java.lang.SecurityException; method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public void disableScoring() throws java.lang.SecurityException; @@ -7585,16 +7276,6 @@ package android.net { field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSocketInfo> CREATOR; } - public final class RouteInfo implements android.os.Parcelable { - ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int); - ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int); - method public int getMtu(); - method public int getType(); - field public static final int RTN_THROW = 9; // 0x9 - field public static final int RTN_UNICAST = 1; // 0x1 - field public static final int RTN_UNREACHABLE = 7; // 0x7 - } - public class RssiCurve implements android.os.Parcelable { ctor public RssiCurve(int, int, byte[]); ctor public RssiCurve(int, int, byte[], int); @@ -7626,53 +7307,12 @@ package android.net { field public final android.net.RssiCurve rssiCurve; } - public abstract class SocketKeepalive implements java.lang.AutoCloseable { - field public static final int SUCCESS = 0; // 0x0 - } - public class SocketLocalAddressChangedException extends java.lang.Exception { } public class SocketNotBoundException extends java.lang.Exception { } - public final class StaticIpConfiguration implements android.os.Parcelable { - ctor public StaticIpConfiguration(); - ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); - method public void addDnsServer(@NonNull java.net.InetAddress); - method public void clear(); - method public int describeContents(); - method @NonNull public java.util.List<java.net.InetAddress> getDnsServers(); - method @Nullable public String getDomains(); - method @Nullable public java.net.InetAddress getGateway(); - method @Nullable public android.net.LinkAddress getIpAddress(); - method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR; - } - - public static final class StaticIpConfiguration.Builder { - ctor public StaticIpConfiguration.Builder(); - method @NonNull public android.net.StaticIpConfiguration build(); - method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>); - method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String); - method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress); - method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress); - } - - public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable { - ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException; - method public int describeContents(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR; - field public final int ipTos; - field public final int ipTtl; - field public final int tcpAck; - field public final int tcpSeq; - field public final int tcpWindow; - field public final int tcpWindowScale; - } - public class TrafficStats { method public static void setThreadStatsTagApp(); method public static void setThreadStatsTagBackup(); @@ -7685,11 +7325,6 @@ package android.net { field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00 } - public interface TransportInfo { - method public default boolean hasLocationSensitiveFields(); - method @NonNull public default android.net.TransportInfo makeCopy(boolean); - } - public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable { method @NonNull public String toSafeString(); } @@ -7713,23 +7348,6 @@ package android.net { } -package android.net.apf { - - public final class ApfCapabilities implements android.os.Parcelable { - ctor public ApfCapabilities(int, int, int); - method public int describeContents(); - method public static boolean getApfDrop8023Frames(); - method @NonNull public static int[] getApfEtherTypeBlackList(); - method public boolean hasDataAccess(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR; - field public final int apfPacketFormat; - field public final int apfVersionSupported; - field public final int maximumApfProgramSize; - } - -} - package android.net.metrics { @Deprecated public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event { @@ -7924,15 +7542,24 @@ package android.net.sip { } -package android.net.util { +package android.net.vcn { + + public class VcnManager { + method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.vcn.VcnNetworkPolicyResult applyVcnNetworkPolicy(@NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties); + method public void removeVcnNetworkPolicyListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener); + } - public final class SocketUtils { - method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException; - method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException; - method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); - method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int); - method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]); - method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]); + public static interface VcnManager.VcnNetworkPolicyListener { + method public void onPolicyChanged(); + } + + public final class VcnNetworkPolicyResult implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); + method public boolean isTeardownRequested(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.vcn.VcnNetworkPolicyResult> CREATOR; } } @@ -8284,7 +7911,7 @@ package android.os { public final class BugreportManager { method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence); - method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); + method @RequiresPermission(android.Manifest.permission.DUMP) @WorkerThread public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); } public final class BugreportParams { @@ -8576,7 +8203,7 @@ package android.os { method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long); method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend(); method @NonNull public android.os.BatterySaverPolicyConfig getFullPowerSavePolicy(); - method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveModeTrigger(); + method public int getPowerSaveModeTrigger(); method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplayAvailable(); method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressed(); method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressedForToken(@NonNull String); @@ -8606,11 +8233,18 @@ package android.os { method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String); method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>); method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String); - method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long); - method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String); + method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String); + method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long); + method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @Nullable String); + method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String); field public static final int EVENT_MMS = 2; // 0x2 field public static final int EVENT_SMS = 1; // 0x1 field public static final int EVENT_UNSPECIFIED = 0; // 0x0 + field public static final int REASON_ACTIVITY_RECOGNITION = 102; // 0x66 + field public static final int REASON_GEOFENCING = 100; // 0x64 + field public static final int REASON_OTHER = 1; // 0x1 + field public static final int REASON_PUSH_MESSAGING = 101; // 0x65 + field public static final int REASON_UNKNOWN = 0; // 0x0 field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0 field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1 } @@ -8759,13 +8393,13 @@ package android.os { method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @NonNull java.util.Set<java.lang.String>) throws android.os.UserManager.UserOperationException; method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getAllProfiles(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getEnabledProfiles(); - method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getRestrictedProfileParent(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType(); - method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserHandle> getUserHandles(boolean); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public long[] getSerialNumbersOfUsers(boolean); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.os.UserHandle> getUserHandles(boolean); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon(); method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle); @@ -9203,6 +8837,7 @@ package android.provider { field public static final String NAMESPACE_BIOMETRICS = "biometrics"; field public static final String NAMESPACE_BLOBSTORE = "blobstore"; field public static final String NAMESPACE_BLUETOOTH = "bluetooth"; + field public static final String NAMESPACE_CLIPBOARD = "clipboard"; field public static final String NAMESPACE_CONNECTIVITY = "connectivity"; field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture"; field @Deprecated public static final String NAMESPACE_DEX_BOOT = "dex_boot"; @@ -9217,6 +8852,7 @@ package android.provider { field public static final String NAMESPACE_PERMISSIONS = "permissions"; field public static final String NAMESPACE_PRIVACY = "privacy"; field public static final String NAMESPACE_PROFCOLLECT_NATIVE_BOOT = "profcollect_native_boot"; + field public static final String NAMESPACE_REBOOT_READINESS = "reboot_readiness"; field public static final String NAMESPACE_ROLLBACK = "rollback"; field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot"; field public static final String NAMESPACE_RUNTIME = "runtime"; @@ -9984,6 +9620,18 @@ package android.service.dataloader { } +package android.service.displayhash { + + public abstract class DisplayHasherService extends android.app.Service { + ctor public DisplayHasherService(); + method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); + method @Nullable public abstract void onGenerateDisplayHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String, @NonNull android.view.displayhash.DisplayHashResultCallback); + method @Nullable public abstract android.view.displayhash.VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[], @NonNull android.view.displayhash.DisplayHash); + field public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHasherService"; + } + +} + package android.service.euicc { public final class DownloadSubscriptionResult implements android.os.Parcelable { @@ -10168,6 +9816,7 @@ package android.service.notification { method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel); method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel, @NonNull android.service.notification.NotificationListenerService.RankingMap); method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean); + method public void onNotificationFeedbackReceived(@NonNull String, @NonNull android.service.notification.NotificationListenerService.RankingMap, @NonNull android.os.Bundle); method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String); method public void onNotificationVisibilityChanged(@NonNull String, boolean); method public void onNotificationsSeen(@NonNull java.util.List<java.lang.String>); @@ -10175,6 +9824,7 @@ package android.service.notification { method public void onPanelRevealed(int); method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int); method public final void unsnoozeNotification(@NonNull String); + field public static final String FEEDBACK_RATING = "feedback.rating"; field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; field public static final int SOURCE_FROM_APP = 0; // 0x0 field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1 @@ -10344,30 +9994,6 @@ package android.service.rotationresolver { } -package android.service.screenshot { - - public final class ScreenshotHash implements android.os.Parcelable { - ctor public ScreenshotHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]); - method public int describeContents(); - method @NonNull public android.graphics.Rect getBoundsInWindow(); - method @NonNull public String getHashingAlgorithm(); - method @NonNull public byte[] getHmac(); - method @NonNull public byte[] getImageHash(); - method public long getScreenshotTimeMillis(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.service.screenshot.ScreenshotHash> CREATOR; - } - - public abstract class ScreenshotHasherService extends android.app.Service { - ctor public ScreenshotHasherService(); - method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); - method @Nullable public abstract android.service.screenshot.ScreenshotHash onGenerateScreenshotHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String); - method public abstract boolean onVerifyScreenshotHash(@NonNull byte[], @NonNull android.service.screenshot.ScreenshotHash); - field public static final String SERVICE_INTERFACE = "android.service.screenshot.ScreenshotHasherService"; - } - -} - package android.service.search { public abstract class SearchUiService extends android.app.Service { @@ -10435,10 +10061,10 @@ package android.service.storage { public abstract class ExternalStorageService extends android.app.Service { ctor public ExternalStorageService(); + method public void onAnrDelayStarted(@NonNull String, int, int, int); method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onEndSession(@NonNull String) throws java.io.IOException; method public void onFreeCache(@NonNull java.util.UUID, long) throws java.io.IOException; - method public long onGetAnrDelayMillis(@NonNull String, int); method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException; method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException; field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2 @@ -10636,7 +10262,7 @@ package android.service.voice { public abstract class HotwordDetectionService extends android.app.Service { ctor public HotwordDetectionService(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); - method public void onDetectFromDspSource(int, @NonNull android.service.voice.HotwordDetectionService.DspHotwordDetectionCallback); + method public void onDetectFromDspSource(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, long, @NonNull android.service.voice.HotwordDetectionService.DspHotwordDetectionCallback); field public static final String SERVICE_INTERFACE = "android.service.voice.HotwordDetectionService"; } @@ -10771,7 +10397,7 @@ package android.telecom { method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle(); method @Nullable public final String getTelecomCallId(); method @Deprecated public void onAudioStateChanged(android.telecom.AudioState); - method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean); + method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(@NonNull android.telecom.Connection.CallFilteringCompletionInfo); method public final void resetConnectionTime(); method public void setCallDirection(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long); @@ -10787,6 +10413,16 @@ package android.telecom { field public static final int PROPERTY_REMOTELY_HOSTED = 2048; // 0x800 } + public static final class Connection.CallFilteringCompletionInfo implements android.os.Parcelable { + ctor public Connection.CallFilteringCompletionInfo(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, @Nullable android.content.ComponentName); + method public int describeContents(); + method @Nullable public android.telecom.CallScreeningService.CallResponse getCallResponse(); + method @Nullable public android.content.ComponentName getCallScreeningComponent(); + method public boolean isBlocked(); + method public boolean isInContacts(); + field @NonNull public static final android.os.Parcelable.Creator<android.telecom.Connection.CallFilteringCompletionInfo> CREATOR; + } + public final class ConnectionRequest implements android.os.Parcelable { method @Nullable public String getTelecomCallId(); } @@ -10948,7 +10584,7 @@ package android.telecom { } public final class RemoteConnection { - method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean); + method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(@NonNull android.telecom.Connection.CallFilteringCompletionInfo); method @Deprecated public void setAudioState(android.telecom.AudioState); } @@ -11129,6 +10765,10 @@ package android.telephony { field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool"; } + public static final class CarrierConfigManager.Ims { + field public static final String KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY = "ims.publish_service_desc_feature_tag_map_override_string_array"; + } + public static final class CarrierConfigManager.Wifi { field public static final String KEY_AVOID_5GHZ_SOFTAP_FOR_LAA_BOOL = "wifi.avoid_5ghz_softap_for_laa_bool"; field public static final String KEY_AVOID_5GHZ_WIFI_DIRECT_FOR_LAA_BOOL = "wifi.avoid_5ghz_wifi_direct_for_laa_bool"; @@ -11870,7 +11510,7 @@ package android.telephony { } public class TelephonyManager { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void bootstrapAuthenticationRequest(int, @NonNull android.net.Uri, @NonNull android.telephony.gba.UaSecurityProtocolIdentifier, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.BootstrapAuthenticationCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) @WorkerThread public void bootstrapAuthenticationRequest(int, @NonNull android.net.Uri, @NonNull android.telephony.gba.UaSecurityProtocolIdentifier, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.BootstrapAuthenticationCallback); method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult changeIccLockPin(@NonNull String, @NonNull String); method public int checkCarrierPrivilegesForPackage(String); @@ -12259,6 +11899,7 @@ package android.telephony.data { method public long getRetryDurationMillis(); method @Nullable public android.telephony.data.SliceInfo getSliceInfo(); method @Deprecated public int getSuggestedRetryTime(); + method @NonNull public java.util.List<android.telephony.data.TrafficDescriptor> getTrafficDescriptors(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR; field public static final int HANDOVER_FAILURE_MODE_DO_FALLBACK = 1; // 0x1 @@ -12294,6 +11935,7 @@ package android.telephony.data { method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long); method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo); method @Deprecated @NonNull public android.telephony.data.DataCallResponse.Builder setSuggestedRetryTime(int); + method @NonNull public android.telephony.data.DataCallResponse.Builder setTrafficDescriptors(@NonNull java.util.List<android.telephony.data.TrafficDescriptor>); } public final class DataProfile implements android.os.Parcelable { @@ -12364,7 +12006,7 @@ package android.telephony.data { method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback); method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback); method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @NonNull android.telephony.data.DataServiceCallback); - method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @NonNull android.telephony.data.DataServiceCallback); + method public void setupDataCall(int, @NonNull android.telephony.data.DataProfile, boolean, boolean, int, @Nullable android.net.LinkProperties, @IntRange(from=0, to=15) int, @Nullable android.telephony.data.SliceInfo, @Nullable android.telephony.data.TrafficDescriptor, boolean, @NonNull android.telephony.data.DataServiceCallback); } public class DataServiceCallback { @@ -12463,6 +12105,15 @@ package android.telephony.data { method @NonNull public android.telephony.data.ThrottleStatus.Builder setTransportType(int); } + public final class TrafficDescriptor implements android.os.Parcelable { + ctor public TrafficDescriptor(@Nullable String, @Nullable String); + method public int describeContents(); + method @Nullable public String getDnn(); + method @Nullable public String getOsAppId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.TrafficDescriptor> CREATOR; + } + } package android.telephony.euicc { @@ -12910,6 +12561,7 @@ package android.telephony.ims { field public static final String EXTRA_EMERGENCY_CALL = "e_call"; field public static final String EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED = "android.telephony.ims.extra.EXTENDING_TO_CONFERENCE_SUPPORTED"; field public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER"; + field public static final String EXTRA_IS_BUSINESS_CALL = "android.telephony.ims.extra.IS_BUSINESS_CALL"; field public static final String EXTRA_IS_CALL_PULL = "CallPull"; field public static final String EXTRA_IS_CROSS_SIM_CALL = "android.telephony.ims.extra.IS_CROSS_SIM_CALL"; field public static final String EXTRA_LOCATION = "android.telephony.ims.extra.LOCATION"; @@ -13318,19 +12970,19 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isRcsVolteSingleRegistrationCapable() throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isRcsVolteSingleRegistrationCapable() throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerRcsProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerRcsProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRcsClientConfiguration(@NonNull android.telephony.ims.RcsClientConfiguration) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void setRcsClientConfiguration(@NonNull android.telephony.ims.RcsClientConfiguration) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void triggerRcsReconfiguration(); + method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerRcsReconfiguration(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterRcsProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback); - field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE = "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE"; + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void unregisterRcsProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.RcsProvisioningCallback); + field @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE = "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE"; field public static final String EXTRA_STATUS = "android.telephony.ims.extra.STATUS"; field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.ims.extra.SUBSCRIPTION_ID"; field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 @@ -13595,10 +13247,10 @@ package android.telephony.ims { } public class SipDelegateManager { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException; - method public void triggerFullNetworkRegistration(@NonNull android.telephony.ims.SipDelegateConnection, @IntRange(from=100, to=699) int, @Nullable String); + method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int); + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isSupported() throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerFullNetworkRegistration(@NonNull android.telephony.ims.SipDelegateConnection, @IntRange(from=100, to=699) int, @Nullable String); field public static final int DENIED_REASON_INVALID = 4; // 0x4 field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1 field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2 @@ -13748,7 +13400,7 @@ package android.telephony.ims.stub { } public static interface CapabilityExchangeEventListener.OptionsRequestCallback { - method public default void onRespondToCapabilityRequest(@NonNull android.telephony.ims.RcsContactUceCapability, boolean); + method public void onRespondToCapabilityRequest(@NonNull android.telephony.ims.RcsContactUceCapability, boolean); method public void onRespondToCapabilityRequestWithError(@IntRange(from=100, to=699) int, @NonNull String); } @@ -14281,21 +13933,10 @@ package android.uwb { public final class UwbManager { method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos(); - method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getAngleOfArrivalSupport(); - method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxRemoteDevicesPerInitiatorSession(); - method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxRemoteDevicesPerResponderSession(); - method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxSimultaneousSessions(); method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo(); - method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public java.util.List<java.lang.Integer> getSupportedChannelNumbers(); - method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public java.util.Set<java.lang.Integer> getSupportedPreambleCodeIndices(); - method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public boolean isRangingSupported(); method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public AutoCloseable openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback); method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback); method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback); - field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2; // 0x2 - field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3; // 0x3 - field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4; // 0x4 - field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1; // 0x1 } public static interface UwbManager.AdapterStateCallback { @@ -14399,6 +14040,21 @@ package android.view.contentcapture { } +package android.view.displayhash { + + public final class DisplayHash implements android.os.Parcelable { + ctor public DisplayHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]); + method public int describeContents(); + method @NonNull public android.graphics.Rect getBoundsInWindow(); + method @NonNull public String getHashAlgorithm(); + method @NonNull public byte[] getHmac(); + method @NonNull public byte[] getImageHash(); + method public long getTimeMillis(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + } + +} + package android.view.translation { public final class UiTranslationManager { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index ff96f92da2f5..6ee57d6b0392 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -5,14 +5,17 @@ package android { field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS"; field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING"; field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS"; + field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA"; field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE"; field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE"; field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"; field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE"; field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA"; + field public static final String CLEAR_FREEZE_PERIOD = "android.permission.CLEAR_FREEZE_PERIOD"; field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS"; field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE"; + field public static final String FORCE_DEVICE_POLICY_MANAGER_LOGS = "android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS"; field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field public static final String KEEP_UNINSTALLED_PACKAGES = "android.permission.KEEP_UNINSTALLED_PACKAGES"; field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; @@ -28,6 +31,7 @@ package android { field public static final String QUERY_USERS = "android.permission.QUERY_USERS"; field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS"; field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE"; + field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO"; field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS"; field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS"; @@ -91,6 +95,8 @@ package android.app { method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener); method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName); method public long getTotalRam(); + method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidProcessCapabilities(int); + method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidProcessState(int); method public void holdLock(android.os.IBinder, int); method public static boolean isHighEndGfx(); method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener); @@ -100,13 +106,16 @@ package android.app { method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String); field public static final long DROP_CLOSE_SYSTEM_DIALOGS = 174664120L; // 0xa6929b8L field public static final long LOCK_DOWN_CLOSE_SYSTEM_DIALOGS = 174664365L; // 0xa692aadL - field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7 + field public static final int PROCESS_CAPABILITY_ALL = 15; // 0xf field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1 field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6 field public static final int PROCESS_CAPABILITY_FOREGROUND_CAMERA = 2; // 0x2 field public static final int PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1; // 0x1 field public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 4; // 0x4 + field public static final int PROCESS_CAPABILITY_NETWORK = 8; // 0x8 field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0 + field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4 + field public static final int PROCESS_STATE_TOP = 2; // 0x2 } public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable { @@ -300,6 +309,7 @@ package android.app { method public void clickNotification(@Nullable String, int, int, boolean); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void collapsePanels(); method public void expandNotificationsPanel(); + method public void sendNotificationFeedback(@Nullable String, @Nullable android.os.Bundle); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean); } @@ -383,7 +393,11 @@ package android.app.admin { public class DevicePolicyManager { method public int checkProvisioningPreCondition(@Nullable String, @NonNull String); + method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord(); method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException; + method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs(); + method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int); + method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs(); method public void forceUpdateUserSetupComplete(); method public long getLastBugReportRequestTime(); method public long getLastNetworkLogRetrievalTime(); @@ -391,10 +405,13 @@ package android.app.admin { method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle); method public boolean isCurrentInputMethodSetByOwner(); method public boolean isFactoryResetProtectionPolicySupported(); + method @RequiresPermission(anyOf={"android.permission.MARK_DEVICE_ORGANIZATION_OWNED", "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName); method @NonNull public static String operationSafetyReasonToString(int); method @NonNull public static String operationToString(int); method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException; 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_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 int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6 @@ -547,6 +564,16 @@ package android.app.blob { } +package android.app.contentsuggestions { + + public final class ContentSuggestionsManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS) public void resetTemporaryService(int); + method @RequiresPermission(android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS) public void setDefaultServiceEnabled(int, boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS) public void setTemporaryService(int, @NonNull String, int); + } + +} + package android.app.prediction { public final class AppPredictor { @@ -579,6 +606,14 @@ package android.app.usage { } +package android.appwidget { + + public class AppWidgetManager { + method public void setBindAppWidgetPermission(@NonNull String, int, boolean); + } + +} + package android.bluetooth { public final class BluetoothClass implements android.os.Parcelable { @@ -672,6 +707,10 @@ package android.content.pm { field public int privateFlags; } + public class CrossProfileApps { + method public boolean canConfigureInteractAcrossProfiles(@NonNull String); + } + public class LauncherApps { ctor public LauncherApps(android.content.Context); } @@ -695,8 +734,10 @@ package android.content.pm { method public void holdLock(android.os.IBinder, int); method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>); field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage"; + field public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle"; field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption"; field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec"; + field public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle"; field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80 field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000 field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services"; @@ -723,6 +764,65 @@ package android.content.pm { ctor public ShortcutManager(android.content.Context); } + public class UserInfo implements android.os.Parcelable { + ctor public UserInfo(int, String, int); + ctor public UserInfo(int, String, String, int); + ctor public UserInfo(int, String, String, int, String); + ctor @Deprecated public UserInfo(); + ctor public UserInfo(android.content.pm.UserInfo); + method public boolean canHaveProfile(); + method public int describeContents(); + method public android.os.UserHandle getUserHandle(); + method public boolean isAdmin(); + method public boolean isDemo(); + method public boolean isEnabled(); + method public boolean isEphemeral(); + method public boolean isFull(); + method public boolean isGuest(); + method public boolean isInitialized(); + method public boolean isManagedProfile(); + method public boolean isPrimary(); + method public boolean isProfile(); + method public boolean isQuietModeEnabled(); + method public boolean isRestricted(); + method public boolean isSystemOnly(); + method public static boolean isSystemOnly(int); + method public boolean supportsSwitchTo(); + method public boolean supportsSwitchToByUser(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserInfo> CREATOR; + field public static final int FLAG_ADMIN = 2; // 0x2 + field @Deprecated public static final int FLAG_DEMO = 512; // 0x200 + field public static final int FLAG_DISABLED = 64; // 0x40 + field public static final int FLAG_EPHEMERAL = 256; // 0x100 + field public static final int FLAG_FULL = 1024; // 0x400 + field @Deprecated public static final int FLAG_GUEST = 4; // 0x4 + field public static final int FLAG_INITIALIZED = 16; // 0x10 + field @Deprecated public static final int FLAG_MANAGED_PROFILE = 32; // 0x20 + field public static final int FLAG_PRIMARY = 1; // 0x1 + field public static final int FLAG_PROFILE = 4096; // 0x1000 + field public static final int FLAG_QUIET_MODE = 128; // 0x80 + field @Deprecated public static final int FLAG_RESTRICTED = 8; // 0x8 + field public static final int FLAG_SYSTEM = 2048; // 0x800 + field public static final int NO_PROFILE_GROUP_ID = -10000; // 0xffffd8f0 + field public boolean convertedFromPreCreated; + field public long creationTime; + field public int flags; + field public boolean guestToRemove; + field public String iconPath; + field public int id; + field public String lastLoggedInFingerprint; + field public long lastLoggedInTime; + field public String name; + field public boolean partial; + field public boolean preCreated; + field public int profileBadge; + field public int profileGroupId; + field public int restrictedProfileParentId; + field public int serialNumber; + field public String userType; + } + } package android.content.res { @@ -836,7 +936,7 @@ package android.graphics.drawable { package android.graphics.fonts { public class FontManager { - method @Nullable public android.text.FontConfig getFontConfig(); + method @NonNull public android.text.FontConfig getFontConfig(); method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int); method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.graphics.fonts.FontFileUpdateRequest, @IntRange(from=0) int); method @Deprecated @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFile(@NonNull android.os.ParcelFileDescriptor, @NonNull byte[], @IntRange(from=0) int); @@ -857,11 +957,21 @@ package android.graphics.fonts { package android.hardware { public final class SensorPrivacyManager { - method public boolean isIndividualSensorPrivacyEnabled(int); - method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setIndividualSensorPrivacy(int, boolean); - method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setIndividualSensorPrivacyForProfileGroup(int, boolean); - field public static final int INDIVIDUAL_SENSOR_CAMERA = 2; // 0x2 - field public static final int INDIVIDUAL_SENSOR_MICROPHONE = 1; // 0x1 + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int); + method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener); + method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyForProfileGroup(int, boolean); + } + + public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener { + method public void onSensorPrivacyChanged(boolean); + } + + public static class SensorPrivacyManager.Sensors { + field public static final int CAMERA = 2; // 0x2 + field public static final int MICROPHONE = 1; // 0x1 } } @@ -911,17 +1021,19 @@ package android.hardware.camera2 { package android.hardware.devicestate { public final class DeviceStateManager { - method public void addDeviceStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener); method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest); method @NonNull public int[] getSupportedStates(); - method public void removeDeviceStateListener(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener); + method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback); method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback); + method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback); field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0 } - public static interface DeviceStateManager.DeviceStateListener { - method public void onDeviceStateChanged(int); + public static interface DeviceStateManager.DeviceStateCallback { + method public default void onBaseStateChanged(int); + method public void onStateChanged(int); + method public default void onSupportedStatesChanged(@NonNull int[]); } public final class DeviceStateRequest { @@ -1000,14 +1112,6 @@ package android.hardware.input { } -package android.hardware.lights { - - public final class LightsManager { - method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light); - } - -} - package android.hardware.soundtrigger { public class KeyphraseEnrollmentInfo { @@ -1484,8 +1588,15 @@ package android.os { } public class UserManager { + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createProfileForUser(@Nullable String, @NonNull String, int, int, @Nullable String[]); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public String getUserType(); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle); + method public static boolean isGuestUserEphemeral(); method public static boolean isSplitSystemUser(); + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException; } public final class VibrationAttributes implements android.os.Parcelable { @@ -1639,6 +1750,7 @@ package android.os.storage { public class StorageManager { method @NonNull public static java.util.UUID convert(@NonNull String); method @NonNull public static String convert(@NonNull java.util.UUID); + method public static boolean isUserKeyUnlocked(int); } public final class StorageVolume implements android.os.Parcelable { @@ -2464,6 +2576,9 @@ package android.view.autofill { package android.view.contentcapture { public final class ContentCaptureManager { + method @RequiresPermission(android.Manifest.permission.MANAGE_CONTENT_CAPTURE) public static void resetTemporaryService(int); + method @RequiresPermission(android.Manifest.permission.MANAGE_CONTENT_CAPTURE) public static void setDefaultServiceEnabled(int, boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_CONTENT_CAPTURE) public static void setTemporaryService(int, @NonNull String, int); field public static final String DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY = "idle_flush_frequency"; field public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level"; field public static final String DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE = "log_history_size"; diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index 87fb5b1b3b2e..a536efb2b488 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -468,7 +468,7 @@ GetterSetterNames: android.location.GnssMeasurement#setBasebandCn0DbHz(double): GetterSetterNames: android.location.GnssMeasurement#setCarrierFrequencyHz(float): GetterSetterNames: android.location.GnssMeasurement#setCodeType(String): - + GetterSetterNames: android.location.GnssMeasurement#setCorrelationVectors(java.util.Collection<android.location.CorrelationVector>): GetterSetterNames: android.location.GnssMeasurement#setFullInterSignalBiasNanos(double): @@ -480,7 +480,7 @@ GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasN GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasUncertaintyNanos(double): GetterSetterNames: android.location.GnssMeasurement#setSatellitePvt(android.location.SatellitePvt): - + GetterSetterNames: android.location.GnssMeasurement#setSnrInDb(double): GetterSetterNames: android.location.LocationRequest#isLocationSettingsIgnored(): @@ -953,6 +953,32 @@ MissingNullability: android.content.pm.PackageManager#holdLock(android.os.IBinde MissingNullability: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context) parameter #0: +MissingNullability: android.content.pm.UserInfo#UserInfo(android.content.pm.UserInfo) parameter #0: + Missing nullability on parameter `orig` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #1: + Missing nullability on parameter `name` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #2: + Missing nullability on parameter `iconPath` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #1: + Missing nullability on parameter `name` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #2: + Missing nullability on parameter `iconPath` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #4: + Missing nullability on parameter `userType` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, int) parameter #1: + Missing nullability on parameter `name` in method `UserInfo` +MissingNullability: android.content.pm.UserInfo#getUserHandle(): + Missing nullability on method `getUserHandle` return +MissingNullability: android.content.pm.UserInfo#iconPath: + Missing nullability on field `iconPath` in class `class android.content.pm.UserInfo` +MissingNullability: android.content.pm.UserInfo#lastLoggedInFingerprint: + Missing nullability on field `lastLoggedInFingerprint` in class `class android.content.pm.UserInfo` +MissingNullability: android.content.pm.UserInfo#name: + Missing nullability on field `name` in class `class android.content.pm.UserInfo` +MissingNullability: android.content.pm.UserInfo#userType: + Missing nullability on field `userType` in class `class android.content.pm.UserInfo` +MissingNullability: android.content.pm.UserInfo#writeToParcel(android.os.Parcel, int) parameter #0: + Missing nullability on parameter `dest` in method `writeToParcel` MissingNullability: android.content.res.AssetManager#getOverlayablesToString(String) parameter #0: MissingNullability: android.content.res.Configuration#windowConfiguration: @@ -2433,6 +2459,38 @@ MutableBareField: android.content.AutofillOptions#disabledActivities: MutableBareField: android.content.AutofillOptions#whitelistedActivitiesForAugmentedAutofill: +MutableBareField: android.content.pm.UserInfo#convertedFromPreCreated: + Bare field convertedFromPreCreated must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#creationTime: + Bare field creationTime must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#flags: + Bare field flags must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#guestToRemove: + Bare field guestToRemove must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#iconPath: + Bare field iconPath must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#id: + Bare field id must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#lastLoggedInFingerprint: + Bare field lastLoggedInFingerprint must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#lastLoggedInTime: + Bare field lastLoggedInTime must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#name: + Bare field name must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#partial: + Bare field partial must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#preCreated: + Bare field preCreated must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#profileBadge: + Bare field profileBadge must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#profileGroupId: + Bare field profileGroupId must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#restrictedProfileParentId: + Bare field restrictedProfileParentId must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#serialNumber: + Bare field serialNumber must be marked final, or moved behind accessors if mutable +MutableBareField: android.content.pm.UserInfo#userType: + Bare field userType must be marked final, or moved behind accessors if mutable MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#cache: MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#dbName: @@ -2548,15 +2606,15 @@ NoSettingsProvider: android.provider.Settings.Global#TETHER_OFFLOAD_DISABLED: NoSettingsProvider: android.provider.Settings.Global#USE_OPEN_WIFI_PACKAGE: NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED: - + NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY: - + NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE: - + NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_ALL: - + NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN: - + NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW: NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE: @@ -2619,6 +2677,10 @@ NotCloseable: android.telephony.ims.stub.ImsUtImplBase: +NullableCollection: android.os.UserManager#createProfileForUser(String, String, int, int, String[]) parameter #4: + Type of parameter disallowedPackages in android.os.UserManager.createProfileForUser(String name, String userType, int flags, int userId, String[] disallowedPackages) is a nullable collection (`java.lang.String[]`); must be non-null + + OnNameExpected: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.PrintWriter, String[]): OnNameExpected: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]): @@ -2729,6 +2791,8 @@ ParcelCreator: android.service.autofill.InternalValidator: ParcelNotFinal: android.app.WindowConfiguration: +ParcelNotFinal: android.content.pm.UserInfo: + Parcelable classes must be final: android.content.pm.UserInfo is not final ParcelNotFinal: android.net.metrics.IpConnectivityLog.Event: ParcelNotFinal: android.os.IncidentManager.IncidentReport: diff --git a/core/java/Android.bp b/core/java/Android.bp index af5df769ffad..2fdf9c146ff6 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -1,3 +1,14 @@ +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 + // SPDX-license-identifier-BSD + // legacy_unencumbered + default_applicable_licenses: ["frameworks_base_license"], +} + filegroup { name: "IKeyAttestationApplicationIdProvider.aidl", srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"], diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 992d054737b9..a73fe71cfe1a 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -156,6 +156,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -3811,6 +3812,22 @@ public class Activity extends ContextThemeWrapper return false; } + private static final class RequestFinishCallback extends IRequestFinishCallback.Stub { + private final WeakReference<Activity> mActivityRef; + + RequestFinishCallback(WeakReference<Activity> activityRef) { + mActivityRef = activityRef; + } + + @Override + public void requestFinish() { + Activity activity = mActivityRef.get(); + if (activity != null) { + activity.mHandler.post(activity::finishAfterTransition); + } + } + } + /** * Called when the activity has detected the user's press of the back * key. The default implementation simply finishes the current activity, @@ -3834,7 +3851,8 @@ public class Activity extends ContextThemeWrapper // Inform activity task manager that the activity received a back press while at the // root of the task. This call allows ActivityTaskManager to intercept or move the task // to the back. - ActivityClient.getInstance().onBackPressedOnTaskRoot(mToken); + ActivityClient.getInstance().onBackPressedOnTaskRoot(mToken, + new RequestFinishCallback(new WeakReference<>(this))); // Activity was launched when user tapped a link in the Autofill Save UI - Save UI must // be restored now. diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java index e3b5e9a32324..fbabfac706e1 100644 --- a/core/java/android/app/ActivityClient.java +++ b/core/java/android/app/ActivityClient.java @@ -480,9 +480,9 @@ public class ActivityClient { } } - void onBackPressedOnTaskRoot(IBinder token) { + void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) { try { - getActivityClientController().onBackPressedOnTaskRoot(token); + getActivityClientController().onBackPressedOnTaskRoot(token, callback); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/ActivityManager.aidl b/core/java/android/app/ActivityManager.aidl index 341393c0d969..eb7cac076822 100644 --- a/core/java/android/app/ActivityManager.aidl +++ b/core/java/android/app/ActivityManager.aidl @@ -17,6 +17,7 @@ package android.app; parcelable ActivityManager.MemoryInfo; +parcelable ActivityManager.PendingIntentInfo; parcelable ActivityManager.ProcessErrorStateInfo; parcelable ActivityManager.RecentTaskInfo; parcelable ActivityManager.TaskDescription; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index e2426d116319..f905ec86aab7 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -511,6 +511,7 @@ public class ActivityManager { /** @hide Process is hosting the current top activities. Note that this covers * all activities that are visible to the user. */ @UnsupportedAppUsage + @TestApi public static final int PROCESS_STATE_TOP = ProcessStateEnum.TOP; /** @hide Process is bound to a TOP app. */ @@ -518,6 +519,7 @@ public class ActivityManager { /** @hide Process is hosting a foreground service. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @TestApi public static final int PROCESS_STATE_FOREGROUND_SERVICE = ProcessStateEnum.FOREGROUND_SERVICE; /** @hide Process is hosting a foreground service due to a system binding. */ @@ -616,11 +618,16 @@ public class ActivityManager { @TestApi public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 2; + /** @hide Process can access network despite any power saving resrictions */ + @TestApi + public static final int PROCESS_CAPABILITY_NETWORK = 1 << 3; + /** @hide all capabilities, the ORing of all flags in {@link ProcessCapability}*/ @TestApi public static final int PROCESS_CAPABILITY_ALL = PROCESS_CAPABILITY_FOREGROUND_LOCATION | PROCESS_CAPABILITY_FOREGROUND_CAMERA - | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; + | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE + | PROCESS_CAPABILITY_NETWORK; /** * All explicit capabilities. These are capabilities that need to be specified from manifest * file. @@ -646,6 +653,15 @@ public class ActivityManager { pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0 ? 'L' : '-'); pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0 ? 'C' : '-'); pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0 ? 'M' : '-'); + pw.print((caps & PROCESS_CAPABILITY_NETWORK) != 0 ? 'N' : '-'); + } + + /** @hide */ + public static void printCapabilitiesSummary(StringBuilder sb, @ProcessCapability int caps) { + sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0 ? 'L' : '-'); + sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0 ? 'C' : '-'); + sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0 ? 'M' : '-'); + sb.append((caps & PROCESS_CAPABILITY_NETWORK) != 0 ? 'N' : '-'); } /** @@ -656,13 +672,21 @@ public class ActivityManager { printCapabilitiesSummary(pw, caps); final int remain = caps & ~(PROCESS_CAPABILITY_FOREGROUND_LOCATION | PROCESS_CAPABILITY_FOREGROUND_CAMERA - | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE); + | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE + | PROCESS_CAPABILITY_NETWORK); if (remain != 0) { pw.print('+'); pw.print(remain); } } + /** @hide */ + public static String getCapabilitiesSummary(@ProcessCapability int caps) { + final StringBuilder sb = new StringBuilder(); + printCapabilitiesSummary(sb, caps); + return sb.toString(); + } + // NOTE: If PROCESS_STATEs are added, then new fields must be added // to frameworks/base/core/proto/android/app/enums.proto and the following method must // be updated to correctly map between them. @@ -3411,6 +3435,36 @@ public class ActivityManager { } /** + * Returns the process state of this uid. + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) + public int getUidProcessState(int uid) { + try { + return getService().getUidProcessState(uid, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the process capability of this uid. + * + * @hide + */ + @TestApi + @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) + public @ProcessCapability int getUidProcessCapabilities(int uid) { + try { + return getService().getUidProcessCapabilities(uid, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Return the importance of a given package name, based on the processes that are * currently running. The return value is one of the importance constants defined * in {@link RunningAppProcessInfo}, giving you the highest importance of all the @@ -4485,6 +4539,80 @@ public class ActivityManager { } } + /** @hide */ + public static String procStateToString(int procState) { + final String procStateStr; + switch (procState) { + case ActivityManager.PROCESS_STATE_PERSISTENT: + procStateStr = "PER "; + break; + case ActivityManager.PROCESS_STATE_PERSISTENT_UI: + procStateStr = "PERU"; + break; + case ActivityManager.PROCESS_STATE_TOP: + procStateStr = "TOP "; + break; + case ActivityManager.PROCESS_STATE_BOUND_TOP: + procStateStr = "BTOP"; + break; + case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE: + procStateStr = "FGS "; + break; + case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE: + procStateStr = "BFGS"; + break; + case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: + procStateStr = "IMPF"; + break; + case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: + procStateStr = "IMPB"; + break; + case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND: + procStateStr = "TRNB"; + break; + case ActivityManager.PROCESS_STATE_BACKUP: + procStateStr = "BKUP"; + break; + case ActivityManager.PROCESS_STATE_SERVICE: + procStateStr = "SVC "; + break; + case ActivityManager.PROCESS_STATE_RECEIVER: + procStateStr = "RCVR"; + break; + case ActivityManager.PROCESS_STATE_TOP_SLEEPING: + procStateStr = "TPSL"; + break; + case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: + procStateStr = "HVY "; + break; + case ActivityManager.PROCESS_STATE_HOME: + procStateStr = "HOME"; + break; + case ActivityManager.PROCESS_STATE_LAST_ACTIVITY: + procStateStr = "LAST"; + break; + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: + procStateStr = "CAC "; + break; + case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: + procStateStr = "CACC"; + break; + case ActivityManager.PROCESS_STATE_CACHED_RECENT: + procStateStr = "CRE "; + break; + case ActivityManager.PROCESS_STATE_CACHED_EMPTY: + procStateStr = "CEM "; + break; + case ActivityManager.PROCESS_STATE_NONEXISTENT: + procStateStr = "NONE"; + break; + default: + procStateStr = "??"; + break; + } + return procStateStr; + } + /** * The AppTask allows you to manage your own application's tasks. * See {@link android.app.ActivityManager#getAppTasks()} @@ -4681,4 +4809,71 @@ public class ActivityManager { throw e.rethrowFromSystemServer(); } } + + /** + * A subset of immutable pending intent information suitable for caching on the client side. + * + * @hide + */ + public static final class PendingIntentInfo implements Parcelable { + + private final String mCreatorPackage; + private final int mCreatorUid; + private final boolean mImmutable; + private final int mIntentSenderType; + + public PendingIntentInfo(String creatorPackage, int creatorUid, boolean immutable, + int intentSenderType) { + mCreatorPackage = creatorPackage; + mCreatorUid = creatorUid; + mImmutable = immutable; + mIntentSenderType = intentSenderType; + } + + public String getCreatorPackage() { + return mCreatorPackage; + } + + public int getCreatorUid() { + return mCreatorUid; + } + + public boolean isImmutable() { + return mImmutable; + } + + public int getIntentSenderType() { + return mIntentSenderType; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel parcel, int flags) { + parcel.writeString(mCreatorPackage); + parcel.writeInt(mCreatorUid); + parcel.writeBoolean(mImmutable); + parcel.writeInt(mIntentSenderType); + } + + public static final @NonNull Creator<PendingIntentInfo> CREATOR = + new Creator<PendingIntentInfo>() { + @Override + public PendingIntentInfo createFromParcel(Parcel in) { + return new PendingIntentInfo( + /* creatorPackage= */ in.readString(), + /* creatorUid= */ in.readInt(), + /* immutable= */ in.readBoolean(), + /* intentSenderType= */ in.readInt()); + } + + @Override + public PendingIntentInfo[] newArray(int size) { + return new PendingIntentInfo[size]; + } + }; + } } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index c31c22cca329..c812e8e1782a 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -30,6 +30,7 @@ import android.content.pm.UserInfo; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.PowerWhitelistManager.TempAllowListType; import android.os.TransactionTooLargeException; import android.os.WorkSource; @@ -102,12 +103,15 @@ public abstract class ActivityManagerInternal { * Sets how long a {@link PendingIntent} can be temporarily allowlisted to bypass restrictions * such as Power Save mode. * @param target - * @param whitelistToken + * @param allowlistToken * @param duration temp allowlist duration in milliseconds. * @param type temp allowlist type defined at {@link TempAllowListType} + * @param reasonCode one of {@link ReasonCode} + * @param reason A human-readable reason for logging purposes. */ - public abstract void setPendingIntentWhitelistDuration(IIntentSender target, - IBinder whitelistToken, long duration, int type); + public abstract void setPendingIntentAllowlistDuration(IIntentSender target, + IBinder allowlistToken, long duration, @TempAllowListType int type, + @ReasonCode int reasonCode, @Nullable String reason); /** * Returns the flags set for a {@link PendingIntent}. @@ -127,20 +131,26 @@ public abstract class ActivityManagerInternal { IBinder allowlistToken); /** - * Allow DeviceIdleController to tell us about what apps are whitelisted. + * Allow DeviceIdleController to tell us about what apps are allowlisted. */ - public abstract void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids); + public abstract void setDeviceIdleAllowlist(int[] allAppids, int[] exceptIdleAppids); /** - * Update information about which app IDs are on the temp whitelist. + * Update information about which app IDs are on the temp allowlist. * @param appids the updated list of appIds in temp allowlist. * @param changingUid uid to add or remove to temp allowlist. * @param adding true to add to temp allowlist, false to remove from temp allowlist. * @param durationMs when adding is true, the duration to be in temp allowlist. * @param type temp allowlist type defined at {@link TempAllowListType}. + * @param reasonCode one of {@link ReasonCode} + * @param reason A human-readable reason for logging purposes. + * @param callingUid the callingUid that setup this temp allowlist, only valid when param adding + * is true. */ - public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, - boolean adding, long durationMs, @TempAllowListType int type); + public abstract void updateDeviceIdleTempAllowlist(int[] appids, int changingUid, + boolean adding, long durationMs, @TempAllowListType int type, + @ReasonCode int reasonCode, + @Nullable String reason, int callingUid); /** * Get the procstate for the UID. The return value will be between @@ -335,10 +345,11 @@ public abstract class ActivityManagerInternal { * @param targetUid the UID that is been temp allowlisted. * @param duration temp allowlist duration in milliseconds. * @param type temp allowlist type defined at {@link TempAllowListType} - * @param tag + * @param reasonCode one of {@link ReasonCode} + * @param reason */ - public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid, - long duration, int type, String tag); + public abstract void tempAllowlistForPendingIntent(int callerPid, int callerUid, int targetUid, + long duration, int type, @ReasonCode int reasonCode, String reason); public abstract int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid, int realCallingUid, int realCallingPid, Intent intent, String resolvedType, @@ -495,9 +506,9 @@ public abstract class ActivityManagerInternal { /** * Sends a broadcast, assuming the caller to be the system and allowing the inclusion of an - * approved whitelist of app Ids >= {@link android.os.Process#FIRST_APPLICATION_UID} that the + * approved allowlist of app Ids >= {@link android.os.Process#FIRST_APPLICATION_UID} that the * broadcast my be sent to; any app Ids < {@link android.os.Process#FIRST_APPLICATION_UID} are - * automatically whitelisted. + * automatically allowlisted. * * @see com.android.server.am.ActivityManagerService#broadcastIntentWithFeature( * IApplicationThread, String, Intent, String, IIntentReceiver, int, String, Bundle, diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 73cc13c82bcb..a1dce77c9191 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -320,6 +320,10 @@ public class ActivityOptions { private static final String KEY_OVERRIDE_TASK_TRANSITION = "android:activity.overrideTaskTransition"; + /** See {@link #setRemoveWithTaskOrganizer(boolean)}. */ + private static final String KEY_REMOVE_WITH_TASK_ORGANIZER = + "android.activity.removeWithTaskOrganizer"; + /** * @see #setLaunchCookie * @hide @@ -405,6 +409,7 @@ public class ActivityOptions { private IRemoteTransition mRemoteTransition; private boolean mOverrideTaskTransition; private int mSplashScreenThemeResId; + private boolean mRemoveWithTaskOrganizer; /** * Create an ActivityOptions specifying a custom animation to run when @@ -1155,6 +1160,7 @@ public class ActivityOptions { KEY_REMOTE_TRANSITION)); mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION); mSplashScreenThemeResId = opts.getInt(KEY_SPLASH_SCREEN_THEME); + mRemoveWithTaskOrganizer = opts.getBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER); } /** @@ -1624,6 +1630,22 @@ public class ActivityOptions { } /** + * Sets whether to remove the task when TaskOrganizer, which is managing it, is destroyed. + * @hide + */ + public void setRemoveWithTaskOrganizer(boolean remove) { + mRemoveWithTaskOrganizer = remove; + } + + /** + * @return whether to remove the task when TaskOrganizer, which is managing it, is destroyed. + * @hide + */ + public boolean getRemoveWithTaskOranizer() { + return mRemoveWithTaskOrganizer; + } + + /** * Update the current values in this ActivityOptions from those supplied * in <var>otherOptions</var>. Any values * defined in <var>otherOptions</var> replace those in the base options. @@ -1857,6 +1879,9 @@ public class ActivityOptions { if (mSplashScreenThemeResId != 0) { b.putInt(KEY_SPLASH_SCREEN_THEME, mSplashScreenThemeResId); } + if (mRemoveWithTaskOrganizer) { + b.putBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER, mRemoveWithTaskOrganizer); + } return b; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 3d9f6123963f..47d2e7cee65a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5141,7 +5141,7 @@ public final class ActivityThread extends ClientTransactionHandler { private void onCoreSettingsChange() { if (updateDebugViewAttributeState()) { // request all activities to relaunch for the changes to take place - relaunchAllActivities(false /* preserveWindows */); + relaunchAllActivities(false /* preserveWindows */, "onCoreSettingsChange"); } } @@ -5160,7 +5160,8 @@ public final class ActivityThread extends ClientTransactionHandler { return previousState != View.sDebugViewAttributes; } - private void relaunchAllActivities(boolean preserveWindows) { + private void relaunchAllActivities(boolean preserveWindows, String reason) { + Log.i(TAG, "Relaunch all activities: " + reason); for (int i = mActivities.size() - 1; i >= 0; i--) { scheduleRelaunchActivityIfPossible(mActivities.valueAt(i), preserveWindows); } @@ -5535,6 +5536,7 @@ public final class ActivityThread extends ClientTransactionHandler { void scheduleRelaunchActivity(IBinder token) { final ActivityClientRecord r = mActivities.get(token); if (r != null) { + Log.i(TAG, "Schedule relaunch activity: " + r.activityInfo.name); scheduleRelaunchActivityIfPossible(r, !r.stopped /* preserveWindow */); } } @@ -6079,7 +6081,7 @@ public final class ActivityThread extends ClientTransactionHandler { handleConfigurationChanged(newConfig, null); // Preserve windows to avoid black flickers when overlays change. - relaunchAllActivities(true /* preserveWindows */); + relaunchAllActivities(true /* preserveWindows */, "handleApplicationInfoChanged"); } static void freeTextLayoutCachesIfNeeded(int configDiff) { diff --git a/core/java/android/app/AnrController.java b/core/java/android/app/AnrController.java index cfc9d2715720..a0d4b3a6a753 100644 --- a/core/java/android/app/AnrController.java +++ b/core/java/android/app/AnrController.java @@ -23,7 +23,29 @@ package android.app; public interface AnrController { /** * Returns the delay in milliseconds for an ANR dialog that is about to be shown for - * {@code packageName}. + * {@code packageName} with {@code uid}. + * + * Implementations should only return a positive value if they actually expect the + * {@code packageName} to be delayed due to them. + + * If there are multiple controllers registered, the controller with the max delay will + * be selected and will receive an {@link #onAnrDelayStarted} callback at the start of the + * delay and an {@link #onAnrDelayCompleted} at the end of the delay. */ long getAnrDelayMillis(String packageName, int uid); + + /** + * Notifies the controller at the start of the ANR dialog delay for {@code packageName} with + * {@code uid}. The controller can decide to show a progress UI after this notification. + */ + void onAnrDelayStarted(String packageName, int uid); + + /** + * Notifies the controller at the end of the ANR dialog delay for {@code packageName} with + * {@code uid}. + * + * @return whether the ANR dialog should be shown or cancelled. {@code true} if the + * ANR dialog should be shown, {@code false} if it should be cancelled. + */ + boolean onAnrDelayCompleted(String packageName, int uid); } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index b85b1861aa02..160844aacc46 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -795,7 +795,8 @@ public class AppOpsManager { // when adding one of these: // - increment _NUM_OP // - define an OPSTR_* constant (marked as @SystemApi) - // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpToPerms, sOpDefault + // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode, sOpDisableReset, + // sOpRestrictions, sOpAllowSystemRestrictionBypass // - add descriptive strings to Settings/res/values/arrays.xml // - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app) @@ -1140,23 +1141,20 @@ public class AppOpsManager { * * @hide */ - // TODO: Add as AppProtoEnums - public static final int OP_PHONE_CALL_MICROPHONE = 100; + public static final int OP_PHONE_CALL_MICROPHONE = AppProtoEnums.APP_OP_PHONE_CALL_MICROPHONE; /** * Phone call is using camera * * @hide */ - // TODO: Add as AppProtoEnums - public static final int OP_PHONE_CALL_CAMERA = 101; + public static final int OP_PHONE_CALL_CAMERA = AppProtoEnums.APP_OP_PHONE_CALL_CAMERA; /** * Audio is being recorded for hotword detection. * * @hide */ - // TODO: Add as AppProtoEnums - public static final int OP_RECORD_AUDIO_HOTWORD = 102; + public static final int OP_RECORD_AUDIO_HOTWORD = AppProtoEnums.APP_OP_RECORD_AUDIO_HOTWORD; /** * Manage credentials in the system KeyChain. @@ -1174,13 +1172,38 @@ public class AppOpsManager { * * @hide */ - // TODO: Add as AppProtoEnums - public static final int OP_RECORD_AUDIO_OUTPUT = 106; + public static final int OP_RECORD_AUDIO_OUTPUT = AppProtoEnums.APP_OP_RECORD_AUDIO_OUTPUT; + /** + * App can schedule exact alarm to perform timing based background work + * + * @hide + */ + public static final int OP_SCHEDULE_EXACT_ALARM = AppProtoEnums.APP_OP_SCHEDULE_EXACT_ALARM; + + /** + * Fine location being accessed by a location source, which is + * a component that already has location data since it is the one + * that produces location, which is it is a data source for + * location data. + * + * @hide + */ + public static final int OP_FINE_LOCATION_SOURCE = AppProtoEnums.APP_OP_FINE_LOCATION_SOURCE; + + /** + * Coarse location being accessed by a location source, which is + * a component that already has location data since it is the one + * that produces location, which is it is a data source for + * location data. + * + * @hide + */ + public static final int OP_COARSE_LOCATION_SOURCE = AppProtoEnums.APP_OP_COARSE_LOCATION_SOURCE; /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 107; + public static final int _NUM_OP = 110; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1553,6 +1576,31 @@ public class AppOpsManager { */ public static final String OPSTR_RECORD_AUDIO_OUTPUT = "android:record_audio_output"; + /** + * App can schedule exact alarm to perform timing based background work. + * + * @hide + */ + public static final String OPSTR_SCHEDULE_EXACT_ALARM = "android:schedule_exact_alarm"; + + /** + * Fine location being accessed by a location source, which is + * a component that already has location since it is the one that + * produces location. + * + * @hide + */ + public static final String OPSTR_FINE_LOCATION_SOURCE = "android:fine_location_source"; + + /** + * Coarse location being accessed by a location source, which is + * a component that already has location since it is the one that + * produces location. + * + * @hide + */ + public static final String OPSTR_COARSE_LOCATION_SOURCE = "android:coarse_location_source"; + /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */ @@ -1633,6 +1681,7 @@ public class AppOpsManager { OP_LOADER_USAGE_STATS, OP_MANAGE_ONGOING_CALLS, OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, + OP_SCHEDULE_EXACT_ALARM, }; /** @@ -1751,6 +1800,9 @@ public class AppOpsManager { OP_MANAGE_CREDENTIALS, // MANAGE_CREDENTIALS OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER OP_RECORD_AUDIO_OUTPUT, // RECORD_AUDIO_OUTPUT + OP_SCHEDULE_EXACT_ALARM, // SCHEDULE_EXACT_ALARM + OP_FINE_LOCATION, // OP_FINE_LOCATION_SOURCE + OP_COARSE_LOCATION, // OP_COARSE_LOCATION_SOURCE }; /** @@ -1864,6 +1916,9 @@ public class AppOpsManager { OPSTR_MANAGE_CREDENTIALS, OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, OPSTR_RECORD_AUDIO_OUTPUT, + OPSTR_SCHEDULE_EXACT_ALARM, + OPSTR_FINE_LOCATION_SOURCE, + OPSTR_COARSE_LOCATION_SOURCE, }; /** @@ -1978,6 +2033,9 @@ public class AppOpsManager { "MANAGE_CREDENTIALS", "USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER", "RECORD_AUDIO_OUTPUT", + "SCHEDULE_EXACT_ALARM", + "FINE_LOCATION_SOURCE", + "COARSE_LOCATION_SOURCE", }; /** @@ -2093,6 +2151,9 @@ public class AppOpsManager { null, // no permission for OP_MANAGE_CREDENTIALS Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, null, // no permission for OP_RECORD_AUDIO_OUTPUT + Manifest.permission.SCHEDULE_EXACT_ALARM, + null, // no permission for OP_ACCESS_FINE_LOCATION_SOURCE, + null, // no permission for OP_ACCESS_COARSE_LOCATION_SOURCE, }; /** @@ -2208,6 +2269,9 @@ public class AppOpsManager { null, // MANAGE_CREDENTIALS null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER null, // RECORD_AUDIO_OUTPUT + null, // SCHEDULE_EXACT_ALARM + null, // ACCESS_FINE_LOCATION_SOURCE + null, // ACCESS_COARSE_LOCATION_SOURCE }; /** @@ -2322,6 +2386,9 @@ public class AppOpsManager { null, // MANAGE_CREDENTIALS null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER null, // RECORD_AUDIO_OUTPUT + null, // SCHEDULE_EXACT_ALARM + null, // ACCESS_FINE_LOCATION_SOURCE + null, // ACCESS_COARSE_LOCATION_SOURCE }; /** @@ -2435,6 +2502,9 @@ public class AppOpsManager { AppOpsManager.MODE_DEFAULT, // MANAGE_CREDENTIALS AppOpsManager.MODE_DEFAULT, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO_OUTPUT + AppOpsManager.MODE_DEFAULT, // SCHEDULE_EXACT_ALARM + AppOpsManager.MODE_ALLOWED, // ACCESS_FINE_LOCATION_SOURCE + AppOpsManager.MODE_ALLOWED, // ACCESS_COARSE_LOCATION_SOURCE }; /** @@ -2529,9 +2599,9 @@ public class AppOpsManager { false, // READ_MEDIA_AUDIO false, // WRITE_MEDIA_AUDIO false, // READ_MEDIA_VIDEO - false, // WRITE_MEDIA_VIDEO + true, // WRITE_MEDIA_VIDEO false, // READ_MEDIA_IMAGES - false, // WRITE_MEDIA_IMAGES + true, // WRITE_MEDIA_IMAGES true, // LEGACY_STORAGE false, // ACCESS_ACCESSIBILITY false, // READ_DEVICE_IDENTIFIERS @@ -2552,6 +2622,9 @@ public class AppOpsManager { false, // MANAGE_CREDENTIALS true, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER false, // RECORD_AUDIO_OUTPUT + false, // SCHEDULE_EXACT_ALARM + false, // ACCESS_FINE_LOCATION_SOURCE + false, // ACCESS_COARSE_LOCATION_SOURCE }; /** @@ -6530,14 +6603,15 @@ public class AppOpsManager { public interface OnOpNotedListener { /** * Called when an op was noted. - * * @param code The op code. * @param uid The UID performing the operation. * @param packageName The package performing the operation. + * @param attributionTag The attribution tag performing the operation. * @param flags The flags of this op * @param result The result of the note. */ - void onOpNoted(int code, int uid, String packageName, @OpFlags int flags, @Mode int result); + void onOpNoted(int code, int uid, String packageName, String attributionTag, + @OpFlags int flags, @Mode int result); } /** @@ -6570,14 +6644,15 @@ public class AppOpsManager { * Called when an op was started. * * Note: This is only for op starts. It is not called when an op is noted or stopped. - * * @param op The op code. * @param uid The UID performing the operation. * @param packageName The package performing the operation. + * @param attributionTag The attribution tag performing the operation. * @param flags The flags of this op * @param result The result of the start. */ - void onOpStarted(int op, int uid, String packageName, @OpFlags int flags, @Mode int result); + void onOpStarted(int op, int uid, String packageName, String attributionTag, + @OpFlags int flags, @Mode int result); } AppOpsManager(Context context, IAppOpsService service) { @@ -7160,8 +7235,9 @@ public class AppOpsManager { } cb = new IAppOpsStartedCallback.Stub() { @Override - public void opStarted(int op, int uid, String packageName, int flags, int mode) { - callback.onOpStarted(op, uid, packageName, flags, mode); + public void opStarted(int op, int uid, String packageName, String attributionTag, + int flags, int mode) { + callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode); } }; mStartedWatchers.put(callback, cb); @@ -7227,8 +7303,9 @@ public class AppOpsManager { } cb = new IAppOpsNotedCallback.Stub() { @Override - public void opNoted(int op, int uid, String packageName, int flags, int mode) { - callback.onOpNoted(op, uid, packageName, flags, mode); + public void opNoted(int op, int uid, String packageName, String attributionTag, + int flags, int mode) { + callback.onOpNoted(op, uid, packageName, attributionTag, flags, mode); } }; mNotedWatchers.put(callback, cb); diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index 7410a1ca04c8..dfc105a2811a 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -428,6 +428,13 @@ public final class ApplicationExitInfo implements Parcelable { */ private IAppTraceRetriever mAppTraceRetriever; + /** + * ParcelFileDescriptor pointing to a native tombstone. + * + * @see #getTraceInputStream + */ + private IParcelFileDescriptorRetriever mNativeTombstoneRetriever; + /** @hide */ @IntDef(prefix = { "REASON_" }, value = { REASON_UNKNOWN, @@ -603,22 +610,38 @@ public final class ApplicationExitInfo implements Parcelable { * prior to the death of the process; typically it'll be available when * the reason is {@link #REASON_ANR}, though if the process gets an ANR * but recovers, and dies for another reason later, this trace will be included - * in the record of {@link ApplicationExitInfo} still. + * in the record of {@link ApplicationExitInfo} still. Beginning with API 31, + * tombstone traces will be returned for + * {@link #REASON_CRASH_NATIVE}, with an InputStream containing a protobuf with + * <a href="https://android.googlesource.com/platform/system/core/+/refs/heads/master/debuggerd/proto/tombstone.proto">this schema</a>. + * Note thatbecause these traces are kept in a separate global circular buffer, crashes may be + * overwritten by newer crashes (including from other applications), so this may still return + * null. * * @return The input stream to the traces that was taken by the system * prior to the death of the process. */ public @Nullable InputStream getTraceInputStream() throws IOException { - if (mAppTraceRetriever == null) { + if (mAppTraceRetriever == null && mNativeTombstoneRetriever == null) { return null; } + try { - final ParcelFileDescriptor fd = mAppTraceRetriever.getTraceFileDescriptor( - mPackageName, mPackageUid, mPid); - if (fd == null) { - return null; + if (mNativeTombstoneRetriever != null) { + final ParcelFileDescriptor pfd = mNativeTombstoneRetriever.getPfd(); + if (pfd == null) { + return null; + } + + return new ParcelFileDescriptor.AutoCloseInputStream(pfd); + } else { + final ParcelFileDescriptor fd = mAppTraceRetriever.getTraceFileDescriptor( + mPackageName, mPackageUid, mPid); + if (fd == null) { + return null; + } + return new GZIPInputStream(new ParcelFileDescriptor.AutoCloseInputStream(fd)); } - return new GZIPInputStream(new ParcelFileDescriptor.AutoCloseInputStream(fd)); } catch (RemoteException e) { return null; } @@ -849,6 +872,15 @@ public final class ApplicationExitInfo implements Parcelable { mAppTraceRetriever = retriever; } + /** + * @see mNativeTombstoneRetriever + * + * @hide + */ + public void setNativeTombstoneRetriever(final IParcelFileDescriptorRetriever retriever) { + mNativeTombstoneRetriever = retriever; + } + @Override public int describeContents() { return 0; @@ -878,6 +910,12 @@ public final class ApplicationExitInfo implements Parcelable { } else { dest.writeInt(0); } + if (mNativeTombstoneRetriever != null) { + dest.writeInt(1); + dest.writeStrongBinder(mNativeTombstoneRetriever.asBinder()); + } else { + dest.writeInt(0); + } } /** @hide */ @@ -906,6 +944,7 @@ public final class ApplicationExitInfo implements Parcelable { mState = other.mState; mTraceFile = other.mTraceFile; mAppTraceRetriever = other.mAppTraceRetriever; + mNativeTombstoneRetriever = other.mNativeTombstoneRetriever; } private ApplicationExitInfo(@NonNull Parcel in) { @@ -928,6 +967,10 @@ public final class ApplicationExitInfo implements Parcelable { if (in.readInt() == 1) { mAppTraceRetriever = IAppTraceRetriever.Stub.asInterface(in.readStrongBinder()); } + if (in.readInt() == 1) { + mNativeTombstoneRetriever = IParcelFileDescriptorRetriever.Stub.asInterface( + in.readStrongBinder()); + } } public @NonNull static final Creator<ApplicationExitInfo> CREATOR = @@ -986,6 +1029,7 @@ public final class ApplicationExitInfo implements Parcelable { sb.append(" state=").append(ArrayUtils.isEmpty(mState) ? "empty" : Integer.toString(mState.length) + " bytes"); sb.append(" trace=").append(mTraceFile); + return sb.toString(); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 062cab457ebe..a6260d6d9cad 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1797,6 +1797,7 @@ public class ApplicationPackageManager extends PackageManager { } } + @UnsupportedAppUsage protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) { mContext = context; mPM = pm; diff --git a/core/java/android/app/BackgroundServiceStartNotAllowedException.java b/core/java/android/app/BackgroundServiceStartNotAllowedException.java new file mode 100644 index 000000000000..f6361b52bf9d --- /dev/null +++ b/core/java/android/app/BackgroundServiceStartNotAllowedException.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 android.app; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Exception thrown when an app tries to start a background {@link Service} when it's not allowed to + * do so. + */ +public final class BackgroundServiceStartNotAllowedException + extends ServiceStartNotAllowedException implements Parcelable { + /** + * Constructor. + */ + public BackgroundServiceStartNotAllowedException(@NonNull String message) { + super(message); + } + + BackgroundServiceStartNotAllowedException(@NonNull Parcel source) { + super(source.readString()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(getMessage()); + } + + public static final @NonNull Creator<android.app.BackgroundServiceStartNotAllowedException> + CREATOR = new Creator<android.app.BackgroundServiceStartNotAllowedException>() { + @NonNull + public android.app.BackgroundServiceStartNotAllowedException createFromParcel( + Parcel source) { + return new android.app.BackgroundServiceStartNotAllowedException(source); + } + + @NonNull + public android.app.BackgroundServiceStartNotAllowedException[] newArray(int size) { + return new android.app.BackgroundServiceStartNotAllowedException[size]; + } + }; +} diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 445fdd83f34a..477e96b16daf 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -16,11 +16,13 @@ package android.app; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.os.Build; import android.os.Bundle; import android.os.PowerWhitelistManager; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.PowerWhitelistManager.TempAllowListType; /** @@ -31,8 +33,11 @@ import android.os.PowerWhitelistManager.TempAllowListType; */ @SystemApi public class BroadcastOptions { - private long mTemporaryAppWhitelistDuration; - private @TempAllowListType int mTemporaryAppWhitelistType; + private long mTemporaryAppAllowlistDuration; + private @TempAllowListType int mTemporaryAppAllowlistType; + private @ReasonCode int mTemporaryAppAllowlistReasonCode = + PowerWhitelistManager.REASON_UNKNOWN; + private @Nullable String mTemporaryAppAllowlistReason; private int mMinManifestReceiverApiLevel = 0; private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT; private boolean mDontSendToRestrictedApps = false; @@ -42,11 +47,17 @@ public class BroadcastOptions { * How long to temporarily put an app on the power allowlist when executing this broadcast * to it. */ - static final String KEY_TEMPORARY_APP_WHITELIST_DURATION - = "android:broadcast.temporaryAppWhitelistDuration"; + static final String KEY_TEMPORARY_APP_ALLOWLIST_DURATION + = "android:broadcast.temporaryAppAllowlistDuration"; - static final String KEY_TEMPORARY_APP_WHITELIST_TYPE - = "android:broadcast.temporaryAppWhitelistType"; + static final String KEY_TEMPORARY_APP_ALLOWLIST_TYPE + = "android:broadcast.temporaryAppAllowlistType"; + + static final String KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE = + "android:broadcast.temporaryAppAllowlistReasonCode"; + + static final String KEY_TEMPORARY_APP_ALLOWLIST_REASON = + "android:broadcast.temporaryAppAllowlistReason"; /** * Corresponds to {@link #setMinManifestReceiverApiLevel}. @@ -80,6 +91,7 @@ public class BroadcastOptions { @Deprecated public static final int TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED = PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; + /** * @hide * @deprecated Use {@link android.os.PowerWhitelistManager# @@ -99,8 +111,11 @@ public class BroadcastOptions { /** @hide */ public BroadcastOptions(Bundle opts) { - mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION); - mTemporaryAppWhitelistType = opts.getInt(KEY_TEMPORARY_APP_WHITELIST_TYPE); + mTemporaryAppAllowlistDuration = opts.getLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION); + mTemporaryAppAllowlistType = opts.getInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE); + mTemporaryAppAllowlistReasonCode = opts.getInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE, + PowerWhitelistManager.REASON_UNKNOWN); + mTemporaryAppAllowlistReason = opts.getString(KEY_TEMPORARY_APP_ALLOWLIST_REASON); mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0); mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, Build.VERSION_CODES.CUR_DEVELOPMENT); @@ -113,45 +128,70 @@ public class BroadcastOptions { * Set a duration for which the system should temporary place an application on the * power allowlist when this broadcast is being delivered to it. * @param duration The duration in milliseconds; 0 means to not place on allowlist. + * @deprecated use {@link #setTemporaryAppAllowlist(long, int, int, String)} instead. */ + @Deprecated @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long duration) { - mTemporaryAppWhitelistDuration = duration; - mTemporaryAppWhitelistType = - PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; + setTemporaryAppAllowlist(duration, + PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + PowerWhitelistManager.REASON_UNKNOWN, null); } /** * Set a duration for which the system should temporary place an application on the * power allowlist when this broadcast is being delivered to it, specify the temp allowlist * type. - * @param type one of {@link TempAllowListType} * @param duration the duration in milliseconds; 0 means to not place on allowlist. + * @param type one of {@link TempAllowListType} + * @param reasonCode one of {@link ReasonCode}, use + * {@link PowerWhitelistManager#REASON_UNKNOWN} if not sure. + * @param reason A human-readable reason explaining why the app is temp allowlisted. Only + * used for logging purposes. Could be null or empty string. */ @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) - public void setTemporaryAppWhitelistDuration(@TempAllowListType int type, long duration) { - mTemporaryAppWhitelistDuration = duration; - mTemporaryAppWhitelistType = type; + public void setTemporaryAppAllowlist(long duration, @TempAllowListType int type, + @ReasonCode int reasonCode, @Nullable String reason) { + mTemporaryAppAllowlistDuration = duration; + mTemporaryAppAllowlistType = type; + mTemporaryAppAllowlistReasonCode = reasonCode; + mTemporaryAppAllowlistReason = reason; + } + + /** + * Return {@link #setTemporaryAppAllowlist}. + * @hide + */ + public long getTemporaryAppAllowlistDuration() { + return mTemporaryAppAllowlistDuration; } /** - * Return {@link #setTemporaryAppWhitelistDuration}. + * Return {@link #mTemporaryAppAllowlistType}. * @hide */ - public long getTemporaryAppWhitelistDuration() { - return mTemporaryAppWhitelistDuration; + public @TempAllowListType int getTemporaryAppAllowlistType() { + return mTemporaryAppAllowlistType; } /** - * Return {@link #mTemporaryAppWhitelistType}. + * Return {@link #mTemporaryAppAllowlistReasonCode}. * @hide */ - public @TempAllowListType int getTemporaryAppWhitelistType() { - return mTemporaryAppWhitelistType; + public @ReasonCode int getTemporaryAppAllowlistReasonCode() { + return mTemporaryAppAllowlistReasonCode; + } + + /** + * Return {@link #mTemporaryAppAllowlistReason}. + * @hide + */ + public @Nullable String getTemporaryAppAllowlistReason() { + return mTemporaryAppAllowlistReason; } /** @@ -236,11 +276,17 @@ public class BroadcastOptions { */ public Bundle toBundle() { Bundle b = new Bundle(); - if (mTemporaryAppWhitelistDuration > 0) { - b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration); + if (mTemporaryAppAllowlistDuration > 0) { + b.putLong(KEY_TEMPORARY_APP_ALLOWLIST_DURATION, mTemporaryAppAllowlistDuration); + } + if (mTemporaryAppAllowlistType != 0) { + b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_TYPE, mTemporaryAppAllowlistType); + } + if (mTemporaryAppAllowlistReasonCode != PowerWhitelistManager.REASON_UNKNOWN) { + b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE, mTemporaryAppAllowlistReasonCode); } - if (mTemporaryAppWhitelistType != 0) { - b.putInt(KEY_TEMPORARY_APP_WHITELIST_TYPE, mTemporaryAppWhitelistType); + if (mTemporaryAppAllowlistReason != null) { + b.putString(KEY_TEMPORARY_APP_ALLOWLIST_REASON, mTemporaryAppAllowlistReason); } if (mMinManifestReceiverApiLevel != 0) { b.putInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, mMinManifestReceiverApiLevel); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 85fb543a3967..bc798136f2e3 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1796,7 +1796,7 @@ class ContextImpl extends Context { "Unable to start service " + service + ": " + cn.getClassName()); } else if (cn.getPackageName().equals("?")) { - throw new IllegalStateException( + throw ServiceStartNotAllowedException.newInstance(requireForeground, "Not allowed to start service " + service + ": " + cn.getClassName()); } } diff --git a/core/java/android/app/ForegroundServiceStartNotAllowedException.java b/core/java/android/app/ForegroundServiceStartNotAllowedException.java new file mode 100644 index 000000000000..41eeada2df6b --- /dev/null +++ b/core/java/android/app/ForegroundServiceStartNotAllowedException.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 android.app; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Exception thrown when an app tries to start a foreground {@link Service} when it's not allowed to + * do so. + */ +public final class ForegroundServiceStartNotAllowedException + extends ServiceStartNotAllowedException implements Parcelable { + /** + * Constructor. + */ + public ForegroundServiceStartNotAllowedException(@NonNull String message) { + super(message); + } + + ForegroundServiceStartNotAllowedException(@NonNull Parcel source) { + super(source.readString()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(getMessage()); + } + + public static final @NonNull Creator<android.app.ForegroundServiceStartNotAllowedException> + CREATOR = new Creator<android.app.ForegroundServiceStartNotAllowedException>() { + @NonNull + public android.app.ForegroundServiceStartNotAllowedException createFromParcel( + Parcel source) { + return new android.app.ForegroundServiceStartNotAllowedException(source); + } + + @NonNull + public android.app.ForegroundServiceStartNotAllowedException[] newArray(int size) { + return new android.app.ForegroundServiceStartNotAllowedException[size]; + } + }; +} diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl index bb743b89e00f..573931ed228e 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -17,6 +17,7 @@ package android.app; import android.app.ActivityManager; +import android.app.IRequestFinishCallback; import android.app.PictureInPictureParams; import android.content.ComponentName; import android.content.Intent; @@ -141,7 +142,8 @@ interface IActivityClientController { * Reports that an Activity received a back key press when there were no additional activities * on the back stack. */ - oneway void onBackPressedOnTaskRoot(in IBinder token); + oneway void onBackPressedOnTaskRoot(in IBinder activityToken, + in IRequestFinishCallback callback); /** Reports that the splash screen view has attached to activity. */ oneway void splashScreenAttached(in IBinder token); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 4ad13e1932dd..3a8172ea98b8 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -17,6 +17,7 @@ package android.app; import android.app.ActivityManager; +import android.app.ActivityManager.PendingIntentInfo; import android.app.ActivityTaskManager; import android.app.ApplicationErrorReport; import android.app.ApplicationExitInfo; @@ -248,7 +249,7 @@ interface IActivityManager { in IBinder token, in String resultWho, int requestCode, in Intent[] intents, in String[] resolvedTypes, int flags, in Bundle options, int userId); void cancelIntentSender(in IIntentSender sender); - String getPackageForIntentSender(in IIntentSender sender); + ActivityManager.PendingIntentInfo getInfoForIntentSender(in IIntentSender sender); void registerIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver); void unregisterIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver); void enterSafeMode(); @@ -293,7 +294,6 @@ interface IActivityManager { int operationType); void backupAgentCreated(in String packageName, in IBinder agent, int userId); void unbindBackupAgent(in ApplicationInfo appInfo); - int getUidForIntentSender(in IIntentSender sender); int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll, boolean requireFull, in String name, in String callerPackage); void addPackageDependency(in String packageName); @@ -345,7 +345,6 @@ interface IActivityManager { @UnsupportedAppUsage void unregisterProcessObserver(in IProcessObserver observer); boolean isIntentSenderTargetedToPackage(in IIntentSender sender); - boolean isIntentSenderImmutable(in IIntentSender sender); @UnsupportedAppUsage void updatePersistentConfiguration(in Configuration values); void updatePersistentConfigurationWithAttribution(in Configuration values, @@ -375,8 +374,6 @@ interface IActivityManager { void unstableProviderDied(in IBinder connection); @UnsupportedAppUsage boolean isIntentSenderAnActivity(in IIntentSender sender); - boolean isIntentSenderAForegroundService(in IIntentSender sender); - boolean isIntentSenderABroadcast(in IIntentSender sender); /** @deprecated Use {@link startActivityAsUserWithFeature} instead */ @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@code android.content.Context#createContextAsUser(android.os.UserHandle, int)} and {@link android.content.Context#startActivity(android.content.Intent)} instead") int startActivityAsUser(in IApplicationThread caller, in String callingPackage, @@ -552,7 +549,7 @@ interface IActivityManager { /** * Add a bare uid to the background restrictions whitelist. Only the system uid may call this. */ - void backgroundWhitelistUid(int uid); + void backgroundAllowlistUid(int uid); // Start of P transactions /** @@ -711,5 +708,5 @@ interface IActivityManager { /** Called by PendingIntent.queryIntentComponents() */ List<ResolveInfo> queryIntentComponentsForIntentSender(in IIntentSender sender, int matchFlags); - boolean isIntentSenderAService(in IIntentSender sender); + int getUidProcessCapabilities(int uid, in String callingPackage); } diff --git a/core/java/android/uwb/AngleOfArrivalSupport.aidl b/core/java/android/app/ILocalWallpaperColorConsumer.aidl index 57666ff8bca9..28b11ec04d96 100644 --- a/core/java/android/uwb/AngleOfArrivalSupport.aidl +++ b/core/java/android/app/ILocalWallpaperColorConsumer.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,31 +14,14 @@ * limitations under the License. */ -package android.uwb; +package android.app; + +import android.app.WallpaperColors; +import android.graphics.RectF; /** * @hide */ -@Backing(type="int") -enum AngleOfArrivalSupport { - /** - * The device does not support angle of arrival - */ - NONE, - - /** - * The device supports planar angle of arrival - */ - TWO_DIMENSIONAL, - - /** - * The device does supports three dimensional angle of arrival with hemispherical azimuth angles - */ - THREE_DIMENSIONAL_HEMISPHERICAL, - - /** - * The device does supports three dimensional angle of arrival with full azimuth angles - */ - THREE_DIMENSIONAL_SPHERICAL, -} - +oneway interface ILocalWallpaperColorConsumer { + void onColorsChanged(in RectF area, in WallpaperColors colors); +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewScope.java b/core/java/android/app/IParcelFileDescriptorRetriever.aidl index ba0642f57a88..7e808e74bd5d 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewScope.java +++ b/core/java/android/app/IParcelFileDescriptorRetriever.aidl @@ -14,19 +14,18 @@ * limitations under the License. */ -package com.android.keyguard.dagger; +package android.app; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; - -import javax.inject.Scope; +import android.os.ParcelFileDescriptor; /** - * Scope annotation for singleton items within the StatusBarComponent. + * An interface used to lazily provide a ParcelFileDescriptor to apps. + * + * @hide */ -@Documented -@Retention(RUNTIME) -@Scope -public @interface KeyguardStatusBarViewScope {} +interface IParcelFileDescriptorRetriever { + /** + * Retrieve the ParcelFileDescriptor. + */ + ParcelFileDescriptor getPfd(); +} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockModule.java b/core/java/android/app/IRequestFinishCallback.aidl index c4be1ba53503..22c20c840e99 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockModule.java +++ b/core/java/android/app/IRequestFinishCallback.aidl @@ -14,20 +14,14 @@ * limitations under the License. */ -package com.android.keyguard.clock; +package android.app; -import java.util.List; - -import dagger.Module; -import dagger.Provides; - -/** Dagger Module for clock package. */ -@Module -public abstract class ClockModule { - - /** */ - @Provides - public static List<ClockInfo> provideClockInfoList(ClockManager clockManager) { - return clockManager.getClockInfos(); - } +/** + * This callback allows ActivityTaskManager to ask the calling Activity + * to finish in response to a call to onBackPressedOnTaskRoot. + * + * {@hide} + */ +oneway interface IRequestFinishCallback { + void requestFinish(); } diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index a9e28bb635be..7bb5d9a8d387 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -60,9 +60,9 @@ oneway interface ITaskStackListener { void onActivityForcedResizable(String packageName, int taskId, int reason); /** - * Called when we launched an activity that dismissed the docked stack. + * Called when we launched an activity that dismissed the docked task. */ - void onActivityDismissingDockedStack(); + void onActivityDismissingDockedTask(); /** * Called when an activity was requested to be launched on a secondary display but was not @@ -209,4 +209,10 @@ oneway interface ITaskStackListener { * @param taskInfo info about the task which moved */ void onTaskMovedToBack(in ActivityManager.RunningTaskInfo taskInfo); + + /** + * Called when the lock task mode changes. See ActivityManager#LOCK_TASK_MODE_* and + * LockTaskController. + */ + void onLockTaskModeChanged(int mode); } diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 101917bc2e07..5402381b7207 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -17,9 +17,11 @@ package android.app; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.app.IWallpaperManagerCallback; +import android.app.ILocalWallpaperColorConsumer; import android.app.WallpaperInfo; import android.content.ComponentName; import android.app.WallpaperColors; @@ -162,6 +164,18 @@ interface IWallpaperManager { WallpaperColors getWallpaperColors(int which, int userId, int displayId); /** + * @hide + */ + void removeOnLocalColorsChangedListener( + in ILocalWallpaperColorConsumer callback, int which, int userId, int displayId); + + /** + * @hide + */ + void addOnLocalColorsChangedListener(in ILocalWallpaperColorConsumer callback, + in List<RectF> regions, int which, int userId, int displayId); + + /** * Register a callback to receive color updates from a display */ void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 77daf8ddf08f..8167622ff13c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1371,6 +1371,18 @@ public class Notification implements Parcelable public static final String EXTRA_HANG_UP_INTENT = "android.hangUpIntent"; /** + * {@link #extras} key: the color used as a hint for the Answer action button of a + * {@link android.app.Notification.CallStyle} notification. This extra is a {@link ColorInt}. + */ + public static final String EXTRA_ANSWER_COLOR = "android.answerColor"; + + /** + * {@link #extras} key: the color used as a hint for the Decline or Hang Up action button of a + * {@link android.app.Notification.CallStyle} notification. This extra is a {@link ColorInt}. + */ + public static final String EXTRA_DECLINE_COLOR = "android.declineColor"; + + /** * {@link #extras} key: whether the notification should be colorized as * supplied to {@link Builder#setColorized(boolean)}. */ @@ -3624,7 +3636,7 @@ public class Notification implements Parcelable private Bundle mUserExtras = new Bundle(); private Style mStyle; @UnsupportedAppUsage - private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS); + private ArrayList<Action> mActions = new ArrayList<>(MAX_ACTION_BUTTONS); private ArrayList<Person> mPersonList = new ArrayList<>(); private ContrastColorUtil mColorUtil; private boolean mIsLegacy; @@ -4878,6 +4890,16 @@ public class Notification implements Parcelable return this; } + private void bindPhishingAlertIcon(RemoteViews contentView, StandardTemplateParams p) { + // TODO(b/180334837): Get buy-in on this color, or make sure to give this the + // accent color, while still accommodating the colorized state. + contentView.setDrawableTint( + R.id.phishing_alert, + false /* targetBackground */, + getPrimaryTextColor(p), + PorterDuff.Mode.SRC_ATOP); + } + private Drawable getProfileBadgeDrawable() { if (mContext.getUserId() == UserHandle.USER_SYSTEM) { // This user can never be a badged profile, @@ -4991,6 +5013,7 @@ public class Notification implements Parcelable bindNotificationHeader(contentView, p); bindLargeIconAndApplyMargin(contentView, p, result); boolean showProgress = handleProgressBar(contentView, ex, p); + boolean hasSecondLine = showProgress; if (p.hasTitle()) { contentView.setViewVisibility(R.id.title, View.VISIBLE); contentView.setTextViewText(R.id.title, processTextSpans(p.title)); @@ -5006,11 +5029,27 @@ public class Notification implements Parcelable contentView.setTextViewText(textId, processTextSpans(p.text)); setTextViewColorSecondary(contentView, textId, p); contentView.setViewVisibility(textId, View.VISIBLE); + hasSecondLine = true; } + setHeaderlessVerticalMargins(contentView, p, hasSecondLine); return contentView; } + private static void setHeaderlessVerticalMargins(RemoteViews contentView, + StandardTemplateParams p, boolean hasSecondLine) { + if (!p.mHeaderless) { + return; + } + int marginDimen = hasSecondLine + ? R.dimen.notification_headerless_margin_twoline + : R.dimen.notification_headerless_margin_oneline; + contentView.setViewLayoutMarginDimen(R.id.notification_headerless_view_column, + RemoteViews.MARGIN_TOP, marginDimen); + contentView.setViewLayoutMarginDimen(R.id.notification_headerless_view_column, + RemoteViews.MARGIN_BOTTOM, marginDimen); + } + private CharSequence processTextSpans(CharSequence text) { if (hasForegroundColor() || mInNightMode) { return ContrastColorUtil.clearColorSpans(text); @@ -5279,6 +5318,7 @@ public class Notification implements Parcelable hasTextToLeft |= bindHeaderAppName(contentView, p, true /* force */); } bindHeaderChronometerAndTime(contentView, p, hasTextToLeft); + bindPhishingAlertIcon(contentView, p); bindProfileBadge(contentView, p); bindAlertedIcon(contentView, p); bindExpandButton(contentView, p); @@ -5433,6 +5473,11 @@ public class Notification implements Parcelable return p.allowColorization && mN.isColorized(); } + private boolean isCallActionColorCustomizable(StandardTemplateParams p) { + return isColorized(p) && mContext.getResources().getBoolean( + R.bool.config_callNotificationActionColorsRequireColorized); + } + private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) { if (mN.mSmallIcon == null && mN.icon != 0) { mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon); @@ -5466,23 +5511,42 @@ public class Notification implements Parcelable big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE); big.setTextViewText(R.id.notification_material_reply_text_3, null); - final boolean snoozeEnabled = mContext.getContentResolver() != null - && (Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1); - int bottomMarginDimen = snoozeEnabled ? 0 : R.dimen.notification_content_margin; + // This may get erased by bindSnoozeAction big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target, - RemoteViews.MARGIN_BOTTOM, bottomMarginDimen); + RemoteViews.MARGIN_BOTTOM, R.dimen.notification_content_margin); } - private static List<Notification.Action> filterOutContextualActions( - List<Notification.Action> actions) { - List<Notification.Action> nonContextualActions = new ArrayList<>(); - for (Notification.Action action : actions) { + private void bindSnoozeAction(RemoteViews big, StandardTemplateParams p) { + boolean hideSnoozeButton = mN.isForegroundService() || mN.fullScreenIntent != null + || isColorized(p) || p.mViewType == StandardTemplateParams.VIEW_TYPE_HEADS_UP; + big.setBoolean(R.id.snooze_button, "setEnabled", !hideSnoozeButton); + if (hideSnoozeButton) { + // Only hide; NotificationContentView will show it when it adds the click listener + big.setViewVisibility(R.id.snooze_button, View.GONE); + } + + final boolean snoozeEnabled = !hideSnoozeButton + && mContext.getContentResolver() != null + && (Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1); + if (snoozeEnabled) { + big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target, + RemoteViews.MARGIN_BOTTOM, 0); + } + } + + /** + * Returns the actions that are not contextual. + */ + private @NonNull List<Notification.Action> getNonContextualActions() { + if (mActions == null) return Collections.emptyList(); + List<Notification.Action> contextualActions = new ArrayList<>(); + for (Notification.Action action : mActions) { if (!action.isContextual()) { - nonContextualActions.add(action); + contextualActions.add(action); } } - return nonContextualActions; + return contextualActions; } private RemoteViews applyStandardTemplateWithActions(int layoutId, @@ -5490,12 +5554,13 @@ public class Notification implements Parcelable RemoteViews big = applyStandardTemplate(layoutId, p, result); resetStandardTemplateWithActions(big); + bindSnoozeAction(big, p); boolean validRemoteInput = false; - // In the UI contextual actions appear separately from the standard actions, so we + // In the UI, contextual actions appear separately from the standard actions, so we // filter them out here. - List<Notification.Action> nonContextualActions = filterOutContextualActions(mActions); + List<Notification.Action> nonContextualActions = getNonContextualActions(); int N = nonContextualActions.size(); boolean emphazisedMode = mN.fullScreenIntent != null; @@ -6753,6 +6818,31 @@ public class Notification implements Parcelable } /** + * @return true if the notification has image + */ + public boolean hasImage() { + if (MessagingStyle.class.equals(getNotificationStyle()) && extras != null) { + final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES); + if (!ArrayUtils.isEmpty(messages)) { + for (MessagingStyle.Message m : MessagingStyle.Message + .getMessagesFromBundleArray(messages)) { + if (m.getDataUri() != null + && m.getDataMimeType() != null + && m.getDataMimeType().startsWith("image/")) { + return true; + } + } + } + } else if (hasLargeIcon()) { + return true; + } else if (extras.containsKey(EXTRA_BACKGROUND_IMAGE_URI)) { + return true; + } + return false; + } + + + /** * @removed */ @SystemApi @@ -6778,26 +6868,13 @@ public class Notification implements Parcelable if (decorationType <= DevFlags.DECORATION_PARTIAL) { template.removeFromParent(R.id.notification_top_line); } - if (decorationType != DevFlags.DECORATION_FULL_COMPATIBLE) { - // Change the max content size from 60dp (the compatible size) to 48dp - // (the constrained size). This is done by increasing the minimum margin - // (implemented as top/bottom margins) and decreasing the extra margin - // (implemented as the height of shrinkable top/bottom views in the column). - template.setViewLayoutMarginDimen( - R.id.notification_headerless_view_column, - RemoteViews.MARGIN_TOP, - R.dimen.notification_headerless_margin_constrained_minimum); - template.setViewLayoutMarginDimen( - R.id.notification_headerless_view_column, - RemoteViews.MARGIN_BOTTOM, - R.dimen.notification_headerless_margin_constrained_minimum); - template.setViewLayoutHeightDimen( - R.id.notification_headerless_margin_extra_top, - R.dimen.notification_headerless_margin_constrained_extra); - template.setViewLayoutHeightDimen( - R.id.notification_headerless_margin_extra_bottom, - R.dimen.notification_headerless_margin_constrained_extra); - } + // The vertical margins are bigger in the "two-line" scenario than the "one-line" + // scenario, but the 'compatible' decoration state is intended to have 3 lines, + // (1 for the top line views and 2 for the custom views), so in that one case we + // use the smaller 1-line margins. This gives the compatible case 88-16*2=56 dp of + // height, 24dp of which goes to the top line, leaving 32dp for the custom view. + boolean hasSecondLine = decorationType != DevFlags.DECORATION_FULL_COMPATIBLE; + Builder.setHeaderlessVerticalMargins(template, p, hasSecondLine); } else { // also update the end margin to account for the large icon or expander Resources resources = context.getResources(); @@ -9027,6 +9104,8 @@ public class Notification implements Parcelable private PendingIntent mAnswerIntent; private PendingIntent mDeclineIntent; private PendingIntent mHangUpIntent; + private Integer mAnswerButtonColor; + private Integer mDeclineButtonColor; private Icon mVerificationIcon; private CharSequence mVerificationText; @@ -9137,6 +9216,28 @@ public class Notification implements Parcelable } /** + * Optional color to be used as a hint for the Answer action button's color. + * The system may change this color to ensure sufficient contrast with the background. + * The system may choose to disregard this hint if the notification is not colorized. + */ + @NonNull + public CallStyle setAnswerButtonColorHint(@ColorInt int color) { + mAnswerButtonColor = color; + return this; + } + + /** + * Optional color to be used as a hint for the Decline or Hang Up action button's color. + * The system may change this color to ensure sufficient contrast with the background. + * The system may choose to disregard this hint if the notification is not colorized. + */ + @NonNull + public CallStyle setDeclineButtonColorHint(@ColorInt int color) { + mDeclineButtonColor = color; + return this; + } + + /** * @hide */ public boolean displayCustomViewInline() { @@ -9195,40 +9296,47 @@ public class Notification implements Parcelable } @NonNull - private Action makeNegativeAction() { + private Action makeNegativeAction(@NonNull StandardTemplateParams p) { if (mDeclineIntent == null) { - return makeAction(R.drawable.ic_call_decline, + return makeAction(p, R.drawable.ic_call_decline, R.string.call_notification_hang_up_action, - R.color.call_notification_decline_color, mHangUpIntent); + mDeclineButtonColor, R.color.call_notification_decline_color, + mHangUpIntent); } else { - return makeAction(R.drawable.ic_call_decline, + return makeAction(p, R.drawable.ic_call_decline, R.string.call_notification_decline_action, - R.color.call_notification_decline_color, mDeclineIntent); + mDeclineButtonColor, R.color.call_notification_decline_color, + mDeclineIntent); } } @Nullable - private Action makeAnswerAction() { - return mAnswerIntent == null ? null : makeAction(R.drawable.ic_call_answer, + private Action makeAnswerAction(@NonNull StandardTemplateParams p) { + return mAnswerIntent == null ? null : makeAction(p, R.drawable.ic_call_answer, R.string.call_notification_answer_action, - R.color.call_notification_answer_color, mAnswerIntent); + mAnswerButtonColor, R.color.call_notification_answer_color, + mAnswerIntent); } @NonNull - private Action makeAction(@DrawableRes int icon, @StringRes int title, - @ColorRes int colorRes, PendingIntent intent) { + private Action makeAction(@NonNull StandardTemplateParams p, + @DrawableRes int icon, @StringRes int title, + @ColorInt Integer colorInt, @ColorRes int defaultColorRes, PendingIntent intent) { + if (colorInt == null || !mBuilder.isCallActionColorCustomizable(p)) { + colorInt = mBuilder.mContext.getColor(defaultColorRes); + } Action action = new Action.Builder(Icon.createWithResource("", icon), new SpannableStringBuilder().append(mBuilder.mContext.getString(title), - new ForegroundColorSpan(mBuilder.mContext.getColor(colorRes)), + new ForegroundColorSpan(colorInt), SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE), intent).build(); action.getExtras().putBoolean(KEY_ACTION_PRIORITY, true); return action; } - private ArrayList<Action> makeActionsList() { - final Action negativeAction = makeNegativeAction(); - final Action answerAction = makeAnswerAction(); + private ArrayList<Action> makeActionsList(@NonNull StandardTemplateParams p) { + final Action negativeAction = makeNegativeAction(p); + final Action answerAction = makeAnswerAction(p); ArrayList<Action> actions = new ArrayList<>(MAX_ACTION_BUTTONS); final Action lastAction; @@ -9241,7 +9349,7 @@ public class Notification implements Parcelable lastAction = answerAction; } // For consistency with the standard actions bar, contextual actions are ignored. - for (Action action : Builder.filterOutContextualActions(mBuilder.mActions)) { + for (Action action : mBuilder.getNonContextualActions()) { if (actions.size() >= MAX_ACTION_BUTTONS - 1) { break; } @@ -9271,6 +9379,7 @@ public class Notification implements Parcelable // Bind actions. mBuilder.resetStandardTemplateWithActions(contentView); + mBuilder.bindSnoozeAction(contentView, p); bindCallActions(contentView, p); // Bind some extra conversation-specific header fields. @@ -9317,7 +9426,7 @@ public class Notification implements Parcelable // Create the buttons for the generated actions list. int i = 0; - for (Action action : makeActionsList()) { + for (Action action : makeActionsList(p)) { final RemoteViews button = mBuilder.generateActionButton(action, emphasizedMode, p); if (i > 0) { // Clear start margin from non-first buttons to reduce the gap between buttons. @@ -9330,11 +9439,16 @@ public class Notification implements Parcelable } private void bindCallerVerification(RemoteViews contentView, StandardTemplateParams p) { + String iconContentDescription = null; + boolean showDivider = true; if (mVerificationIcon != null) { contentView.setImageViewIcon(R.id.verification_icon, mVerificationIcon); contentView.setDrawableTint(R.id.verification_icon, false /* targetBackground */, mBuilder.getSecondaryTextColor(p), PorterDuff.Mode.SRC_ATOP); contentView.setViewVisibility(R.id.verification_icon, View.VISIBLE); + iconContentDescription = mBuilder.mContext.getString( + R.string.notification_verified_content_description); + showDivider = false; // the icon replaces the divider } else { contentView.setViewVisibility(R.id.verification_icon, View.GONE); } @@ -9342,8 +9456,17 @@ public class Notification implements Parcelable contentView.setTextViewText(R.id.verification_text, mVerificationText); mBuilder.setTextViewColorSecondary(contentView, R.id.verification_text, p); contentView.setViewVisibility(R.id.verification_text, View.VISIBLE); + iconContentDescription = null; // let the app's text take precedence } else { contentView.setViewVisibility(R.id.verification_text, View.GONE); + showDivider = false; // no divider if no text + } + contentView.setContentDescription(R.id.verification_icon, iconContentDescription); + if (showDivider) { + contentView.setViewVisibility(R.id.verification_divider, View.VISIBLE); + mBuilder.setTextViewColorSecondary(contentView, R.id.verification_divider, p); + } else { + contentView.setViewVisibility(R.id.verification_divider, View.GONE); } } @@ -9382,6 +9505,12 @@ public class Notification implements Parcelable if (mHangUpIntent != null) { extras.putParcelable(EXTRA_HANG_UP_INTENT, mHangUpIntent); } + if (mAnswerButtonColor != null) { + extras.putInt(EXTRA_ANSWER_COLOR, mAnswerButtonColor); + } + if (mDeclineButtonColor != null) { + extras.putInt(EXTRA_DECLINE_COLOR, mDeclineButtonColor); + } fixTitleAndTextExtras(extras); } @@ -9408,6 +9537,10 @@ public class Notification implements Parcelable mAnswerIntent = extras.getParcelable(EXTRA_ANSWER_INTENT); mDeclineIntent = extras.getParcelable(EXTRA_DECLINE_INTENT); mHangUpIntent = extras.getParcelable(EXTRA_HANG_UP_INTENT); + mAnswerButtonColor = extras.containsKey(EXTRA_ANSWER_COLOR) + ? extras.getInt(EXTRA_ANSWER_COLOR) : null; + mDeclineButtonColor = extras.containsKey(EXTRA_DECLINE_COLOR) + ? extras.getInt(EXTRA_DECLINE_COLOR) : null; } /** @@ -11901,6 +12034,7 @@ public class Notification implements Parcelable boolean mHideTitle; boolean mHideActions; boolean mHideProgress; + boolean mHideSnoozeButton; boolean mPromotePicture; boolean mAllowActionIcons; CharSequence title; @@ -11918,6 +12052,7 @@ public class Notification implements Parcelable mHideTitle = false; mHideActions = false; mHideProgress = false; + mHideSnoozeButton = false; mPromotePicture = false; mAllowActionIcons = false; title = null; @@ -11964,6 +12099,11 @@ public class Notification implements Parcelable return this; } + final StandardTemplateParams hideSnoozeButton(boolean hideSnoozeButton) { + this.mHideSnoozeButton = hideSnoozeButton; + return this; + } + final StandardTemplateParams promotePicture(boolean promotePicture) { this.mPromotePicture = promotePicture; return this; diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 671315f37b20..549bd4b9fe6a 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -16,6 +16,11 @@ package android.app; +import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY; +import static android.app.ActivityManager.INTENT_SENDER_BROADCAST; +import static android.app.ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE; +import static android.app.ActivityManager.INTENT_SENDER_SERVICE; + import android.Manifest.permission; import android.annotation.IntDef; import android.annotation.NonNull; @@ -25,6 +30,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemApi.Client; import android.annotation.TestApi; +import android.app.ActivityManager.PendingIntentInfo; import android.compat.Compatibility; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; @@ -55,6 +61,7 @@ import com.android.internal.os.IResultReceiver; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.Objects; /** * A description of an Intent and target action to perform with it. Instances @@ -122,6 +129,9 @@ public final class PendingIntent implements Parcelable { private IBinder mWhitelistToken; private ArraySet<CancelListener> mCancelListeners; + // cached pending intent information + private @Nullable PendingIntentInfo mCachedInfo; + /** * It is now required to specify either {@link #FLAG_IMMUTABLE} * or {@link #FLAG_MUTABLE} when creating a PendingIntent. @@ -455,15 +465,14 @@ public final class PendingIntent implements Parcelable { public static PendingIntent getActivityAsUser(Context context, int requestCode, @NonNull Intent intent, int flags, Bundle options, UserHandle user) { String packageName = context.getPackageName(); - String resolvedType = intent != null ? intent.resolveTypeIfNeeded( - context.getContentResolver()) : null; + String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver()); checkFlags(flags, packageName); try { intent.migrateExtraStreamToClipData(context); intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( - ActivityManager.INTENT_SENDER_ACTIVITY, packageName, + INTENT_SENDER_ACTIVITY, packageName, context.getAttributionTag(), null, null, requestCode, new Intent[] { intent }, resolvedType != null ? new String[] { resolvedType } : null, flags, options, user.getIdentifier()); @@ -596,7 +605,7 @@ public final class PendingIntent implements Parcelable { try { IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( - ActivityManager.INTENT_SENDER_ACTIVITY, packageName, + INTENT_SENDER_ACTIVITY, packageName, context.getAttributionTag(), null, null, requestCode, intents, resolvedTypes, flags, options, user.getIdentifier()); return target != null ? new PendingIntent(target) : null; @@ -630,7 +639,7 @@ public final class PendingIntent implements Parcelable { */ @SuppressWarnings("AndroidFrameworkPendingIntentMutability") public static PendingIntent getBroadcast(Context context, int requestCode, - Intent intent, @Flags int flags) { + @NonNull Intent intent, @Flags int flags) { return getBroadcastAsUser(context, requestCode, intent, flags, context.getUser()); } @@ -643,14 +652,13 @@ public final class PendingIntent implements Parcelable { public static PendingIntent getBroadcastAsUser(Context context, int requestCode, Intent intent, int flags, UserHandle userHandle) { String packageName = context.getPackageName(); - String resolvedType = intent != null ? intent.resolveTypeIfNeeded( - context.getContentResolver()) : null; + String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver()); checkFlags(flags, packageName); try { intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature( - ActivityManager.INTENT_SENDER_BROADCAST, packageName, + INTENT_SENDER_BROADCAST, packageName, context.getAttributionTag(), null, null, requestCode, new Intent[] { intent }, resolvedType != null ? new String[] { resolvedType } : null, flags, null, userHandle.getIdentifier()); @@ -687,7 +695,7 @@ public final class PendingIntent implements Parcelable { public static PendingIntent getService(Context context, int requestCode, @NonNull Intent intent, @Flags int flags) { return buildServicePendingIntent(context, requestCode, intent, flags, - ActivityManager.INTENT_SENDER_SERVICE); + INTENT_SENDER_SERVICE); } /** @@ -717,14 +725,13 @@ public final class PendingIntent implements Parcelable { public static PendingIntent getForegroundService(Context context, int requestCode, @NonNull Intent intent, @Flags int flags) { return buildServicePendingIntent(context, requestCode, intent, flags, - ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE); + INTENT_SENDER_FOREGROUND_SERVICE); } private static PendingIntent buildServicePendingIntent(Context context, int requestCode, Intent intent, int flags, int serviceKind) { String packageName = context.getPackageName(); - String resolvedType = intent != null ? intent.resolveTypeIfNeeded( - context.getContentResolver()) : null; + String resolvedType = intent.resolveTypeIfNeeded(context.getContentResolver()); checkFlags(flags, packageName); try { intent.prepareToLeaveProcess(context); @@ -746,6 +753,7 @@ public final class PendingIntent implements Parcelable { * @return Returns a IntentSender object that wraps the sender of PendingIntent * */ + @NonNull public IntentSender getIntentSender() { return new IntentSender(mTarget, mWhitelistToken); } @@ -758,6 +766,7 @@ public final class PendingIntent implements Parcelable { try { ActivityManager.getService().cancelIntentSender(mTarget); } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } @@ -1000,13 +1009,9 @@ public final class PendingIntent implements Parcelable { * @deprecated Renamed to {@link #getCreatorPackage()}. */ @Deprecated + @NonNull public String getTargetPackage() { - try { - return ActivityManager.getService() - .getPackageForIntentSender(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCreatorPackage(); } /** @@ -1024,17 +1029,11 @@ public final class PendingIntent implements Parcelable { * only use this information to identify who you expect to be interacting with * through a {@link #send} call, not who gave you the PendingIntent.</p> * - * @return The package name of the PendingIntent, or null if there is - * none associated with it. + * @return The package name of the PendingIntent. */ - @Nullable + @NonNull public String getCreatorPackage() { - try { - return ActivityManager.getService() - .getPackageForIntentSender(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().getCreatorPackage(); } /** @@ -1056,12 +1055,7 @@ public final class PendingIntent implements Parcelable { * none associated with it. */ public int getCreatorUid() { - try { - return ActivityManager.getService() - .getUidForIntentSender(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().getCreatorUid(); } /** @@ -1149,18 +1143,12 @@ public final class PendingIntent implements Parcelable { * only use this information to identify who you expect to be interacting with * through a {@link #send} call, not who gave you the PendingIntent.</p> * - * @return The user handle of the PendingIntent, or null if there is - * none associated with it. + * @return The user handle of the PendingIntent */ - @Nullable + @NonNull public UserHandle getCreatorUserHandle() { - try { - int uid = ActivityManager.getService() - .getUidForIntentSender(mTarget); - return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + int uid = getCachedInfo().getCreatorUid(); + return UserHandle.getUserHandleForUid(uid); } /** @@ -1180,12 +1168,7 @@ public final class PendingIntent implements Parcelable { * Check if this PendingIntent is marked with {@link #FLAG_IMMUTABLE}. */ public boolean isImmutable() { - try { - return ActivityManager.getService() - .isIntentSenderImmutable(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().isImmutable(); } /** @@ -1193,48 +1176,28 @@ public final class PendingIntent implements Parcelable { * {@link #getActivity} or {@link #getActivities}. */ public boolean isActivity() { - try { - return ActivityManager.getService() - .isIntentSenderAnActivity(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().getIntentSenderType() == INTENT_SENDER_ACTIVITY; } /** * @return TRUE if this {@link PendingIntent} was created with {@link #getForegroundService}. */ public boolean isForegroundService() { - try { - return ActivityManager.getService() - .isIntentSenderAForegroundService(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().getIntentSenderType() == INTENT_SENDER_FOREGROUND_SERVICE; } /** * @return TRUE if this {@link PendingIntent} was created with {@link #getService}. */ public boolean isService() { - try { - return ActivityManager.getService() - .isIntentSenderAService(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().getIntentSenderType() == INTENT_SENDER_SERVICE; } /** * @return TRUE if this {@link PendingIntent} was created with {@link #getBroadcast}. */ public boolean isBroadcast() { - try { - return ActivityManager.getService() - .isIntentSenderABroadcast(mTarget); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return getCachedInfo().getIntentSenderType() == INTENT_SENDER_BROADCAST; } /** @@ -1318,7 +1281,7 @@ public final class PendingIntent implements Parcelable { sb.append("PendingIntent{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(": "); - sb.append(mTarget != null ? mTarget.asBinder() : null); + sb.append(mTarget.asBinder()); sb.append('}'); return sb.toString(); } @@ -1326,9 +1289,7 @@ public final class PendingIntent implements Parcelable { /** @hide */ public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); - if (mTarget != null) { - proto.write(PendingIntentProto.TARGET, mTarget.asBinder().toString()); - } + proto.write(PendingIntentProto.TARGET, mTarget.asBinder().toString()); proto.end(token); } @@ -1345,8 +1306,7 @@ public final class PendingIntent implements Parcelable { } - public static final @android.annotation.NonNull Parcelable.Creator<PendingIntent> CREATOR - = new Parcelable.Creator<PendingIntent>() { + public static final @NonNull Creator<PendingIntent> CREATOR = new Creator<PendingIntent>() { public PendingIntent createFromParcel(Parcel in) { IBinder target = in.readStrongBinder(); return target != null @@ -1400,11 +1360,11 @@ public final class PendingIntent implements Parcelable { * @hide */ public PendingIntent(IIntentSender target) { - mTarget = target; + mTarget = Objects.requireNonNull(target); } /*package*/ PendingIntent(IBinder target, Object cookie) { - mTarget = IIntentSender.Stub.asInterface(target); + mTarget = Objects.requireNonNull(IIntentSender.Stub.asInterface(target)); if (cookie != null) { mWhitelistToken = (IBinder)cookie; } @@ -1433,4 +1393,16 @@ public final class PendingIntent implements Parcelable { */ void onCancelled(PendingIntent intent); } + + private PendingIntentInfo getCachedInfo() { + if (mCachedInfo == null) { + try { + mCachedInfo = ActivityManager.getService().getInfoForIntentSender(mTarget); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + return mCachedInfo; + } } diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 3798de921dc7..2ceea7f1a6a8 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -697,7 +697,8 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * service element of manifest file. The value of attribute * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p> * - * @throws IllegalStateException If the app targeting API is + * @throws ForegroundServiceStartNotAllowedException + * If the app targeting API is * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from * becoming foreground service due to background restriction. * @@ -738,8 +739,14 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * @param notification The Notification to be displayed. * @param foregroundServiceType must be a subset flags of manifest attribute * {@link android.R.attr#foregroundServiceType} flags. + * * @throws IllegalArgumentException if param foregroundServiceType is not subset of manifest * attribute {@link android.R.attr#foregroundServiceType}. + * @throws ForegroundServiceStartNotAllowedException + * If the app targeting API is + * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from + * becoming foreground service due to background restriction. + * * @see android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST */ public final void startForeground(int id, @NonNull Notification notification, diff --git a/core/java/android/app/ServiceStartNotAllowedException.java b/core/java/android/app/ServiceStartNotAllowedException.java new file mode 100644 index 000000000000..33285b2190eb --- /dev/null +++ b/core/java/android/app/ServiceStartNotAllowedException.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 android.app; + +import android.annotation.NonNull; + +/** + * Exception thrown when an app tries to start a {@link Service} when it's not allowed to do so. + */ +public abstract class ServiceStartNotAllowedException extends IllegalStateException { + ServiceStartNotAllowedException(@NonNull String message) { + super(message); + } + + /** + * Return either {@link ForegroundServiceStartNotAllowedException} or + * {@link BackgroundServiceStartNotAllowedException} + * @hide + */ + @NonNull + public static ServiceStartNotAllowedException newInstance(boolean foreground, + @NonNull String message) { + if (foreground) { + return new ForegroundServiceStartNotAllowedException(message); + } else { + return new BackgroundServiceStartNotAllowedException(message); + } + } +} diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 7d2bc1a5f8e2..afcf63b0f1e2 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -27,6 +27,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Binder; import android.os.Build; +import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -286,6 +287,23 @@ public class StatusBarManager { } /** + * Simulate notification feedback for testing + * + * @hide + */ + @TestApi + public void sendNotificationFeedback(@Nullable String key, @Nullable Bundle feedback) { + try { + final IStatusBarService svc = getService(); + if (svc != null) { + svc.onNotificationFeedbackReceived(key, feedback); + } + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** * Expand the notifications panel. * * @hide diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index c47b546653b9..e16e40b6d572 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -47,6 +47,7 @@ import android.app.usage.IUsageStatsManager; import android.app.usage.NetworkStatsManager; import android.app.usage.StorageStatsManager; import android.app.usage.UsageStatsManager; +import android.apphibernation.AppHibernationManager; import android.appwidget.AppWidgetManager; import android.bluetooth.BluetoothManager; import android.companion.CompanionDeviceManager; @@ -99,6 +100,7 @@ import android.hardware.input.InputManager; import android.hardware.iris.IIrisService; import android.hardware.iris.IrisManager; import android.hardware.lights.LightsManager; +import android.hardware.lights.SystemLightsManager; import android.hardware.location.ContextHubManager; import android.hardware.radio.RadioManager; import android.hardware.usb.IUsbManager; @@ -211,6 +213,7 @@ import android.view.autofill.AutofillManager; import android.view.autofill.IAutoFillManager; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.IContentCaptureManager; +import android.view.displayhash.DisplayHashManager; import android.view.inputmethod.InputMethodManager; import android.view.textclassifier.TextClassificationManager; import android.view.textservice.TextServicesManager; @@ -1343,7 +1346,7 @@ public final class SystemServiceRegistry { @Override public LightsManager createService(ContextImpl ctx) throws ServiceNotFoundException { - return new LightsManager(ctx); + return new SystemLightsManager(ctx); }}); registerService(Context.INCREMENTAL_SERVICE, IncrementalManager.class, new CachedServiceFetcher<IncrementalManager>() { @@ -1376,6 +1379,13 @@ public final class SystemServiceRegistry { IBinder b = ServiceManager.getServiceOrThrow(Context.APP_INTEGRITY_SERVICE); return new AppIntegrityManager(IAppIntegrityManager.Stub.asInterface(b)); }}); + registerService(Context.APP_HIBERNATION_SERVICE, AppHibernationManager.class, + new CachedServiceFetcher<AppHibernationManager>() { + @Override + public AppHibernationManager createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(Context.APP_HIBERNATION_SERVICE); + return b == null ? null : new AppHibernationManager(ctx); + }}); registerService(Context.DREAM_SERVICE, DreamManager.class, new CachedServiceFetcher<DreamManager>() { @Override @@ -1426,6 +1436,13 @@ public final class SystemServiceRegistry { } }); + registerService(Context.DISPLAY_HASH_SERVICE, DisplayHashManager.class, + new CachedServiceFetcher<DisplayHashManager>() { + @Override + public DisplayHashManager createService(ContextImpl ctx) { + return new DisplayHashManager(); + }}); + sInitializing = true; try { // Note: the following functions need to be @SystemApis, once they become mainline diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index 517ae24b75a6..f523a7d29713 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -43,7 +43,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void onActivityPinned(String packageName, int userId, int taskId, int stackId) + public void onActivityPinned(String packageName, int userId, int taskId, int rootTaskId) throws RemoteException { } @@ -66,7 +66,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void onActivityDismissingDockedStack() throws RemoteException { + public void onActivityDismissingDockedTask() throws RemoteException { } @Override @@ -193,4 +193,8 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override public void onTaskMovedToBack(RunningTaskInfo taskInfo) { } + + @Override + public void onLockTaskModeChanged(int mode) { + } } diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index 2d203f57a66f..0a8a73404a7b 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -16,9 +16,9 @@ package android.app; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -30,14 +30,19 @@ import android.util.Log; import android.util.Size; import com.android.internal.graphics.ColorUtils; +import com.android.internal.graphics.palette.CelebiQuantizer; import com.android.internal.graphics.palette.Palette; -import com.android.internal.graphics.palette.VariationalKMeansQuantizer; import com.android.internal.util.ContrastColorUtil; import java.io.FileOutputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * Provides information about the colors of a wallpaper. @@ -47,6 +52,13 @@ import java.util.List; * or {@link WallpaperColors#getTertiaryColor()}. */ public final class WallpaperColors implements Parcelable { + /** + * @hide + */ + @IntDef(prefix = "HINT_", value = {HINT_SUPPORTS_DARK_TEXT, HINT_SUPPORTS_DARK_THEME}, + flag = true) + @Retention(RetentionPolicy.SOURCE) + public @interface ColorsHints {} private static final boolean DEBUG_DARK_PIXELS = false; @@ -54,18 +66,14 @@ public final class WallpaperColors implements Parcelable { * Specifies that dark text is preferred over the current wallpaper for best presentation. * <p> * eg. A launcher may set its text color to black if this flag is specified. - * @hide */ - @SystemApi public static final int HINT_SUPPORTS_DARK_TEXT = 1 << 0; /** * Specifies that dark theme is preferred over the current wallpaper for best presentation. * <p> * eg. A launcher may set its drawer color to black if this flag is specified. - * @hide */ - @SystemApi public static final int HINT_SUPPORTS_DARK_THEME = 1 << 1; /** @@ -94,16 +102,21 @@ public final class WallpaperColors implements Parcelable { private static final float DARK_PIXEL_CONTRAST = 6f; private static final float MAX_DARK_AREA = 0.025f; - private final ArrayList<Color> mMainColors; + private final List<Color> mMainColors; + private final Map<Integer, Integer> mAllColors; private int mColorHints; public WallpaperColors(Parcel parcel) { mMainColors = new ArrayList<>(); + mAllColors = new HashMap<>(); final int count = parcel.readInt(); for (int i = 0; i < count; i++) { final int colorInt = parcel.readInt(); Color color = Color.valueOf(colorInt); mMainColors.add(color); + + final int population = parcel.readInt(); + mAllColors.put(colorInt, population); } mColorHints = parcel.readInt(); } @@ -166,39 +179,22 @@ public final class WallpaperColors implements Parcelable { } final Palette palette = Palette - .from(bitmap) - .setQuantizer(new VariationalKMeansQuantizer()) - .maximumColorCount(5) - .clearFilters() + .from(bitmap, new CelebiQuantizer()) + .maximumColorCount(256) .resizeBitmapArea(MAX_WALLPAPER_EXTRACTION_AREA) .generate(); - // Remove insignificant colors and sort swatches by population final ArrayList<Palette.Swatch> swatches = new ArrayList<>(palette.getSwatches()); - final float minColorArea = bitmap.getWidth() * bitmap.getHeight() * MIN_COLOR_OCCURRENCE; - swatches.removeIf(s -> s.getPopulation() < minColorArea); swatches.sort((a, b) -> b.getPopulation() - a.getPopulation()); final int swatchesSize = swatches.size(); - Color primary = null, secondary = null, tertiary = null; - swatchLoop: + final Map<Integer, Integer> populationByColor = new HashMap<>(); for (int i = 0; i < swatchesSize; i++) { - Color color = Color.valueOf(swatches.get(i).getRgb()); - switch (i) { - case 0: - primary = color; - break; - case 1: - secondary = color; - break; - case 2: - tertiary = color; - break; - default: - // out of bounds - break swatchLoop; - } + Palette.Swatch swatch = swatches.get(i); + int colorInt = swatch.getInt(); + populationByColor.put(colorInt, swatch.getPopulation()); + } int hints = calculateDarkHints(bitmap); @@ -207,7 +203,7 @@ public final class WallpaperColors implements Parcelable { bitmap.recycle(); } - return new WallpaperColors(primary, secondary, tertiary, HINT_FROM_BITMAP | hints); + return new WallpaperColors(populationByColor, HINT_FROM_BITMAP | hints); } /** @@ -238,24 +234,25 @@ public final class WallpaperColors implements Parcelable { * @param primaryColor Primary color. * @param secondaryColor Secondary color. * @param tertiaryColor Tertiary color. - * @param colorHints A combination of WallpaperColor hints. - * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT + * @param colorHints A combination of color hints. * @see WallpaperColors#fromBitmap(Bitmap) * @see WallpaperColors#fromDrawable(Drawable) - * @hide */ - @SystemApi public WallpaperColors(@NonNull Color primaryColor, @Nullable Color secondaryColor, - @Nullable Color tertiaryColor, int colorHints) { + @Nullable Color tertiaryColor, @ColorsHints int colorHints) { if (primaryColor == null) { throw new IllegalArgumentException("Primary color should never be null."); } mMainColors = new ArrayList<>(3); + mAllColors = new HashMap<>(); + mMainColors.add(primaryColor); + mAllColors.put(primaryColor.toArgb(), 0); if (secondaryColor != null) { mMainColors.add(secondaryColor); + mAllColors.put(secondaryColor.toArgb(), 0); } if (tertiaryColor != null) { if (secondaryColor == null) { @@ -263,8 +260,33 @@ public final class WallpaperColors implements Parcelable { + "secondaryColor is null"); } mMainColors.add(tertiaryColor); + mAllColors.put(tertiaryColor.toArgb(), 0); } + mColorHints = colorHints; + } + /** + * Constructs a new object from a set of colors, where hints can be specified. + * + * @param populationByColor Map with keys of colors, and value representing the number of + * occurrences of color in the wallpaper. + * @param colorHints A combination of color hints. + * @hide + * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT + * @see WallpaperColors#fromBitmap(Bitmap) + * @see WallpaperColors#fromDrawable(Drawable) + */ + public WallpaperColors(@NonNull Map<Integer, Integer> populationByColor, + @ColorsHints int colorHints) { + mAllColors = populationByColor; + + ArrayList<Map.Entry<Integer, Integer>> mapEntries = new ArrayList( + populationByColor.entrySet()); + mapEntries.sort((a, b) -> + a.getValue().compareTo(b.getValue()) + ); + mMainColors = mapEntries.stream().map(entry -> Color.valueOf(entry.getKey())).collect( + Collectors.toList()); mColorHints = colorHints; } @@ -293,6 +315,9 @@ public final class WallpaperColors implements Parcelable { for (int i = 0; i < count; i++) { Color color = mainColors.get(i); dest.writeInt(color.toArgb()); + Integer population = mAllColors.get(color.toArgb()); + int populationInt = (population != null) ? population : 0; + dest.writeInt(populationInt); } dest.writeInt(mColorHints); } @@ -336,6 +361,17 @@ public final class WallpaperColors implements Parcelable { return Collections.unmodifiableList(mMainColors); } + /** + * Map of all colors. Key is rgb integer, value is importance of color. + * + * @return List of colors. + * @hide + */ + public @NonNull Map<Integer, Integer> getAllColors() { + return Collections.unmodifiableMap(mAllColors); + } + + @Override public boolean equals(@Nullable Object o) { if (o == null || getClass() != o.getClass()) { @@ -353,27 +389,14 @@ public final class WallpaperColors implements Parcelable { } /** - * Combination of WallpaperColor hints. - * - * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT - * @return True if dark text is supported. - * @hide + * Returns the color hints for this instance. + * @return The color hints. */ - @SystemApi - public int getColorHints() { + public @ColorsHints int getColorHints() { return mColorHints; } /** - * @param colorHints Combination of WallpaperColors hints. - * @see WallpaperColors#HINT_SUPPORTS_DARK_TEXT - * @hide - */ - public void setColorHints(int colorHints) { - mColorHints = colorHints; - } - - /** * Checks if image is bright and clean enough to support light text. * * @param source What to read. diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index ac2f22357013..bd99348b235a 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -66,6 +66,7 @@ import android.os.RemoteException; import android.os.StrictMode; import android.os.SystemProperties; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.Log; import android.util.Pair; import android.view.Display; @@ -107,6 +108,8 @@ public class WallpaperManager { private static boolean DEBUG = false; private float mWallpaperXStep = -1; private float mWallpaperYStep = -1; + private static final @NonNull RectF LOCAL_COLOR_BOUNDS = + new RectF(0, 0, 1, 1); /** {@hide} */ private static final String PROP_WALLPAPER = "ro.config.wallpaper"; @@ -114,6 +117,9 @@ public class WallpaperManager { private static final String PROP_LOCK_WALLPAPER = "ro.config.lock_wallpaper"; /** {@hide} */ private static final String PROP_WALLPAPER_COMPONENT = "ro.config.wallpaper_component"; + /** {@hide} */ + private static final String VALUE_CMF_COLOR = + android.os.SystemProperties.get("ro.boot.hardware.color"); /** * Activity Action: Show settings for choosing wallpaper. Do not use directly to construct @@ -309,6 +315,8 @@ public class WallpaperManager { private int mCachedWallpaperUserId; private Bitmap mDefaultWallpaper; private Handler mMainLooperHandler; + private ArrayMap<LocalWallpaperColorConsumer, ILocalWallpaperColorConsumer> + mLocalColorCallbacks = new ArrayMap<>(); Globals(IWallpaperManager service, Looper looper) { mService = service; @@ -350,6 +358,40 @@ public class WallpaperManager { } } + private ILocalWallpaperColorConsumer wrap(LocalWallpaperColorConsumer callback) { + ILocalWallpaperColorConsumer callback2 = new ILocalWallpaperColorConsumer.Stub() { + @Override + public void onColorsChanged(RectF area, WallpaperColors colors) { + callback.onColorsChanged(area, colors); + } + }; + mLocalColorCallbacks.put(callback, callback2); + return callback2; + } + + public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback, + @NonNull List<RectF> regions, int which, int userId, int displayId) { + try { + mService.addOnLocalColorsChangedListener(wrap(callback) , regions, which, + userId, displayId); + } catch (RemoteException e) { + // Can't get colors, connection lost. + } + } + + public void removeOnColorsChangedListener( + @NonNull LocalWallpaperColorConsumer callback, int which, int userId, + int displayId) { + ILocalWallpaperColorConsumer callback2 = mLocalColorCallbacks.remove(callback); + if (callback2 == null) return; + try { + mService.removeOnLocalColorsChangedListener( + callback2, which, userId, displayId); + } catch (RemoteException e) { + // Can't get colors, connection lost. + } + } + /** * Stop listening to wallpaper color events. * @@ -1057,6 +1099,29 @@ public class WallpaperManager { } /** + * @hide + */ + public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback, + List<RectF> regions) throws IllegalArgumentException { + for (RectF region : regions) { + if (!LOCAL_COLOR_BOUNDS.contains(region)) { + throw new IllegalArgumentException("Regions must be within bounds " + + LOCAL_COLOR_BOUNDS); + } + } + sGlobals.addOnColorsChangedListener(callback, regions, FLAG_SYSTEM, + mContext.getUserId(), mContext.getDisplayId()); + } + + /** + * @hide + */ + public void removeOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback) { + sGlobals.removeOnColorsChangedListener(callback, FLAG_SYSTEM, mContext.getUserId(), + mContext.getDisplayId()); + } + + /** * Version of {@link #getWallpaperFile(int)} that can access the wallpaper data * for a given user. The caller must hold the INTERACT_ACROSS_USERS_FULL * permission to access another user's wallpaper data. @@ -2002,7 +2067,11 @@ public class WallpaperManager { return null; } else { whichProp = PROP_WALLPAPER; - defaultResId = com.android.internal.R.drawable.default_wallpaper; + final int defaultColorResId = context.getResources().getIdentifier( + "default_wallpaper_" + VALUE_CMF_COLOR, "drawable", "android"); + defaultResId = + defaultColorResId == 0 ? com.android.internal.R.drawable.default_wallpaper + : defaultColorResId; } final String path = SystemProperties.get(whichProp); if (!TextUtils.isEmpty(path)) { @@ -2202,4 +2271,18 @@ public class WallpaperManager { onColorsChanged(colors, which); } } + + /** + * Callback to update a consumer with a local color change + * @hide + */ + public interface LocalWallpaperColorConsumer { + + /** + * Gets called when a color of an area gets updated + * @param area + * @param colors + */ + void onColorsChanged(RectF area, WallpaperColors colors); + } } diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index 4ae1670e9041..d04ca1d9a48e 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -57,7 +57,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu * TODO: Investigate combining with {@link #mAppBounds}. Can the latter be a product of the * former? */ - private Rect mBounds = new Rect(); + private final Rect mBounds = new Rect(); /** * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of @@ -71,7 +71,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu * The maximum {@link Rect} bounds that an app can expect. It is used to report value of * {@link WindowManager#getMaximumWindowMetrics()}. */ - private Rect mMaxBounds = new Rect(); + private final Rect mMaxBounds = new Rect(); /** * The current rotation of this window container relative to the default @@ -240,9 +240,9 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(mBounds, flags); - dest.writeParcelable(mAppBounds, flags); - dest.writeParcelable(mMaxBounds, flags); + mBounds.writeToParcel(dest, flags); + dest.writeTypedObject(mAppBounds, flags); + mMaxBounds.writeToParcel(dest, flags); dest.writeInt(mWindowingMode); dest.writeInt(mActivityType); dest.writeInt(mAlwaysOnTop); @@ -250,10 +250,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu dest.writeInt(mDisplayWindowingMode); } - private void readFromParcel(Parcel source) { - mBounds = source.readParcelable(Rect.class.getClassLoader()); - mAppBounds = source.readParcelable(Rect.class.getClassLoader()); - mMaxBounds = source.readParcelable(Rect.class.getClassLoader()); + /** @hide */ + public void readFromParcel(@NonNull Parcel source) { + mBounds.readFromParcel(source); + mAppBounds = source.readTypedObject(Rect.CREATOR); + mMaxBounds.readFromParcel(source); mWindowingMode = source.readInt(); mActivityType = source.readInt(); mAlwaysOnTop = source.readInt(); @@ -693,9 +694,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu } protoOutputStream.write(WINDOWING_MODE, mWindowingMode); protoOutputStream.write(ACTIVITY_TYPE, mActivityType); - if (mBounds != null) { - mBounds.dumpDebug(protoOutputStream, BOUNDS); - } + mBounds.dumpDebug(protoOutputStream, BOUNDS); mMaxBounds.dumpDebug(protoOutputStream, MAX_BOUNDS); protoOutputStream.end(token); } @@ -719,11 +718,9 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu mAppBounds.readFromProto(proto, APP_BOUNDS); break; case (int) BOUNDS: - mBounds = new Rect(); mBounds.readFromProto(proto, BOUNDS); break; case (int) MAX_BOUNDS: - mMaxBounds = new Rect(); mMaxBounds.readFromProto(proto, MAX_BOUNDS); break; case (int) WINDOWING_MODE: diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index 4dbff0c06423..cccc9294c2d5 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -585,6 +585,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * @param intent The received intent as per {@link #onReceive}. */ public void onEnabled(@NonNull Context context, @NonNull Intent intent) { + if (LOCAL_LOGV) { + Log.v(TAG, getClass().getName() + ".onEnabled() on user " + context.getUserId()); + } } /** @@ -600,6 +603,10 @@ public class DeviceAdminReceiver extends BroadcastReceiver { */ public @Nullable CharSequence onDisableRequested(@NonNull Context context, @NonNull Intent intent) { + if (LOCAL_LOGV) { + Log.v(TAG, getClass().getName() + ".onDisableRequested() on user " + + context.getUserId()); + } return null; } @@ -612,6 +619,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * @param intent The received intent as per {@link #onReceive}. */ public void onDisabled(@NonNull Context context, @NonNull Intent intent) { + if (LOCAL_LOGV) { + Log.v(TAG, getClass().getName() + ".onDisabled() on user " + context.getUserId()); + } } /** @@ -786,6 +796,10 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * @param intent The received intent as per {@link #onReceive}. */ public void onProfileProvisioningComplete(@NonNull Context context, @NonNull Intent intent) { + if (LOCAL_LOGV) { + Log.v(TAG, getClass().getName() + ".onProfileProvisioningComplete() on user " + + context.getUserId()); + } } /** @@ -961,6 +975,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver { */ public void onUserAdded(@NonNull Context context, @NonNull Intent intent, @NonNull UserHandle addedUser) { + if (LOCAL_LOGV) { + Log.v(TAG, getClass().getName() + ".onUserAdded() on user " + context.getUserId()); + } } /** @@ -974,6 +991,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver { */ public void onUserRemoved(@NonNull Context context, @NonNull Intent intent, @NonNull UserHandle removedUser) { + if (LOCAL_LOGV) { + Log.v(TAG, getClass().getName() + ".onUserRemoved() on user " + context.getUserId()); + } } /** @@ -987,6 +1007,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver { */ public void onUserStarted(@NonNull Context context, @NonNull Intent intent, @NonNull UserHandle startedUser) { + if (LOCAL_LOGV) { + Log.v(TAG, getClass().getName() + ".onUserStarted() on user " + context.getUserId()); + } } /** @@ -1000,6 +1023,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver { */ public void onUserStopped(@NonNull Context context, @NonNull Intent intent, @NonNull UserHandle stoppedUser) { + if (LOCAL_LOGV) { + Log.v(TAG, getClass().getName() + ".onUserStopped() on user " + context.getUserId()); + } } /** @@ -1013,6 +1039,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver { */ public void onUserSwitched(@NonNull Context context, @NonNull Intent intent, @NonNull UserHandle switchedUser) { + if (LOCAL_LOGV) { + Log.v(TAG, getClass().getName() + ".onUserSwitched() on user " + context.getUserId()); + } } /** @@ -1073,6 +1102,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { private void onOperationSafetyStateChanged(Context context, Intent intent) { if (!hasRequiredExtra(intent, EXTRA_OPERATION_SAFETY_REASON) || !hasRequiredExtra(intent, EXTRA_OPERATION_SAFETY_STATE)) { + Log.w(TAG, "Igoring intent that's missing required extras"); return; } @@ -1084,7 +1114,6 @@ public class DeviceAdminReceiver extends BroadcastReceiver { } boolean isSafe = intent.getBooleanExtra(EXTRA_OPERATION_SAFETY_STATE, /* defaultValue=*/ false); - onOperationSafetyStateChanged(context, reason, isSafe); } @@ -1104,7 +1133,8 @@ public class DeviceAdminReceiver extends BroadcastReceiver { public void onReceive(@NonNull Context context, @NonNull Intent intent) { String action = intent.getAction(); if (LOCAL_LOGV) { - Log.v(TAG, "onReceive(): received " + action + " on user " + context.getUserId()); + Log.v(TAG, getClass().getName() + ".onReceive(): received " + action + " on user " + + context.getUserId()); } if (ACTION_PASSWORD_CHANGED.equals(action)) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 59e5144113c9..bb1ff6051d56 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2746,6 +2746,32 @@ public class DevicePolicyManager { @Retention(RetentionPolicy.SOURCE) public @interface PersonalAppsSuspensionReason {} + /** + * The default device owner type for a managed device. + * + * @hide + */ + public static final int DEVICE_OWNER_TYPE_DEFAULT = 0; + + /** + * The device owner type for a financed device. + * + * @hide + */ + public static final int DEVICE_OWNER_TYPE_FINANCED = 1; + + /** + * Different device owner types for a managed device. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "DEVICE_OWNER_TYPE_" }, value = { + DEVICE_OWNER_TYPE_DEFAULT, + DEVICE_OWNER_TYPE_FINANCED + }) + public @interface DeviceOwnerType {} + /** @hide */ @TestApi public static final int OPERATION_LOCK_NOW = 1; @@ -7276,7 +7302,12 @@ public class DevicePolicyManager { /** * @hide */ + @TestApi @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @RequiresPermission(allOf = { + android.Manifest.permission.MANAGE_DEVICE_ADMINS, + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL + }) public void setActiveAdmin(@NonNull ComponentName policyReceiver, boolean refreshing, int userHandle) { if (mService != null) { @@ -7453,8 +7484,10 @@ public class DevicePolicyManager { * @throws IllegalArgumentException if the package name is null or invalid * @throws IllegalStateException If the preconditions mentioned are not met. */ - public boolean setDeviceOwner(ComponentName who, String ownerName, int userId) - throws IllegalArgumentException, IllegalStateException { + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + public boolean setDeviceOwner( + @NonNull ComponentName who, @Nullable String ownerName, @UserIdInt int userId) { if (mService != null) { try { return mService.setDeviceOwner(who, ownerName, userId); @@ -7521,7 +7554,10 @@ public class DevicePolicyManager { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, + }) public ComponentName getDeviceOwnerComponentOnAnyUser() { return getDeviceOwnerComponentInner(/* callingUserOnly =*/ false); } @@ -10477,9 +10513,10 @@ public class DevicePolicyManager { /** * Reset record of previous system update freeze period the device went through. - * Only callable by ADB. * @hide */ + @TestApi + @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord() { throwIfParentInstance("clearSystemUpdatePolicyFreezePeriodRecord"); if (mService == null) { @@ -11207,9 +11244,11 @@ public class DevicePolicyManager { /** * Makes all accumulated network logs available to DPC in a new batch. - * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0. + * If throttled, returns time to wait in milliseconds, otherwise 0. * @hide */ + @TestApi + @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs() { if (mService == null) { return -1; @@ -11223,9 +11262,11 @@ public class DevicePolicyManager { /** * Forces a batch of security logs to be fetched from logd and makes it available for DPC. - * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0. + * If throttled, returns time to wait in milliseconds, otherwise 0. * @hide */ + @TestApi + @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs() { if (mService == null) { return 0; @@ -11657,7 +11698,10 @@ public class DevicePolicyManager { * @throws SecurityException if the caller is not shell / root or the admin package * isn't a test application see {@link ApplicationInfo#FLAG_TEST_APP}. */ - public void forceRemoveActiveAdmin(ComponentName adminReceiver, int userHandle) { + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + public void forceRemoveActiveAdmin( + @NonNull ComponentName adminReceiver, @UserIdInt int userHandle) { try { mService.forceRemoveActiveAdmin(adminReceiver, userHandle); } catch (RemoteException re) { @@ -12727,8 +12771,11 @@ public class DevicePolicyManager { * * @hide */ - @RequiresPermission(value = android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, - conditional = true) + @TestApi + @RequiresPermission(anyOf = { + android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS + }, conditional = true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull ComponentName who) { if (mService == null) { return; @@ -13460,6 +13507,57 @@ public class DevicePolicyManager { } /** + * Sets the device owner type for a managed device (e.g. financed device). + * + * @param admin The {@link DeviceAdminReceiver} that is the device owner. + * @param deviceOwnerType The device owner type is set to. Use + * {@link #DEVICE_OWNER_TYPE_DEFAULT} for the default device owner type. Use + * {@link #DEVICE_OWNER_TYPE_FINANCED} for the financed device owner type. + * + * @throws IllegalStateException When admin is not the device owner, or there is no device + * owner, or attempting to set the device owner type again for the same admin. + * @throws SecurityException If the caller does not have the permission + * {@link permission#MANAGE_PROFILE_AND_DEVICE_OWNERS}. + * + * @hide + */ + public void setDeviceOwnerType(@NonNull ComponentName admin, + @DeviceOwnerType int deviceOwnerType) { + throwIfParentInstance("setDeviceOwnerType"); + if (mService != null) { + try { + mService.setDeviceOwnerType(admin, deviceOwnerType); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + } + + /** + * Returns the device owner type for the admin used in + * {@link #setDeviceOwnerType(ComponentName, int)}. {@link #DEVICE_OWNER_TYPE_DEFAULT} + * would be returned when the device owner type is not set for the device owner admin. + * + * @param admin The {@link DeviceAdminReceiver} that is the device owner. + * + * @throws IllegalStateException When admin is not the device owner or there is no device owner. + * + * @hide + */ + @DeviceOwnerType + public int getDeviceOwnerType(@NonNull ComponentName admin) { + throwIfParentInstance("getDeviceOwnerType"); + if (mService != null) { + try { + return mService.getDeviceOwnerType(admin); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + return DEVICE_OWNER_TYPE_DEFAULT; + } + + /** * Called by device owner or profile owner of an organization-owned managed profile to * enable or disable USB data signaling for the device. When disabled, USB data connections * (except from charging functions) are prohibited. diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 8a87b16b760b..ac1592d2d2a1 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -503,6 +503,9 @@ interface IDevicePolicyManager { UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage); void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage); + void setDeviceOwnerType(in ComponentName admin, in int deviceOwnerType); + int getDeviceOwnerType(in ComponentName admin); + void resetDefaultCrossProfileIntentFilters(int userId); boolean canAdminGrantSensorsPermissionsForUser(int userId); diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 85cfe835c28d..22492ccd0373 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -564,10 +564,6 @@ public abstract class BackupAgent extends ContextWrapper { @VisibleForTesting public IncludeExcludeRules getIncludeExcludeRules(FullBackup.BackupScheme backupScheme) throws IOException, XmlPullParserException { - if (isDeviceToDeviceMigration()) { - return IncludeExcludeRules.emptyRules(); - } - Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap; ArraySet<PathWithRequiredFlags> manifestExcludeSet; diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index 673de8fa7c8c..dae565e12fd7 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -361,36 +361,7 @@ public class BackupManager { try { // All packages, current transport IRestoreSession binder = - sService.beginRestoreSessionForUser(mContext.getUserId(), null, null, - OperationType.BACKUP); - if (binder != null) { - session = new RestoreSession(mContext, binder); - } - } catch (RemoteException e) { - Log.e(TAG, "beginRestoreSession() couldn't connect"); - } - } - return session; - } - - /** - * Begin the process of restoring data from backup. See the - * {@link android.app.backup.RestoreSession} class for documentation on that process. - * - * @param operationType Type of the operation, see {@link OperationType} - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.BACKUP) - public RestoreSession beginRestoreSession(@OperationType int operationType) { - RestoreSession session = null; - checkServiceBinder(); - if (sService != null) { - try { - // All packages, current transport - IRestoreSession binder = - sService.beginRestoreSessionForUser(mContext.getUserId(), null, null, - operationType); + sService.beginRestoreSessionForUser(mContext.getUserId(), null, null); if (binder != null) { session = new RestoreSession(mContext, binder); } @@ -801,7 +772,7 @@ public class BackupManager { @SystemApi @RequiresPermission(android.Manifest.permission.BACKUP) public int requestBackup(String[] packages, BackupObserver observer) { - return requestBackup(packages, observer, null, 0, OperationType.BACKUP); + return requestBackup(packages, observer, null, 0); } /** @@ -826,31 +797,6 @@ public class BackupManager { @RequiresPermission(android.Manifest.permission.BACKUP) public int requestBackup(String[] packages, BackupObserver observer, BackupManagerMonitor monitor, int flags) { - return requestBackup(packages, observer, monitor, flags, OperationType.BACKUP); - } - - /** - * Request an immediate backup, providing an observer to which results of the backup operation - * will be published. The Android backup system will decide for each package whether it will - * be full app data backup or key/value-pair-based backup. - * - * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all - * provided packages using the remote transport. - * - * @param packages List of package names to backup. - * @param observer The {@link BackupObserver} to receive callbacks during the backup - * operation. Could be {@code null}. - * @param monitor The {@link BackupManagerMonitorWrapper} to receive callbacks of important - * events during the backup operation. Could be {@code null}. - * @param flags {@link #FLAG_NON_INCREMENTAL_BACKUP}. - * @param operationType {@link OperationType} - * @return {@link BackupManager#SUCCESS} on success; nonzero on error. - * @throws IllegalArgumentException on null or empty {@code packages} param. - * @hide - */ - @RequiresPermission(android.Manifest.permission.BACKUP) - public int requestBackup(String[] packages, BackupObserver observer, - BackupManagerMonitor monitor, int flags, @OperationType int operationType) { checkServiceBinder(); if (sService != null) { try { @@ -860,8 +806,7 @@ public class BackupManager { BackupManagerMonitorWrapper monitorWrapper = monitor == null ? null : new BackupManagerMonitorWrapper(monitor); - return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags, - operationType); + return sService.requestBackup(packages, observerWrapper, monitorWrapper, flags); } catch (RemoteException e) { Log.e(TAG, "requestBackup() couldn't connect"); } diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index f7ed6f1f2feb..829b6cd43934 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -19,10 +19,16 @@ package android.app.backup; import static android.app.backup.BackupManager.OperationType; import android.annotation.Nullable; +import android.annotation.StringDef; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.XmlResourceParser; +import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.storage.StorageManager; @@ -33,6 +39,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -94,6 +101,23 @@ public class FullBackup { "fakeClientSideEncryption"; /** + * When this change is enabled, include / exclude rules specified via + * {@code android:fullBackupContent} are ignored during D2D transfers. + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) + private static final long IGNORE_FULL_BACKUP_CONTENT_IN_D2D = 180523564L; + + @StringDef({ + ConfigSection.CLOUD_BACKUP, + ConfigSection.DEVICE_TRANSFER + }) + private @interface ConfigSection { + String CLOUD_BACKUP = "cloud-backup"; + String DEVICE_TRANSFER = "device-transfer"; + } + + /** * Identify {@link BackupScheme} object by package and operation type * (see {@link OperationType}) it corresponds to. */ @@ -273,6 +297,7 @@ public class FullBackup { private final static String TAG_INCLUDE = "include"; private final static String TAG_EXCLUDE = "exclude"; + final int mDataExtractionRules; final int mFullBackupContent; @OperationType final int mOperationType; final PackageManager mPackageManager; @@ -394,7 +419,10 @@ public class FullBackup { ArraySet<PathWithRequiredFlags> mExcludes; BackupScheme(Context context, @OperationType int operationType) { - mFullBackupContent = context.getApplicationInfo().fullBackupContent; + ApplicationInfo applicationInfo = context.getApplicationInfo(); + + mDataExtractionRules = applicationInfo.dataExtractionRulesRes; + mFullBackupContent = applicationInfo.fullBackupContent; mOperationType = operationType; mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); mPackageManager = context.getPackageManager(); @@ -468,34 +496,101 @@ public class FullBackup { mIncludes = new ArrayMap<String, Set<PathWithRequiredFlags>>(); mExcludes = new ArraySet<PathWithRequiredFlags>(); - if (mFullBackupContent == 0) { - // android:fullBackupContent="true" which means that we'll do everything. + if (mFullBackupContent == 0 && mDataExtractionRules == 0) { + // No scheme specified via either new or legacy config, will copy everything. if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { Log.v(FullBackup.TAG_XML_PARSER, "android:fullBackupContent - \"true\""); } } else { - // android:fullBackupContent="@xml/some_resource". + // Scheme is present. if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { - Log.v(FullBackup.TAG_XML_PARSER, - "android:fullBackupContent - found xml resource"); + Log.v(FullBackup.TAG_XML_PARSER, "Found xml scheme: " + + "android:fullBackupContent=" + mFullBackupContent + + "; android:dataExtractionRules=" + mDataExtractionRules); } - XmlResourceParser parser = null; + try { - parser = mPackageManager - .getResourcesForApplication(mPackageName) - .getXml(mFullBackupContent); - parseBackupSchemeFromXmlLocked(parser, mExcludes, mIncludes); + parseSchemeForOperationType(mOperationType); } catch (PackageManager.NameNotFoundException e) { // Throw it as an IOException throw new IOException(e); - } finally { - if (parser != null) { - parser.close(); - } } } } + private void parseSchemeForOperationType(@OperationType int operationType) + throws PackageManager.NameNotFoundException, IOException, XmlPullParserException { + String configSection = getConfigSectionForOperationType(operationType); + if (configSection == null) { + Slog.w(TAG, "Given operation type isn't supported by backup scheme: " + + operationType); + return; + } + + if (mDataExtractionRules != 0) { + // New config is present. Use it if it has configuration for this operation + // type. + try (XmlResourceParser parser = getParserForResource(mDataExtractionRules)) { + parseNewBackupSchemeFromXmlLocked(parser, configSection, mExcludes, mIncludes); + } + if (!mExcludes.isEmpty() || !mIncludes.isEmpty()) { + // Found configuration in the new config, we will use it. + return; + } + } + + if (operationType == OperationType.MIGRATION + && CompatChanges.isChangeEnabled(IGNORE_FULL_BACKUP_CONTENT_IN_D2D)) { + return; + } + + if (mFullBackupContent != 0) { + // Fall back to the old config. + try (XmlResourceParser parser = getParserForResource(mFullBackupContent)) { + parseBackupSchemeFromXmlLocked(parser, mExcludes, mIncludes); + } + } + } + + @Nullable + private String getConfigSectionForOperationType(@OperationType int operationType) { + switch (operationType) { + case OperationType.BACKUP: + return ConfigSection.CLOUD_BACKUP; + case OperationType.MIGRATION: + return ConfigSection.DEVICE_TRANSFER; + default: + return null; + } + } + + private XmlResourceParser getParserForResource(int resourceId) + throws PackageManager.NameNotFoundException { + return mPackageManager + .getResourcesForApplication(mPackageName) + .getXml(resourceId); + } + + private void parseNewBackupSchemeFromXmlLocked(XmlPullParser parser, + @ConfigSection String configSection, + Set<PathWithRequiredFlags> excludes, + Map<String, Set<PathWithRequiredFlags>> includes) + throws IOException, XmlPullParserException { + verifyTopLevelTag(parser, "data-extraction-rules"); + + int event; + while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (event != XmlPullParser.START_TAG || !configSection.equals(parser.getName())) { + continue; + } + + // TODO(b/180523028): Parse required attributes for rules (e.g. encryption). + parseRules(parser, excludes, includes, Optional.of(0), configSection); + } + + logParsingResults(excludes, includes); + } + @VisibleForTesting public void parseBackupSchemeFromXmlLocked(XmlPullParser parser, Set<PathWithRequiredFlags> excludes, @@ -503,7 +598,7 @@ public class FullBackup { throws IOException, XmlPullParserException { verifyTopLevelTag(parser, "full-backup-content"); - parseRules(parser, excludes, includes, Optional.empty()); + parseRules(parser, excludes, includes, Optional.empty(), "full-backup-content"); logParsingResults(excludes, includes); } @@ -532,10 +627,12 @@ public class FullBackup { private void parseRules(XmlPullParser parser, Set<PathWithRequiredFlags> excludes, Map<String, Set<PathWithRequiredFlags>> includes, - Optional<Integer> maybeRequiredFlags) + Optional<Integer> maybeRequiredFlags, + String endingTag) throws IOException, XmlPullParserException { int event; - while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { + while ((event = parser.next()) != XmlPullParser.END_DOCUMENT + && !parser.getName().equals(endingTag)) { switch (event) { case XmlPullParser.START_TAG: validateInnerTagContents(parser); diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index e1bbc08e72f3..bf5be95c4ab0 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -547,11 +547,9 @@ interface IBackupManager { * set can be restored. * @param transportID The name of the transport to use for the restore operation. * May be null, in which case the current active transport is used. - * @param operationType Type of the operation, see {@link BackupManager#OperationType} * @return An interface to the restore session, or null on error. */ - IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID, - int operationType); + IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID); /** * Notify the backup manager that a BackupAgent has completed the operation @@ -680,7 +678,7 @@ interface IBackupManager { * {@link android.app.backup.IBackupManager.requestBackupForUser} for the calling user id. */ int requestBackup(in String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, - int flags, int operationType); + int flags); /** * Cancel all running backups. After this call returns, no currently running backups will diff --git a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java index b3f9e31abaa4..33f83026ba0f 100644 --- a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java +++ b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java @@ -19,7 +19,9 @@ package android.app.contentsuggestions; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.annotation.UserIdInt; import android.graphics.Bitmap; import android.os.Binder; @@ -220,6 +222,72 @@ public final class ContentSuggestionsManager { } /** + * Resets the temporary service implementation to the default component. + * + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS) + public void resetTemporaryService(@UserIdInt int userId) { + if (mService == null) { + Log.e(TAG, "resetTemporaryService called, but no ContentSuggestionsManager " + + "configured"); + return; + } + try { + mService.resetTemporaryService(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Temporarily sets the service implementation. + * + * @param userId user Id to set the temporary service on. + * @param serviceName name of the new component + * @param duration how long the change will be valid (the service will be automatically reset + * to the default component after this timeout expires). + * + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS) + public void setTemporaryService( + @UserIdInt int userId, @NonNull String serviceName, int duration) { + if (mService == null) { + Log.e(TAG, "setTemporaryService called, but no ContentSuggestionsManager " + + "configured"); + return; + } + try { + mService.setTemporaryService(userId, serviceName, duration); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets whether the default service should be used. + * + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS) + public void setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) { + if (mService == null) { + Log.e(TAG, "setDefaultServiceEnabled called, but no ContentSuggestionsManager " + + "configured"); + return; + } + try { + mService.setDefaultServiceEnabled(userId, enabled); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Callback to receive content selections from * {@link #suggestContentSelections(SelectionsRequest, Executor, SelectionsCallback)}. */ diff --git a/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl b/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl index 8e6338babf22..9350eeb0ee66 100644 --- a/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl +++ b/core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl @@ -45,4 +45,7 @@ oneway interface IContentSuggestionsManager { in IClassificationsCallback callback); void notifyInteraction(int userId, in String requestId, in Bundle interaction); void isEnabled(int userId, in IResultReceiver receiver); + void resetTemporaryService(int userId); + void setTemporaryService(int userId, in String serviceName, int duration); + void setDefaultServiceEnabled(int userId, boolean enabled); } diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java index 9a53b99aaefe..dd2ba7db03ae 100644 --- a/core/java/android/app/people/PeopleSpaceTile.java +++ b/core/java/android/app/people/PeopleSpaceTile.java @@ -29,6 +29,7 @@ import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import java.util.ArrayList; import java.util.List; @@ -45,7 +46,7 @@ public class PeopleSpaceTile implements Parcelable { private String mId; private CharSequence mUserName; private Icon mUserIcon; - private int mUid; + private UserHandle mUserHandle; private Uri mContactUri; private String mPackageName; private String mBirthdayText; @@ -53,6 +54,7 @@ public class PeopleSpaceTile implements Parcelable { private boolean mIsImportantConversation; private String mNotificationKey; private CharSequence mNotificationContent; + private String mNotificationCategory; private Uri mNotificationDataUri; private Intent mIntent; private long mNotificationTimestamp; @@ -63,13 +65,14 @@ public class PeopleSpaceTile implements Parcelable { mUserName = b.mUserName; mUserIcon = b.mUserIcon; mContactUri = b.mContactUri; - mUid = b.mUid; + mUserHandle = b.mUserHandle; mPackageName = b.mPackageName; mBirthdayText = b.mBirthdayText; mLastInteractionTimestamp = b.mLastInteractionTimestamp; mIsImportantConversation = b.mIsImportantConversation; mNotificationKey = b.mNotificationKey; mNotificationContent = b.mNotificationContent; + mNotificationCategory = b.mNotificationCategory; mNotificationDataUri = b.mNotificationDataUri; mIntent = b.mIntent; mNotificationTimestamp = b.mNotificationTimestamp; @@ -93,8 +96,8 @@ public class PeopleSpaceTile implements Parcelable { return mContactUri; } - public int getUid() { - return mUid; + public UserHandle getUserHandle() { + return mUserHandle; } public String getPackageName() { @@ -129,6 +132,10 @@ public class PeopleSpaceTile implements Parcelable { return mNotificationContent; } + public String getNotificationCategory() { + return mNotificationCategory; + } + public Uri getNotificationDataUri() { return mNotificationDataUri; } @@ -159,13 +166,14 @@ public class PeopleSpaceTile implements Parcelable { Builder builder = new Builder(mId, mUserName.toString(), mUserIcon, mIntent); builder.setContactUri(mContactUri); - builder.setUid(mUid); + builder.setUserHandle(mUserHandle); builder.setPackageName(mPackageName); builder.setBirthdayText(mBirthdayText); builder.setLastInteractionTimestamp(mLastInteractionTimestamp); builder.setIsImportantConversation(mIsImportantConversation); builder.setNotificationKey(mNotificationKey); builder.setNotificationContent(mNotificationContent); + builder.setNotificationCategory(mNotificationCategory); builder.setNotificationDataUri(mNotificationDataUri); builder.setIntent(mIntent); builder.setNotificationTimestamp(mNotificationTimestamp); @@ -179,13 +187,14 @@ public class PeopleSpaceTile implements Parcelable { private CharSequence mUserName; private Icon mUserIcon; private Uri mContactUri; - private int mUid; + private UserHandle mUserHandle; private String mPackageName; private String mBirthdayText; private long mLastInteractionTimestamp; private boolean mIsImportantConversation; private String mNotificationKey; private CharSequence mNotificationContent; + private String mNotificationCategory; private Uri mNotificationDataUri; private Intent mIntent; private long mNotificationTimestamp; @@ -204,7 +213,7 @@ public class PeopleSpaceTile implements Parcelable { mId = info.getId(); mUserName = info.getLabel(); mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0)); - mUid = info.getUserId(); + mUserHandle = info.getUserHandle(); mPackageName = info.getPackage(); mContactUri = getContactUri(info); } @@ -214,7 +223,7 @@ public class PeopleSpaceTile implements Parcelable { mId = info.getId(); mUserName = info.getLabel(); mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0)); - mUid = info.getUserId(); + mUserHandle = info.getUserHandle(); mPackageName = info.getPackage(); mContactUri = getContactUri(info); mStatuses = channel.getStatuses(); @@ -257,9 +266,9 @@ public class PeopleSpaceTile implements Parcelable { return this; } - /** Sets the associated uid. */ - public Builder setUid(int uid) { - mUid = uid; + /** Sets the associated {@code userHandle}. */ + public Builder setUserHandle(UserHandle userHandle) { + mUserHandle = userHandle; return this; } @@ -299,6 +308,12 @@ public class PeopleSpaceTile implements Parcelable { return this; } + /** Sets the associated notification's category. */ + public Builder setNotificationCategory(String notificationCategory) { + mNotificationCategory = notificationCategory; + return this; + } + /** Sets the associated notification's data URI. */ public Builder setNotificationDataUri(Uri notificationDataUri) { mNotificationDataUri = notificationDataUri; @@ -335,13 +350,14 @@ public class PeopleSpaceTile implements Parcelable { mUserName = in.readCharSequence(); mUserIcon = in.readParcelable(Icon.class.getClassLoader()); mContactUri = in.readParcelable(Uri.class.getClassLoader()); - mUid = in.readInt(); + mUserHandle = in.readParcelable(UserHandle.class.getClassLoader()); mPackageName = in.readString(); mBirthdayText = in.readString(); mLastInteractionTimestamp = in.readLong(); mIsImportantConversation = in.readBoolean(); mNotificationKey = in.readString(); mNotificationContent = in.readCharSequence(); + mNotificationCategory = in.readString(); mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader()); mIntent = in.readParcelable(Intent.class.getClassLoader()); mNotificationTimestamp = in.readLong(); @@ -360,13 +376,14 @@ public class PeopleSpaceTile implements Parcelable { dest.writeCharSequence(mUserName); dest.writeParcelable(mUserIcon, flags); dest.writeParcelable(mContactUri, flags); - dest.writeInt(mUid); + dest.writeParcelable(mUserHandle, flags); dest.writeString(mPackageName); dest.writeString(mBirthdayText); dest.writeLong(mLastInteractionTimestamp); dest.writeBoolean(mIsImportantConversation); dest.writeString(mNotificationKey); dest.writeCharSequence(mNotificationContent); + dest.writeString(mNotificationCategory); dest.writeParcelable(mNotificationDataUri, flags); dest.writeParcelable(mIntent, flags); dest.writeLong(mNotificationTimestamp); diff --git a/core/java/android/app/time/ExternalTimeSuggestion.java b/core/java/android/app/time/ExternalTimeSuggestion.java index b566eab9867d..61defb588170 100644 --- a/core/java/android/app/time/ExternalTimeSuggestion.java +++ b/core/java/android/app/time/ExternalTimeSuggestion.java @@ -16,6 +16,8 @@ package android.app.time; +import android.annotation.CurrentTimeMillisLong; +import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; @@ -31,9 +33,9 @@ import java.util.Objects; /** * A time signal from an External source. * - * External time suggestions are for use in situations where the Android device is part of a wider - * network of devices that are required to use a single time source, and where authority for the - * time is external to the Android device. For example, for the Android Auto use case where the + * <p>External time suggestions are for use in situations where the Android device is part of a + * wider network of devices that are required to use a single time source, and where authority for + * the time is external to the Android device. For example, for the Android Auto use case where the * Android device is part of a wider in-car network of devices that should display the same time. * * <p>Android allows for a single external source for time. If there are several external sources @@ -49,19 +51,19 @@ import java.util.Objects; * capture the elapsed realtime reference clock, e.g. via {@link SystemClock#elapsedRealtime()}, * when the UTC time is first obtained (usually under a wakelock). This enables Android to adjust * for latency introduced between suggestion creation and eventual use. Adjustments for other - * sources of latency, i.e. those before the external time suggestion is created, must be handled - * by the creator. + * sources of latency, i.e. those before the external time suggestion is created, must be handled by + * the creator. * - * <p>{@code utcTime} is the suggested time. The {@code utcTime.value} is the number of milliseconds - * elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the value of the - * elapsed realtime clock when the {@code utcTime.value} was established. - * Note that the elapsed realtime clock is considered accurate but it is volatile, so time - * suggestions cannot be persisted across device resets. + * <p>{@code elapsedRealtimeMillis} and {@code suggestionMillis} represent the suggested time. + * {@code suggestionMillis} is the number of milliseconds elapsed since 1/1/1970 00:00:00 UTC. + * {@code elapsedRealtimeMillis} is the value of the elapsed realtime clock when {@code + * suggestionMillis} was established. Note that the elapsed realtime clock is considered accurate + * but it is volatile, so time suggestions cannot be persisted across device resets. * * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to * record why the suggestion exists and how it was entered. This information exists only to aid in - * debugging and therefore is used by {@link #toString()}, but it is not for use in detection - * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}. + * debugging and therefore is used by {@link #toString()}, but it is not for use in detection logic + * and is not considered in {@link #hashCode()} or {@link #equals(Object)}. * * @hide */ @@ -78,17 +80,28 @@ public final class ExternalTimeSuggestion implements Parcelable { } }; - @NonNull private final TimestampedValue<Long> mUtcTime; - @Nullable private ArrayList<String> mDebugInfo; + @NonNull + private final TimestampedValue<Long> mUtcTime; + @Nullable + private ArrayList<String> mDebugInfo; - public ExternalTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) { - mUtcTime = Objects.requireNonNull(utcTime); - Objects.requireNonNull(utcTime.getValue()); + /** + * Creates a time suggestion cross-referenced to the elapsed realtime clock. See {@link + * ExternalTimeSuggestion} for more details. + * + * @param elapsedRealtimeMillis the elapsed realtime clock reference for the suggestion + * @param suggestionMillis the suggested UTC time in milliseconds since the start of the + * Unix epoch + */ + public ExternalTimeSuggestion(@ElapsedRealtimeLong long elapsedRealtimeMillis, + @CurrentTimeMillisLong long suggestionMillis) { + mUtcTime = new TimestampedValue(elapsedRealtimeMillis, suggestionMillis); } private static ExternalTimeSuggestion createFromParcel(Parcel in) { TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); - ExternalTimeSuggestion suggestion = new ExternalTimeSuggestion(utcTime); + ExternalTimeSuggestion suggestion = + new ExternalTimeSuggestion(utcTime.getReferenceTimeMillis(), utcTime.getValue()); @SuppressWarnings("unchecked") ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); suggestion.mDebugInfo = debugInfo; @@ -106,23 +119,31 @@ public final class ExternalTimeSuggestion implements Parcelable { dest.writeList(mDebugInfo); } + /** + * {@hide} + */ @NonNull public TimestampedValue<Long> getUtcTime() { return mUtcTime; } + /** + * Returns information that can be useful for debugging / logging. See {@link #addDebugInfo}. + * {@hide} + */ @NonNull public List<String> getDebugInfo() { return mDebugInfo == null - ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo); + ? Collections.emptyList() + : Collections.unmodifiableList(mDebugInfo); } /** * Associates information with the instance that can be useful for debugging / logging. The - * information is present in {@link #toString()} but is not considered for - * {@link #equals(Object)} and {@link #hashCode()}. + * information is present in {@link #toString()} but is not considered for {@link + * #equals(Object)} and {@link #hashCode()}. */ - public void addDebugInfo(String... debugInfos) { + public void addDebugInfo(@NonNull String... debugInfos) { if (mDebugInfo == null) { mDebugInfo = new ArrayList<>(); } @@ -148,9 +169,7 @@ public final class ExternalTimeSuggestion implements Parcelable { @Override public String toString() { - return "ExternalTimeSuggestion{" - + "mUtcTime=" + mUtcTime - + ", mDebugInfo=" + mDebugInfo + return "ExternalTimeSuggestion{" + "mUtcTime=" + mUtcTime + ", mDebugInfo=" + mDebugInfo + '}'; } } diff --git a/core/java/android/app/time/LocationTimeZoneManager.java b/core/java/android/app/time/LocationTimeZoneManager.java index 71a800f2085e..066aadae1476 100644 --- a/core/java/android/app/time/LocationTimeZoneManager.java +++ b/core/java/android/app/time/LocationTimeZoneManager.java @@ -37,7 +37,7 @@ public final class LocationTimeZoneManager { /** * The name of the service for shell commands */ - public static final String SHELL_COMMAND_SERVICE_NAME = "location_time_zone_manager"; + public static final String SERVICE_NAME = "location_time_zone_manager"; /** * A shell command that starts the service (after stop). diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java index 262d244c5b1c..430960fb11a8 100644 --- a/core/java/android/app/time/TimeManager.java +++ b/core/java/android/app/time/TimeManager.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.app.timedetector.ITimeDetectorService; import android.app.timezonedetector.ITimeZoneDetectorService; import android.content.Context; import android.os.RemoteException; @@ -45,6 +46,7 @@ public final class TimeManager { private final Object mLock = new Object(); private final ITimeZoneDetectorService mITimeZoneDetectorService; + private final ITimeDetectorService mITimeDetectorService; @GuardedBy("mLock") private ITimeZoneDetectorListener mTimeZoneDetectorReceiver; @@ -62,6 +64,8 @@ public final class TimeManager { // internal refactoring. mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE)); + mITimeDetectorService = ITimeDetectorService.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.TIME_DETECTOR_SERVICE)); } /** @@ -214,4 +218,23 @@ public final class TimeManager { } } } + + /** + * Suggests the current time from an external time source. For example, a form factor-specific + * HAL. This time <em>may</em> be used to set the device system clock, depending on the device + * configuration and user settings. This method call is processed asynchronously. + * See {@link ExternalTimeSuggestion} for more details. + * {@hide} + */ + @RequiresPermission(android.Manifest.permission.SET_TIME) + public void suggestExternalTime(@NonNull ExternalTimeSuggestion timeSuggestion) { + if (DEBUG) { + Log.d(TAG, "suggestExternalTime called: " + timeSuggestion); + } + try { + mITimeDetectorService.suggestExternalTime(timeSuggestion); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java index 76f378590ae2..52016b65688b 100644 --- a/core/java/android/app/timedetector/TimeDetector.java +++ b/core/java/android/app/timedetector/TimeDetector.java @@ -19,7 +19,6 @@ package android.app.timedetector; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; -import android.app.time.ExternalTimeSuggestion; import android.content.Context; import android.os.SystemClock; import android.os.TimestampedValue; @@ -80,12 +79,4 @@ public interface TimeDetector { */ @RequiresPermission(android.Manifest.permission.SET_TIME) void suggestGnssTime(GnssTimeSuggestion timeSuggestion); - - /** - * Suggests the time according to an external time source (form factor specific HAL, etc). - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.SET_TIME) - void suggestExternalTime(ExternalTimeSuggestion timeSuggestion); } diff --git a/core/java/android/app/timedetector/TimeDetectorImpl.java b/core/java/android/app/timedetector/TimeDetectorImpl.java index ef818ef647d6..b0aa3c8d4575 100644 --- a/core/java/android/app/timedetector/TimeDetectorImpl.java +++ b/core/java/android/app/timedetector/TimeDetectorImpl.java @@ -17,7 +17,6 @@ package android.app.timedetector; import android.annotation.NonNull; -import android.app.time.ExternalTimeSuggestion; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; @@ -87,16 +86,4 @@ public final class TimeDetectorImpl implements TimeDetector { throw e.rethrowFromSystemServer(); } } - - @Override - public void suggestExternalTime(ExternalTimeSuggestion timeSuggestion) { - if (DEBUG) { - Log.d(TAG, "suggestExternalTime called: " + timeSuggestion); - } - try { - mITimeDetectorService.suggestExternalTime(timeSuggestion); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } } diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index 565e4cd7fea0..a72877e27943 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -37,7 +37,7 @@ import android.os.ServiceManager; import android.util.DisplayMetrics; import android.util.SparseArray; import android.widget.RemoteViews; -import android.widget.RemoteViews.OnClickHandler; +import android.widget.RemoteViews.InteractionHandler; import com.android.internal.R; import com.android.internal.appwidget.IAppWidgetHost; @@ -71,7 +71,7 @@ public class AppWidgetHost { private final int mHostId; private final Callbacks mCallbacks; private final SparseArray<AppWidgetHostView> mViews = new SparseArray<>(); - private OnClickHandler mOnClickHandler; + private InteractionHandler mInteractionHandler; static class Callbacks extends IAppWidgetHost.Stub { private final WeakReference<Handler> mWeakHandler; @@ -175,10 +175,10 @@ public class AppWidgetHost { * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public AppWidgetHost(Context context, int hostId, OnClickHandler handler, Looper looper) { + public AppWidgetHost(Context context, int hostId, InteractionHandler handler, Looper looper) { mContextOpPackageName = context.getOpPackageName(); mHostId = hostId; - mOnClickHandler = handler; + mInteractionHandler = handler; mHandler = new UpdateHandler(looper); mCallbacks = new Callbacks(mHandler); mDisplayMetrics = context.getResources().getDisplayMetrics(); @@ -401,7 +401,7 @@ public class AppWidgetHost { return null; } AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget); - view.setOnClickHandler(mOnClickHandler); + view.setInteractionHandler(mInteractionHandler); view.setAppWidget(appWidgetId, appWidget); synchronized (mViews) { mViews.put(appWidgetId, view); @@ -423,7 +423,7 @@ public class AppWidgetHost { */ protected AppWidgetHostView onCreateView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget) { - return new AppWidgetHostView(context, mOnClickHandler); + return new AppWidgetHostView(context, mInteractionHandler); } /** diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 42d90a794e74..fc54c716d4ec 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -40,6 +40,7 @@ import android.util.AttributeSet; import android.util.Log; import android.util.Pair; import android.util.SparseArray; +import android.util.SparseIntArray; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -49,7 +50,7 @@ import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.FrameLayout; import android.widget.RemoteViews; -import android.widget.RemoteViews.OnClickHandler; +import android.widget.RemoteViews.InteractionHandler; import android.widget.RemoteViewsAdapter.RemoteAdapterConnectionCallback; import android.widget.TextView; @@ -90,9 +91,12 @@ public class AppWidgetHostView extends FrameLayout { View mView; int mViewMode = VIEW_MODE_NOINIT; int mLayoutId = -1; - private OnClickHandler mOnClickHandler; + private InteractionHandler mInteractionHandler; private boolean mOnLightBackground; - PointF mCurrentSize = null; + private PointF mCurrentSize = null; + private RemoteViews.ColorResources mColorResources = null; + // Stores the last remote views last inflated. + private RemoteViews mLastInflatedRemoteViews = null; private Executor mAsyncExecutor; private CancellationSignal mLastExecutionSignal; @@ -107,9 +111,9 @@ public class AppWidgetHostView extends FrameLayout { /** * @hide */ - public AppWidgetHostView(Context context, OnClickHandler handler) { + public AppWidgetHostView(Context context, InteractionHandler handler) { this(context, android.R.anim.fade_in, android.R.anim.fade_out); - mOnClickHandler = getHandler(handler); + mInteractionHandler = getHandler(handler); } /** @@ -135,8 +139,8 @@ public class AppWidgetHostView extends FrameLayout { * @param handler * @hide */ - public void setOnClickHandler(OnClickHandler handler) { - mOnClickHandler = getHandler(handler); + public void setInteractionHandler(InteractionHandler handler) { + mInteractionHandler = getHandler(handler); } /** @@ -358,7 +362,7 @@ public class AppWidgetHostView extends FrameLayout { PointF newSize = new PointF(size.x - xPaddingDips, size.y - yPaddingDips); if (!newSize.equals(mCurrentSize)) { mCurrentSize = newSize; - mLayoutId = -1; // Prevents recycling the view. + reapplyLastRemoteViews(); } } @@ -368,7 +372,7 @@ public class AppWidgetHostView extends FrameLayout { public void clearCurrentSize() { if (mCurrentSize != null) { mCurrentSize = null; - mLayoutId = -1; + reapplyLastRemoteViews(); } } @@ -477,10 +481,18 @@ public class AppWidgetHostView extends FrameLayout { * AppWidget provider. Will animate into these new views as needed */ public void updateAppWidget(RemoteViews remoteViews) { + this.mLastInflatedRemoteViews = remoteViews; applyRemoteViews(remoteViews, true); } /** + * Reapply the last inflated remote views, or the default view is none was inflated. + */ + private void reapplyLastRemoteViews() { + applyRemoteViews(mLastInflatedRemoteViews, true); + } + + /** * @hide */ protected void applyRemoteViews(RemoteViews remoteViews, boolean useAsyncIfPossible) { @@ -518,7 +530,8 @@ public class AppWidgetHostView extends FrameLayout { // layout matches, try recycling it if (content == null && layoutId == mLayoutId) { try { - remoteViews.reapply(mContext, mView, mOnClickHandler); + remoteViews.reapply(mContext, mView, mInteractionHandler, mCurrentSize, + mColorResources); content = mView; recycled = true; if (LOGD) Log.d(TAG, "was able to recycle existing layout"); @@ -530,7 +543,8 @@ public class AppWidgetHostView extends FrameLayout { // Try normal RemoteView inflation if (content == null) { try { - content = remoteViews.apply(mContext, this, mOnClickHandler, mCurrentSize); + content = remoteViews.apply(mContext, this, mInteractionHandler, + mCurrentSize, mColorResources); if (LOGD) Log.d(TAG, "had to inflate new layout"); } catch (RuntimeException e) { exception = e; @@ -582,8 +596,9 @@ public class AppWidgetHostView extends FrameLayout { mView, mAsyncExecutor, new ViewApplyListener(remoteViews, layoutId, true), - mOnClickHandler, - mCurrentSize); + mInteractionHandler, + mCurrentSize, + mColorResources); } catch (Exception e) { // Reapply failed. Try apply } @@ -593,8 +608,9 @@ public class AppWidgetHostView extends FrameLayout { this, mAsyncExecutor, new ViewApplyListener(remoteViews, layoutId, false), - mOnClickHandler, - mCurrentSize); + mInteractionHandler, + mCurrentSize, + mColorResources); } } @@ -625,7 +641,7 @@ public class AppWidgetHostView extends FrameLayout { AppWidgetHostView.this, mAsyncExecutor, new ViewApplyListener(mViews, mLayoutId, false), - mOnClickHandler, + mInteractionHandler, mCurrentSize); } else { applyContent(null, false, e); @@ -662,9 +678,13 @@ public class AppWidgetHostView extends FrameLayout { protected Context getRemoteContext() { try { // Return if cloned successfully, otherwise default - return mContext.createApplicationContext( + Context newContext = mContext.createApplicationContext( mInfo.providerInfo.applicationInfo, Context.CONTEXT_RESTRICTED); + if (mColorResources != null) { + mColorResources.apply(newContext); + } + return newContext; } catch (NameNotFoundException e) { Log.e(TAG, "Package name " + mInfo.providerInfo.packageName + " not found"); return mContext; @@ -808,15 +828,48 @@ public class AppWidgetHostView extends FrameLayout { return null; } - private OnClickHandler getHandler(OnClickHandler handler) { + private InteractionHandler getHandler(InteractionHandler handler) { return (view, pendingIntent, response) -> { AppWidgetManager.getInstance(mContext).noteAppWidgetTapped(mAppWidgetId); if (handler != null) { - return handler.onClickHandler(view, pendingIntent, response); + return handler.onInteraction(view, pendingIntent, response); } else { return RemoteViews.startPendingIntent(view, pendingIntent, response.getLaunchOptions(view)); } }; } + + /** + * Set the dynamically overloaded color resources. + * + * {@code colorMapping} maps a predefined set of color resources to their ARGB + * representation. Any entry not in the predefined set of colors will be ignored. + * + * Calling this method will trigger a full re-inflation of the App Widget. + * + * The color resources that can be overloaded are the ones whose name is prefixed with + * {@code system_primary_}, {@code system_secondary_} or {@code system_neutral_}, for example + * {@link android.R.color#system_primary_500}. + */ + public void setColorResources(@NonNull SparseIntArray colorMapping) { + mColorResources = RemoteViews.ColorResources.create(mContext, colorMapping); + mLayoutId = -1; + reapplyLastRemoteViews(); + } + + /** + * Reset the dynamically overloaded resources, reverting to the default values for + * all the colors. + * + * If colors were defined before, calling this method will trigger a full re-inflation of the + * App Widget. + */ + public void resetColorResources() { + if (mColorResources != null) { + mColorResources = null; + mLayoutId = -1; + reapplyLastRemoteViews(); + } + } } diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index aac8710e8691..38919f61d9df 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -23,6 +23,8 @@ import android.annotation.RequiresFeature; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemService; +import android.annotation.TestApi; +import android.annotation.UserIdInt; import android.app.IServiceConnection; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; @@ -1095,7 +1097,9 @@ public class AppWidgetManager { * * @hide */ - public void setBindAppWidgetPermission(String packageName, int userId, boolean permission) { + @TestApi + public void setBindAppWidgetPermission( + @NonNull String packageName, @UserIdInt int userId, boolean permission) { if (mService == null) { return; } diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java index d893a5e49aa9..6ac1c1ae61ec 100644 --- a/core/java/android/appwidget/AppWidgetProviderInfo.java +++ b/core/java/android/appwidget/AppWidgetProviderInfo.java @@ -332,12 +332,13 @@ public class AppWidgetProviderInfo implements Parcelable { /** * Resource id for the description of the AppWidget. + * * <p>This field corresponds to the <code>android:description</code> attribute in the AppWidget * meta-data file. */ @SuppressLint("MutableBareField") @IdRes - public int descriptionResource; + public int descriptionRes; /** * Flags indicating various features supported by the widget. These are hints to the widget @@ -385,7 +386,7 @@ public class AppWidgetProviderInfo implements Parcelable { this.widgetCategory = in.readInt(); this.providerInfo = in.readTypedObject(ActivityInfo.CREATOR); this.widgetFeatures = in.readInt(); - this.descriptionResource = in.readInt(); + this.descriptionRes = in.readInt(); } /** @@ -442,14 +443,22 @@ public class AppWidgetProviderInfo implements Parcelable { return loadDrawable(context, density, previewImage, false); } - /** Loads localized description for the app widget. */ + /** + * Loads localized description for the app widget. + * + * <p>Description is intended to be displayed in the UI of the widget picker. + * + * @param context Context for accessing resources. + * + * @return CharSequence for app widget description for the current locale. + */ @Nullable - public final String loadDescription(@NonNull Context context) { - if (ResourceId.isValid(descriptionResource)) { + public final CharSequence loadDescription(@NonNull Context context) { + if (ResourceId.isValid(descriptionRes)) { return context.getPackageManager() .getText( providerInfo.packageName, - descriptionResource, + descriptionRes, providerInfo.applicationInfo) .toString() .trim(); @@ -499,7 +508,7 @@ public class AppWidgetProviderInfo implements Parcelable { out.writeInt(this.widgetCategory); out.writeTypedObject(this.providerInfo, flags); out.writeInt(this.widgetFeatures); - out.writeInt(this.descriptionResource); + out.writeInt(this.descriptionRes); } @Override @@ -528,7 +537,7 @@ public class AppWidgetProviderInfo implements Parcelable { that.widgetCategory = this.widgetCategory; that.providerInfo = this.providerInfo; that.widgetFeatures = this.widgetFeatures; - that.descriptionResource = this.descriptionResource; + that.descriptionRes = this.descriptionRes; return that; } diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index cd91aa9b16b7..53aaae0470e2 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -943,12 +943,13 @@ public final class BluetoothA2dp implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setBufferMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) { - if (VDBG) log("setBufferMillis(" + codec + ", " + value + ")"); + public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec, + int value) { + if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")"); try { final IBluetoothA2dp service = getService(); if (service != null && isEnabled()) { - return service.setBufferMillis(codec, value); + return service.setBufferLengthMillis(codec, value); } if (service == null) Log.w(TAG, "Proxy not attached to service"); return false; diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index ec46da0dcf0e..8d4157259ff7 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -3654,12 +3654,12 @@ public final class BluetoothAdapter { } @Override - public void onDeviceDisconnected(BluetoothDevice device) { + public void onDeviceDisconnected(BluetoothDevice device, int hciReason) { for (Map.Entry<BluetoothConnectionCallback, Executor> callbackExecutorEntry: mBluetoothConnectionCallbackExecutorMap.entrySet()) { BluetoothConnectionCallback callback = callbackExecutorEntry.getKey(); Executor executor = callbackExecutorEntry.getValue(); - executor.execute(() -> callback.onDeviceDisconnected(device)); + executor.execute(() -> callback.onDeviceDisconnected(device, hciReason)); } } }; @@ -3764,8 +3764,155 @@ public final class BluetoothAdapter { /** * Callback triggered when a bluetooth device (classic or BLE) is disconnected * @param device is the disconnected bluetooth device + * @param reason is the disconnect reason */ - public void onDeviceDisconnected(BluetoothDevice device) {} + public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) {} + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "REASON_" }, value = { + REASON_UNKNOWN, + REASON_LOCAL_REQUEST, + REASON_REMOTE_REQUEST, + REASON_LOCAL_ERROR, + REASON_REMOTE_ERROR, + REASON_TIMEOUT, + REASON_SECURITY, + REASON_SYSTEM_POLICY, + REASON_RESOURCE_LIMIT_REACHED, + REASON_CONNECTION_EXISTS, + REASON_BAD_PARAMETERS}) + public @interface DisconnectReason {} + + /** + * Indicates that the ACL disconnected due to an unknown reason. + */ + public static final int REASON_UNKNOWN = 0; + + /** + * Indicates that the ACL disconnected due to an explicit request from the local device. + * <p> + * Example cause: This is a normal disconnect reason, e.g., user/app initiates + * disconnection. + */ + public static final int REASON_LOCAL_REQUEST = 1; + + /** + * Indicates that the ACL disconnected due to an explicit request from the remote device. + * <p> + * Example cause: This is a normal disconnect reason, e.g., user/app initiates + * disconnection. + * <p> + * Example solution: The app can also prompt the user to check their remote device. + */ + public static final int REASON_REMOTE_REQUEST = 2; + + /** + * Generic disconnect reason indicating the ACL disconnected due to an error on the local + * device. + * <p> + * Example solution: Prompt the user to check their local device (e.g., phone, car + * headunit). + */ + public static final int REASON_LOCAL_ERROR = 3; + + /** + * Generic disconnect reason indicating the ACL disconnected due to an error on the remote + * device. + * <p> + * Example solution: Prompt the user to check their remote device (e.g., headset, car + * headunit, watch). + */ + public static final int REASON_REMOTE_ERROR = 4; + + /** + * Indicates that the ACL disconnected due to a timeout. + * <p> + * Example cause: remote device might be out of range. + * <p> + * Example solution: Prompt user to verify their remote device is on or in + * connection/pairing mode. + */ + public static final int REASON_TIMEOUT = 5; + + /** + * Indicates that the ACL disconnected due to link key issues. + * <p> + * Example cause: Devices are either unpaired or remote device is refusing our pairing + * request. + * <p> + * Example solution: Prompt user to unpair and pair again. + */ + public static final int REASON_SECURITY = 6; + + /** + * Indicates that the ACL disconnected due to the local device's system policy. + * <p> + * Example cause: privacy policy, power management policy, permissions, etc. + * <p> + * Example solution: Prompt the user to check settings, or check with their system + * administrator (e.g. some corp-managed devices do not allow OPP connection). + */ + public static final int REASON_SYSTEM_POLICY = 7; + + /** + * Indicates that the ACL disconnected due to resource constraints, either on the local + * device or the remote device. + * <p> + * Example cause: controller is busy, memory limit reached, maximum number of connections + * reached. + * <p> + * Example solution: The app should wait and try again. If still failing, prompt the user + * to disconnect some devices, or toggle Bluetooth on the local and/or the remote device. + */ + public static final int REASON_RESOURCE_LIMIT_REACHED = 8; + + /** + * Indicates that the ACL disconnected because another ACL connection already exists. + */ + public static final int REASON_CONNECTION_EXISTS = 9; + + /** + * Indicates that the ACL disconnected due to incorrect parameters passed in from the app. + * <p> + * Example solution: Change parameters and try again. If error persists, the app can report + * telemetry and/or log the error in a bugreport. + */ + public static final int REASON_BAD_PARAMETERS = 10; + + /** + * Returns human-readable strings corresponding to {@link DisconnectReason}. + */ + public static String disconnectReasonText(@DisconnectReason int reason) { + switch (reason) { + case REASON_UNKNOWN: + return "Reason unknown"; + case REASON_LOCAL_REQUEST: + return "Local request"; + case REASON_REMOTE_REQUEST: + return "Remote request"; + case REASON_LOCAL_ERROR: + return "Local error"; + case REASON_REMOTE_ERROR: + return "Remote error"; + case REASON_TIMEOUT: + return "Timeout"; + case REASON_SECURITY: + return "Security"; + case REASON_SYSTEM_POLICY: + return "System policy"; + case REASON_RESOURCE_LIMIT_REACHED: + return "Resource constrained"; + case REASON_CONNECTION_EXISTS: + return "Connection already exists"; + case REASON_BAD_PARAMETERS: + return "Bad parameters"; + default: + return "Unrecognized disconnect reason: " + reason; + } + } } /** diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java index ff6cffb272a5..0312a2190a4b 100644 --- a/core/java/android/bluetooth/BluetoothMapClient.java +++ b/core/java/android/bluetooth/BluetoothMapClient.java @@ -18,7 +18,9 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -30,6 +32,7 @@ import android.os.RemoteException; import android.util.Log; import java.util.ArrayList; +import java.util.Collection; import java.util.List; /** @@ -37,44 +40,60 @@ import java.util.List; * * @hide */ +@SystemApi public final class BluetoothMapClient implements BluetoothProfile { private static final String TAG = "BluetoothMapClient"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); + /** @hide */ public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED"; + /** @hide */ public static final String ACTION_MESSAGE_RECEIVED = "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED"; /* Actions to be used for pending intents */ + /** @hide */ public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY"; + /** @hide */ public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY"; /** * Action to notify read status changed + * + * @hide */ public static final String ACTION_MESSAGE_READ_STATUS_CHANGED = "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED"; /** * Action to notify deleted status changed + * + * @hide */ public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED = "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED"; - /* Extras used in ACTION_MESSAGE_RECEIVED intent. - * NOTE: HANDLE is only valid for a single session with the device. */ + /** + * Extras used in ACTION_MESSAGE_RECEIVED intent. + * NOTE: HANDLE is only valid for a single session with the device. + */ + /** @hide */ public static final String EXTRA_MESSAGE_HANDLE = "android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE"; + /** @hide */ public static final String EXTRA_MESSAGE_TIMESTAMP = "android.bluetooth.mapmce.profile.extra.MESSAGE_TIMESTAMP"; + /** @hide */ public static final String EXTRA_MESSAGE_READ_STATUS = "android.bluetooth.mapmce.profile.extra.MESSAGE_READ_STATUS"; + /** @hide */ public static final String EXTRA_SENDER_CONTACT_URI = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI"; + /** @hide */ public static final String EXTRA_SENDER_CONTACT_NAME = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME"; @@ -84,6 +103,8 @@ public final class BluetoothMapClient implements BluetoothProfile { * Possible values are: * true: deleted * false: undeleted + * + * @hide */ public static final String EXTRA_MESSAGE_DELETED_STATUS = "android.bluetooth.mapmce.profile.extra.MESSAGE_DELETED_STATUS"; @@ -93,24 +114,42 @@ public final class BluetoothMapClient implements BluetoothProfile { * Possible values are: * 0: failure * 1: success + * + * @hide */ public static final String EXTRA_RESULT_CODE = "android.bluetooth.device.extra.RESULT_CODE"; - /** There was an error trying to obtain the state */ + /** + * There was an error trying to obtain the state + * @hide + */ public static final int STATE_ERROR = -1; + /** @hide */ public static final int RESULT_FAILURE = 0; + /** @hide */ public static final int RESULT_SUCCESS = 1; - /** Connection canceled before completion. */ + /** + * Connection canceled before completion. + * @hide + */ public static final int RESULT_CANCELED = 2; - + /** @hide */ private static final int UPLOADING_FEATURE_BITMASK = 0x08; - /** Parameters in setMessageStatus */ + /* + * UNREAD, READ, UNDELETED, DELETED are passed as parameters + * to setMessageStatus to indicate the messages new state. + */ + + /** @hide */ public static final int UNREAD = 0; + /** @hide */ public static final int READ = 1; + /** @hide */ public static final int UNDELETED = 2; + /** @hide */ public static final int DELETED = 3; private BluetoothAdapter mAdapter; @@ -132,19 +171,12 @@ public final class BluetoothMapClient implements BluetoothProfile { mProfileConnector.connect(context, listener); } - protected void finalize() throws Throwable { - try { - close(); - } finally { - super.finalize(); - } - } - /** * Close the connection to the backing service. * Other public functions of BluetoothMap will return default error * results once close() has been called. Multiple invocations of close() * are ok. + * @hide */ public void close() { mProfileConnector.disconnect(); @@ -158,6 +190,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * Returns true if the specified Bluetooth device is connected. * Returns false if not connected, or if this proxy object is not * currently connected to the Map service. + * @hide */ public boolean isConnected(BluetoothDevice device) { if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); @@ -225,6 +258,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * Get the list of connected devices. Currently at most one. * * @return list of connected devices + * @hide */ @Override public List<BluetoothDevice> getConnectedDevices() { @@ -246,6 +280,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * Get the list of devices matching specified states. Currently at most one. * * @return list of matching devices + * @hide */ @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { @@ -267,6 +302,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * Get connection state of device * * @return device connection state + * @hide */ @Override public int getConnectionState(BluetoothDevice device) { @@ -383,11 +419,44 @@ public final class BluetoothMapClient implements BluetoothProfile { * Send an SMS message to either the contacts primary number or the telephone number specified. * * @param device Bluetooth device + * @param contacts Uri Collection of the contacts + * @param message Message to be sent + * @param sentIntent intent issued when message is sent + * @param deliveredIntent intent issued when message is delivered + * @return true if the message is enqueued, false on error + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.SEND_SMS) + public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection<Uri> contacts, + @NonNull String message, @Nullable PendingIntent sentIntent, + @Nullable PendingIntent deliveredIntent) { + if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); + final IBluetoothMapClient service = getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.sendMessage(device, contacts.toArray(new Uri[contacts.size()]), + message, sentIntent, deliveredIntent); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + return false; + } + + /** + * Send a message. + * + * Send an SMS message to either the contacts primary number or the telephone number specified. + * + * @param device Bluetooth device * @param contacts Uri[] of the contacts * @param message Message to be sent * @param sentIntent intent issued when message is sent * @param deliveredIntent intent issued when message is delivered * @return true if the message is enqueued, false on error + * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, @@ -410,6 +479,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * * @param device Bluetooth device * @return true if the message is enqueued, false on error + * @hide */ public boolean getUnreadMessages(BluetoothDevice device) { if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")"); @@ -431,6 +501,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @param device The Bluetooth device to get this value for. * @return Returns true if the Uploading bit value in SDP record's * MapSupportedFeatures field is set. False is returned otherwise. + * @hide */ public boolean isUploadingSupported(BluetoothDevice device) { final IBluetoothMapClient service = getService(); @@ -457,7 +528,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * "read", <code>UNDELETED</code> for "undeleted", <code>DELETED</code> for * "deleted", otherwise return error * @return <code>true</code> if request has been sent, <code>false</code> on error - * + * @hide */ @RequiresPermission(Manifest.permission.READ_SMS) public boolean setMessageStatus(BluetoothDevice device, String handle, int status) { diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index c31b04e81456..201d6c495d98 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -186,6 +186,7 @@ public interface BluetoothProfile { * * @hide */ + @SystemApi int MAP_CLIENT = 18; /** diff --git a/core/java/android/bluetooth/BufferConstraints.java b/core/java/android/bluetooth/BufferConstraints.java index 7e5ec1e78435..97d97232b7a6 100644 --- a/core/java/android/bluetooth/BufferConstraints.java +++ b/core/java/android/bluetooth/BufferConstraints.java @@ -90,7 +90,7 @@ public final class BufferConstraints implements Parcelable { * @hide */ @SystemApi - public @Nullable BufferConstraint getCodec(@BluetoothCodecConfig.SourceCodecType int codec) { + public @Nullable BufferConstraint forCodec(@BluetoothCodecConfig.SourceCodecType int codec) { return mBufferConstraints.get(codec); } } diff --git a/core/java/android/companion/DeviceNotAssociatedException.java b/core/java/android/companion/DeviceNotAssociatedException.java index bebb6c9ff7eb..f8a7a7cdeffe 100644 --- a/core/java/android/companion/DeviceNotAssociatedException.java +++ b/core/java/android/companion/DeviceNotAssociatedException.java @@ -23,7 +23,7 @@ import android.annotation.Nullable; * An exception for a case when a given device was not * {@link CompanionDeviceManager#associate associated} to the calling app. */ -public class DeviceNotAssociatedException extends Exception { +public class DeviceNotAssociatedException extends RuntimeException { /** @hide */ public DeviceNotAssociatedException(@Nullable String deviceName) { super("Device not associated with the current app: " + deviceName); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 46d8900e59a1..230c985d1dc8 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -132,8 +132,11 @@ public abstract class ContentResolver implements ContentInterface { public static final String SYNC_EXTRAS_ACCOUNT = "account"; /** - * If this extra is set to true, the sync request will be scheduled - * at the front of the sync request queue and without any delay + * If this extra is set to true, the sync request will be scheduled at the front of the + * sync request queue, but it is still subject to JobScheduler quota and throttling due to + * App Standby buckets. + * + * <p>This is different from {@link #SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB}. */ public static final String SYNC_EXTRAS_EXPEDITED = "expedited"; @@ -145,6 +148,29 @@ public abstract class ContentResolver implements ContentInterface { public static final String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging"; /** + * Run this sync operation as an "expedited job" + * (see {@link android.app.job.JobInfo.Builder#setExpedited(boolean)}). + * Normally (if this flag isn't specified), sync operations are executed as regular + * {@link android.app.job.JobService} jobs. + * + * <p> Because Expedited Jobs have various restrictions compared to regular jobs, this flag + * cannot be combined with certain other flags, otherwise an + * <code>IllegalArgumentException</code> will be thrown. Notably, because Expedited Jobs do not + * support various constraints, the following restriction apply: + * <ul> + * <li>Can't be used with {@link #SYNC_EXTRAS_REQUIRE_CHARGING} + * <li>Can't be used with {@link #SYNC_EXTRAS_EXPEDITED} + * <li>Can't be used on periodic syncs. + * <li>When an expedited-job-sync fails and a retry is scheduled, the retried sync will be + * scheduled as a regular job unless {@link #SYNC_EXTRAS_IGNORE_BACKOFF} is set. + * </ul> + * + * <p>This is different from {@link #SYNC_EXTRAS_EXPEDITED}. + */ + @SuppressLint("IntentName") + public static final String SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB = "schedule_as_expedited_job"; + + /** * @deprecated instead use * {@link #SYNC_EXTRAS_MANUAL} */ @@ -3220,6 +3246,18 @@ public abstract class ContentResolver implements ContentInterface { } /** + * {@hide} + * Helper function to throw an <code>IllegalArgumentException</code> if any illegal + * extras were set for a sync scheduled as an expedited job. + * + * @param extras bundle to validate. + */ + public static boolean hasInvalidScheduleAsEjExtras(Bundle extras) { + return extras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING) + || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED); + } + + /** * Specifies that a sync should be requested with the specified the account, authority, * and extras at the given frequency. If there is already another periodic sync scheduled * with the account, authority and extras then a new periodic sync won't be added, instead @@ -3233,7 +3271,8 @@ public abstract class ContentResolver implements ContentInterface { * Periodic syncs are not allowed to have any of {@link #SYNC_EXTRAS_DO_NOT_RETRY}, * {@link #SYNC_EXTRAS_IGNORE_BACKOFF}, {@link #SYNC_EXTRAS_IGNORE_SETTINGS}, * {@link #SYNC_EXTRAS_INITIALIZE}, {@link #SYNC_EXTRAS_FORCE}, - * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL} set to true. + * {@link #SYNC_EXTRAS_EXPEDITED}, {@link #SYNC_EXTRAS_MANUAL}, + * {@link #SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB} set to true. * If any are supplied then an {@link IllegalArgumentException} will be thrown. * * <p>This method requires the caller to hold the permission @@ -3273,16 +3312,14 @@ public abstract class ContentResolver implements ContentInterface { * @param extras bundle to validate. */ public static boolean invalidPeriodicExtras(Bundle extras) { - if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false) + return extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false) || extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false) || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false) || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) || extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false) || extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false) - || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) { - return true; - } - return false; + || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false) + || extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false); } /** diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 10b00f245d79..f3a4e1f79955 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -370,6 +370,25 @@ public abstract class Context { /*********** Hidden flags below this line ***********/ /** + * Flag for {@link #bindService}: allow the process hosting the target service to be treated + * as if it's as important as a perceptible app to the user and avoid the oom killer killing + * this process in low memory situations until there aren't any other processes left but the + * ones which are user-perceptible. + * + * @hide + */ + public static final int BIND_ALMOST_PERCEPTIBLE = 0x000010000; + + /** + * Flag for {@link #bindService}: allow the process hosting the target service to gain + * {@link ActivityManager#PROCESS_CAPABILITY_NETWORK}, which allows it be able + * to access network regardless of any power saving restrictions. + * + * @hide + */ + public static final int BIND_ALLOW_NETWORK_ACCESS = 0x00020000; + + /** * Flag for {@link #bindService}: allow background foreground service starts from the bound * service's process. * This flag is only respected if the caller is holding @@ -3118,8 +3137,18 @@ public abstract class Context { * * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. - * @throws IllegalStateException If the application is in a state where the service - * can not be started (such as not in the foreground in a state when services are allowed). + * @throws IllegalStateException + * Before Android {@link android.os.Build.VERSION_CODES#S}, + * if the application is in a state where the service + * can not be started (such as not in the foreground in a state when services are allowed), + * {@link IllegalStateException} was thrown. + * @throws android.app.BackgroundServiceStartNotAllowedException + * On Android {@link android.os.Build.VERSION_CODES#S} and later, + * if the application is in a state where the service + * can not be started (such as not in the foreground in a state when services are allowed), + * {@link android.app.BackgroundServiceStartNotAllowedException} is thrown + * This excemption extends {@link IllegalStateException}, so apps can + * use {@code catch (IllegalStateException)} to catch both. * * @see #stopService * @see #bindService @@ -3150,7 +3179,8 @@ public abstract class Context { * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. * - * @throws IllegalStateException If the caller app's targeting API is + * @throws android.app.ForegroundServiceStartNotAllowedException + * If the caller app's targeting API is * {@link android.os.Build.VERSION_CODES#S} or later, and the foreground service is restricted * from start due to background restriction. * @@ -4753,7 +4783,7 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve an - * {@link android.app.scheduling.RebootReadinessManagerService} for communicating + * {@link android.scheduling.RebootReadinessManagerService} for communicating * with the reboot readiness detector. * * @see #getSystemService(String) @@ -5499,6 +5529,14 @@ public abstract class Context { public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification"; /** + * Use with {@link #getSystemService(String)} to access + * {@link android.view.displayhash.DisplayHashManager} to handle display hashes. + * + * @see #getSystemService(String) + */ + public static final String DISPLAY_HASH_SERVICE = "display_hash"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * @@ -6655,15 +6693,6 @@ public abstract class Context { } /** - * Indicates if this context is a visual context such as {@link android.app.Activity} or - * a context created from {@link #createWindowContext(int, Bundle)}. - * @hide - */ - public boolean isUiContext() { - throw new RuntimeException("Not implemented. Must override in a subclass."); - } - - /** * Returns {@code true} if the context is a UI context which can access UI components such as * {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or * {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI @@ -6675,12 +6704,16 @@ public abstract class Context { * {@link #createWindowContext(int, Bundle)} or * {@link android.inputmethodservice.InputMethodService InputMethodService} * </p> + * <p> + * Note that even if it is allowed programmatically, it is not suggested to override this + * method to bypass {@link android.os.strictmode.IncorrectContextUseViolation} verification. + * </p> * * @see #getDisplay() * @see #getSystemService(String) * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse() */ - public static boolean isUiContext(@NonNull Context context) { - return context.isUiContext(); + public boolean isUiContext() { + throw new RuntimeException("Not implemented. Must override in a subclass."); } } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 4abd8cd7d37b..d352b273f882 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4754,6 +4754,32 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION = "android.intent.action.PACKAGE_NEEDS_INTEGRITY_VERIFICATION"; + /** + * Broadcast Action: Indicates that the device's reboot readiness has changed. + * + * <p>This broadcast will be sent with an extra that indicates whether or not the device is + * ready to reboot. + * <p> + * The receiver <em>must</em> have the {@link android.Manifest.permission#REBOOT} permission. + * <p class="note"> + * This is a protected intent that can only be sent by the system. + * + * @see #EXTRA_IS_READY_TO_REBOOT + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_REBOOT_READY = "android.intent.action.REBOOT_READY"; + + /** + * A boolean extra used with {@link #ACTION_REBOOT_READY} which indicates if the + * device is ready to reboot. + * Will be {@code true} if ready to reboot, {@code false} otherwise. + * @hide + */ + @SystemApi + public static final String EXTRA_IS_READY_TO_REBOOT = "android.intent.extra.IS_READY_TO_REBOOT"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). @@ -6729,6 +6755,12 @@ public class Intent implements Parcelable, Cloneable { */ private static final int LOCAL_FLAG_FROM_PROTECTED_COMPONENT = 1 << 2; + /** + * Local flag indicating this instance had unfiltered extras copied into it. This could be + * from either {@link #putExtras(Intent)} when an unparceled Intent is provided or {@link + * #putExtras(Bundle)} when the provided Bundle has not been unparceled. + */ + private static final int LOCAL_FLAG_UNFILTERED_EXTRAS = 1 << 3; // --------------------------------------------------------------------- // --------------------------------------------------------------------- // toUri() and parseUri() options. @@ -9983,6 +10015,15 @@ public class Intent implements Parcelable, Cloneable { mExtras.putAll(src.mExtras); } } + // If the provided Intent was unparceled and this is not an Intent delivered to a protected + // component then mark the extras as unfiltered. An Intent delivered to a protected + // component had to come from a trusted component, and if unfiltered data was copied to the + // delivered Intent then it would have been reported when that Intent left the sending + // process. + if ((src.mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0 + && (src.mLocalFlags & LOCAL_FLAG_FROM_PROTECTED_COMPONENT) == 0) { + mLocalFlags |= LOCAL_FLAG_UNFILTERED_EXTRAS; + } return this; } @@ -9997,6 +10038,10 @@ public class Intent implements Parcelable, Cloneable { * @see #removeExtra */ public @NonNull Intent putExtras(@NonNull Bundle extras) { + // If the provided Bundle has not yet been unparceled then treat this as unfiltered extras. + if (extras.isParcelled()) { + mLocalFlags |= LOCAL_FLAG_UNFILTERED_EXTRAS; + } if (mExtras == null) { mExtras = new Bundle(); } @@ -11302,10 +11347,13 @@ public class Intent implements Parcelable, Cloneable { } // Detect cases where we're about to launch a potentially unsafe intent - if ((mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0 - && (mLocalFlags & LOCAL_FLAG_FROM_PROTECTED_COMPONENT) == 0 - && StrictMode.vmUnsafeIntentLaunchEnabled()) { - StrictMode.onUnsafeIntentLaunch(this); + if (StrictMode.vmUnsafeIntentLaunchEnabled()) { + if ((mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0 + && (mLocalFlags & LOCAL_FLAG_FROM_PROTECTED_COMPONENT) == 0) { + StrictMode.onUnsafeIntentLaunch(this); + } else if ((mLocalFlags & LOCAL_FLAG_UNFILTERED_EXTRAS) != 0) { + StrictMode.onUnsafeIntentLaunch(this); + } } } diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java index 858d1e498783..b1252fd0b21f 100644 --- a/core/java/android/content/IntentSender.java +++ b/core/java/android/content/IntentSender.java @@ -18,6 +18,7 @@ package android.content; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManager.PendingIntentInfo; import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.os.Handler; @@ -60,6 +61,9 @@ public class IntentSender implements Parcelable { private final IIntentSender mTarget; IBinder mWhitelistToken; + // cached pending intent information + private @Nullable PendingIntentInfo mCachedInfo; + /** * Exception thrown when trying to send through a PendingIntent that * has been canceled or is otherwise no longer able to execute the request. @@ -209,13 +213,7 @@ public class IntentSender implements Parcelable { */ @Deprecated public String getTargetPackage() { - try { - return ActivityManager.getService() - .getPackageForIntentSender(mTarget); - } catch (RemoteException e) { - // Should never happen. - return null; - } + return getCreatorPackage(); } /** @@ -228,13 +226,7 @@ public class IntentSender implements Parcelable { * none associated with it. */ public String getCreatorPackage() { - try { - return ActivityManager.getService() - .getPackageForIntentSender(mTarget); - } catch (RemoteException e) { - // Should never happen. - return null; - } + return getCachedInfo().getCreatorPackage(); } /** @@ -247,13 +239,7 @@ public class IntentSender implements Parcelable { * none associated with it. */ public int getCreatorUid() { - try { - return ActivityManager.getService() - .getUidForIntentSender(mTarget); - } catch (RemoteException e) { - // Should never happen. - return -1; - } + return getCachedInfo().getCreatorUid(); } /** @@ -268,14 +254,8 @@ public class IntentSender implements Parcelable { * none associated with it. */ public UserHandle getCreatorUserHandle() { - try { - int uid = ActivityManager.getService() - .getUidForIntentSender(mTarget); - return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null; - } catch (RemoteException e) { - // Should never happen. - return null; - } + int uid = getCachedInfo().getCreatorUid(); + return uid > 0 ? new UserHandle(UserHandle.getUserId(uid)) : null; } /** @@ -384,4 +364,16 @@ public class IntentSender implements Parcelable { public IntentSender(IBinder target) { mTarget = IIntentSender.Stub.asInterface(target); } + + private PendingIntentInfo getCachedInfo() { + if (mCachedInfo == null) { + try { + mCachedInfo = ActivityManager.getService().getInfoForIntentSender(mTarget); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + return mCachedInfo; + } } diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java index 9e568a40e0ee..e1e6f75d152f 100644 --- a/core/java/android/content/SyncRequest.java +++ b/core/java/android/content/SyncRequest.java @@ -17,6 +17,7 @@ package android.content; import android.accounts.Account; +import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Bundle; @@ -58,6 +59,8 @@ public class SyncRequest implements Parcelable { private final boolean mIsAuthority; /** Sync should be run in lieu of other syncs. */ private final boolean mIsExpedited; + /** Sync sound be ran as an expedited job. */ + private final boolean mIsScheduledAsExpeditedJob; /** * {@hide} @@ -79,6 +82,14 @@ public class SyncRequest implements Parcelable { /** * {@hide} + * @return whether this sync is scheduled as an expedited job. + */ + public boolean isScheduledAsExpeditedJob() { + return mIsScheduledAsExpeditedJob; + } + + /** + * {@hide} * * @return account object for this sync. * @throws IllegalArgumentException if this function is called for a request that targets a @@ -149,6 +160,7 @@ public class SyncRequest implements Parcelable { parcel.writeInt((mDisallowMetered ? 1 : 0)); parcel.writeInt((mIsAuthority ? 1 : 0)); parcel.writeInt((mIsExpedited? 1 : 0)); + parcel.writeInt(mIsScheduledAsExpeditedJob ? 1 : 0); parcel.writeParcelable(mAccountToSync, flags); parcel.writeString(mAuthority); } @@ -161,6 +173,7 @@ public class SyncRequest implements Parcelable { mDisallowMetered = (in.readInt() != 0); mIsAuthority = (in.readInt() != 0); mIsExpedited = (in.readInt() != 0); + mIsScheduledAsExpeditedJob = (in.readInt() != 0); mAccountToSync = in.readParcelable(null); mAuthority = in.readString(); } @@ -174,6 +187,7 @@ public class SyncRequest implements Parcelable { mIsPeriodic = (b.mSyncType == Builder.SYNC_TYPE_PERIODIC); mIsAuthority = (b.mSyncTarget == Builder.SYNC_TARGET_ADAPTER); mIsExpedited = b.mExpedited; + mIsScheduledAsExpeditedJob = b.mScheduleAsExpeditedJob; mExtras = new Bundle(b.mCustomExtras); // For now we merge the sync config extras & the custom extras into one bundle. // TODO: pass the configuration extras through separately. @@ -258,6 +272,11 @@ public class SyncRequest implements Parcelable { */ private boolean mRequiresCharging; + /** + * Whether the sync should be scheduled as an expedited job. + */ + private boolean mScheduleAsExpeditedJob; + public Builder() { } @@ -309,7 +328,8 @@ public class SyncRequest implements Parcelable { * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE}, * {@link ContentResolver#SYNC_EXTRAS_FORCE}, * {@link ContentResolver#SYNC_EXTRAS_EXPEDITED}, - * {@link ContentResolver#SYNC_EXTRAS_MANUAL} + * {@link ContentResolver#SYNC_EXTRAS_MANUAL}, + * {@link ContentResolver#SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB} * set to true. If any are supplied then an <code>IllegalArgumentException</code> will * be thrown. * @@ -500,6 +520,22 @@ public class SyncRequest implements Parcelable { } /** + * Convenience function for setting + * {@link ContentResolver#SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB}. + * + * <p> Not to be confused with {@link ContentResolver#SYNC_EXTRAS_EXPEDITED}. + * + * <p> Not valid for periodic syncs, expedited syncs, and syncs that require charging - an + * <code>IllegalArgumentException</code> will be thrown in {@link #build()}. + * + * @param scheduleAsExpeditedJob whether to schedule as an expedited job. Default false. + */ + public @NonNull Builder setScheduleAsExpeditedJob(boolean scheduleAsExpeditedJob) { + mScheduleAsExpeditedJob = scheduleAsExpeditedJob; + return this; + } + + /** * Performs validation over the request and throws the runtime exception * <code>IllegalArgumentException</code> if this validation fails. * @@ -507,11 +543,6 @@ public class SyncRequest implements Parcelable { * builder. */ public SyncRequest build() { - // Validate the extras bundle - ContentResolver.validateSyncExtrasBundle(mCustomExtras); - if (mCustomExtras == null) { - mCustomExtras = new Bundle(); - } // Combine builder extra flags into the config bundle. mSyncConfigExtras = new Bundle(); if (mIgnoreBackoff) { @@ -532,17 +563,35 @@ public class SyncRequest implements Parcelable { if (mExpedited) { mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); } + if (mScheduleAsExpeditedJob) { + mSyncConfigExtras.putBoolean( + ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true); + } if (mIsManual) { mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); } + + if (mCustomExtras == null) { + mCustomExtras = new Bundle(); + } + // Validate the extras bundles + ContentResolver.validateSyncExtrasBundle(mCustomExtras); + // If this is a periodic sync ensure than invalid extras were not set. if (mSyncType == SYNC_TYPE_PERIODIC) { - // If this is a periodic sync ensure than invalid extras were not set. if (ContentResolver.invalidPeriodicExtras(mCustomExtras) || ContentResolver.invalidPeriodicExtras(mSyncConfigExtras)) { throw new IllegalArgumentException("Illegal extras were set"); } } + // If this sync is scheduled as an EJ, ensure that invalid extras were not set. + if (mCustomExtras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB) + || mScheduleAsExpeditedJob) { + if (ContentResolver.hasInvalidScheduleAsEjExtras(mCustomExtras) + || ContentResolver.hasInvalidScheduleAsEjExtras(mSyncConfigExtras)) { + throw new IllegalArgumentException("Illegal extras were set"); + } + } // Ensure that a target for the sync has been set. if (mSyncTarget == SYNC_TARGET_UNKNOWN) { throw new IllegalArgumentException("Must specify an adapter with" + diff --git a/core/java/android/content/pm/AppSearchPerson.java b/core/java/android/content/pm/AppSearchPerson.java index d70ac918e208..66295eb513d8 100644 --- a/core/java/android/content/pm/AppSearchPerson.java +++ b/core/java/android/content/pm/AppSearchPerson.java @@ -42,7 +42,7 @@ public class AppSearchPerson extends GenericDocument { public static final String KEY_IS_BOT = "isBot"; public static final String KEY_IS_IMPORTANT = "isImportant"; - private AppSearchPerson(@NonNull GenericDocument document) { + public AppSearchPerson(@NonNull GenericDocument document) { super(document); } diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java index ebe202b2a3fa..5af3b5a01d07 100644 --- a/core/java/android/content/pm/AppSearchShortcutInfo.java +++ b/core/java/android/content/pm/AppSearchShortcutInfo.java @@ -28,7 +28,6 @@ import android.content.LocusId; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.PersistableBundle; -import android.os.UserHandle; import android.text.TextUtils; import android.util.ArraySet; @@ -53,17 +52,23 @@ public class AppSearchShortcutInfo extends GenericDocument { /** The name of the schema type for {@link ShortcutInfo} documents.*/ public static final String SCHEMA_TYPE = "Shortcut"; - public static final String KEY_PACKAGE_NAME = "packageName"; public static final String KEY_ACTIVITY = "activity"; - public static final String KEY_TITLE = "title"; - public static final String KEY_TEXT = "text"; + public static final String KEY_SHORT_LABEL = "shortLabel"; + public static final String KEY_SHORT_LABEL_RES_ID = "shortLabelResId"; + public static final String KEY_SHORT_LABEL_RES_NAME = "shortLabelResName"; + public static final String KEY_LONG_LABEL = "longLabel"; + public static final String KEY_LONG_LABEL_RES_ID = "longLabelResId"; + public static final String KEY_LONG_LABEL_RES_NAME = "longLabelResName"; public static final String KEY_DISABLED_MESSAGE = "disabledMessage"; + public static final String KEY_DISABLED_MESSAGE_RES_ID = "disabledMessageResId"; + public static final String KEY_DISABLED_MESSAGE_RES_NAME = "disabledMessageResName"; public static final String KEY_CATEGORIES = "categories"; public static final String KEY_INTENTS = "intents"; public static final String KEY_INTENT_PERSISTABLE_EXTRAS = "intentPersistableExtras"; public static final String KEY_PERSON = "person"; public static final String KEY_LOCUS_ID = "locusId"; public static final String KEY_RANK = "rank"; + public static final String KEY_IMPLICIT_RANK = "implicitRank"; public static final String KEY_EXTRAS = "extras"; public static final String KEY_FLAGS = "flags"; public static final String KEY_ICON_RES_ID = "iconResId"; @@ -73,36 +78,62 @@ public class AppSearchShortcutInfo extends GenericDocument { public static final String KEY_DISABLED_REASON = "disabledReason"; public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) - .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_PACKAGE_NAME) - .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED) + .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_ACTIVITY) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS) .build() - ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_ACTIVITY) + ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_SHORT_LABEL) .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES) .build() - ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_TITLE) + ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_SHORT_LABEL_RES_ID) .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES) .build() - ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_TEXT) + ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_SHORT_LABEL_RES_NAME) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE) + .build() + + ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_LONG_LABEL) .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES) .build() + ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_LONG_LABEL_RES_ID) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .build() + + ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_LONG_LABEL_RES_NAME) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE) + .build() + ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_DISABLED_MESSAGE) .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE) .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE) .build() + ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder( + KEY_DISABLED_MESSAGE_RES_ID) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .build() + + ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder( + KEY_DISABLED_MESSAGE_RES_NAME) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE) + .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE) + .build() + ).addProperty(new AppSearchSchema.StringPropertyConfig.Builder(KEY_CATEGORIES) .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN) @@ -135,6 +166,10 @@ public class AppSearchShortcutInfo extends GenericDocument { .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) .build() + ).addProperty(new AppSearchSchema.Int64PropertyConfig.Builder(KEY_IMPLICIT_RANK) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .build() + ).addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(KEY_EXTRAS) .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) .build() @@ -183,13 +218,21 @@ public class AppSearchShortcutInfo extends GenericDocument { Objects.requireNonNull(shortcutInfo); return new Builder(shortcutInfo.getId()) .setActivity(shortcutInfo.getActivity()) - .setPackageName(shortcutInfo.getPackage()) - .setTitle(shortcutInfo.getShortLabel()) - .setText(shortcutInfo.getLongLabel()) + .setNamespace(shortcutInfo.getPackage()) + .setShortLabel(shortcutInfo.getShortLabel()) + .setShortLabelResId(shortcutInfo.getShortLabelResourceId()) + .setShortLabelResName(shortcutInfo.getTitleResName()) + .setLongLabel(shortcutInfo.getLongLabel()) + .setLongLabelResId(shortcutInfo.getLongLabelResourceId()) + .setLongLabelResName(shortcutInfo.getTextResName()) .setDisabledMessage(shortcutInfo.getDisabledMessage()) + .setDisabledMessageResId(shortcutInfo.getDisabledMessageResourceId()) + .setDisabledMessageResName(shortcutInfo.getDisabledMessageResName()) .setCategories(shortcutInfo.getCategories()) .setIntents(shortcutInfo.getIntents()) .setRank(shortcutInfo.getRank()) + .setImplicitRank(shortcutInfo.getImplicitRank() + | (shortcutInfo.isRankChanged() ? ShortcutInfo.RANK_CHANGED_BIT : 0)) .setExtras(shortcutInfo.getExtras()) .setCreationTimestampMillis(shortcutInfo.getLastChangedTimestamp()) .setFlags(shortcutInfo.getFlags()) @@ -207,17 +250,8 @@ public class AppSearchShortcutInfo extends GenericDocument { * @hide */ @NonNull - public ShortcutInfo toShortcutInfo() { - return toShortcutInfo(UserHandle.myUserId()); - } - - /** - * @hide - * TODO: This should be @SystemApi when AppSearchShortcutInfo unhides. - */ - @NonNull - public ShortcutInfo toShortcutInfo(@UserIdInt final int userId) { - final String packageName = getPropertyString(KEY_PACKAGE_NAME); + public ShortcutInfo toShortcutInfo(@UserIdInt int userId) { + final String packageName = getNamespace(); final String activityString = getPropertyString(KEY_ACTIVITY); final ComponentName activity = activityString == null ? null : ComponentName.unflattenFromString(activityString); @@ -228,15 +262,24 @@ public class AppSearchShortcutInfo extends GenericDocument { // @hide and @UnsupportedAppUsage, we could migrate existing usage in platform with // LauncherApps#getShortcutIconDrawable instead. final Icon icon = null; - final String title = getPropertyString(KEY_TITLE); - final String text = getPropertyString(KEY_TEXT); + final String shortLabel = getPropertyString(KEY_SHORT_LABEL); + final int shortLabelResId = (int) getPropertyLong(KEY_SHORT_LABEL_RES_ID); + final String shortLabelResName = getPropertyString(KEY_SHORT_LABEL_RES_NAME); + final String longLabel = getPropertyString(KEY_LONG_LABEL); + final int longLabelResId = (int) getPropertyLong(KEY_LONG_LABEL_RES_ID); + final String longLabelResName = getPropertyString(KEY_LONG_LABEL_RES_NAME); final String disabledMessage = getPropertyString(KEY_DISABLED_MESSAGE); + final int disabledMessageResId = (int) getPropertyLong(KEY_DISABLED_MESSAGE_RES_ID); + final String disabledMessageResName = getPropertyString(KEY_DISABLED_MESSAGE_RES_NAME); final String[] categories = getPropertyStringArray(KEY_CATEGORIES); final Set<String> categoriesSet = categories == null - ? new ArraySet<>() : new ArraySet<>(Arrays.asList(categories)); + ? null : new ArraySet<>(Arrays.asList(categories)); final String[] intentsStrings = getPropertyStringArray(KEY_INTENTS); final Intent[] intents = intentsStrings == null - ? null : Arrays.stream(intentsStrings).map(uri -> { + ? new Intent[0] : Arrays.stream(intentsStrings).map(uri -> { + if (TextUtils.isEmpty(uri)) { + return new Intent(Intent.ACTION_VIEW); + } try { return Intent.parseUri(uri, /* flags =*/ 0); } catch (URISyntaxException e) { @@ -251,15 +294,18 @@ public class AppSearchShortcutInfo extends GenericDocument { if (intents != null) { for (int i = 0; i < intents.length; i++) { final Intent intent = intents[i]; - if (intent != null) { - intent.replaceExtras(intentExtrases[i].size() == 0 ? null : intentExtrases[i]); + if (intent == null || intentExtrases == null || intentExtrases.length <= i + || intentExtrases[i] == null || intentExtrases[i].size() == 0) { + continue; } + intent.replaceExtras(intentExtrases[i]); } } final Person[] persons = parsePerson(getPropertyDocumentArray(KEY_PERSON)); final String locusIdString = getPropertyString(KEY_LOCUS_ID); final LocusId locusId = locusIdString == null ? null : new LocusId(locusIdString); final int rank = (int) getPropertyLong(KEY_RANK); + final int implicitRank = (int) getPropertyLong(KEY_IMPLICIT_RANK); final byte[] extrasByte = getPropertyBytes(KEY_EXTRAS); final PersistableBundle extras = transformToPersistableBundle(extrasByte); final int flags = parseFlags(getPropertyLongArray(KEY_FLAGS)); @@ -268,12 +314,17 @@ public class AppSearchShortcutInfo extends GenericDocument { final String iconUri = getPropertyString(KEY_ICON_URI); final String bitmapPath = getPropertyString(KEY_BITMAP_PATH); final int disabledReason = (int) getPropertyLong(KEY_DISABLED_REASON); - return new ShortcutInfo( - userId, getUri(), packageName, activity, icon, title, 0, null, - text, 0, null, disabledMessage, 0, null, - categoriesSet, intents, rank, extras, + final ShortcutInfo si = new ShortcutInfo( + userId, getUri(), packageName, activity, icon, shortLabel, shortLabelResId, + shortLabelResName, longLabel, longLabelResId, longLabelResName, disabledMessage, + disabledMessageResId, disabledMessageResName, categoriesSet, intents, rank, extras, getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri, disabledReason, persons, locusId, 0); + si.setImplicitRank(implicitRank); + if ((implicitRank & ShortcutInfo.RANK_CHANGED_BIT) != 0) { + si.setRankChanged(); + } + return si; } /** @hide */ @@ -310,9 +361,9 @@ public class AppSearchShortcutInfo extends GenericDocument { * @hide */ @NonNull - public Builder setTitle(@Nullable final CharSequence shortLabel) { + public Builder setShortLabel(@Nullable final CharSequence shortLabel) { if (!TextUtils.isEmpty(shortLabel)) { - setPropertyString(KEY_TITLE, Preconditions.checkStringNotEmpty( + setPropertyString(KEY_SHORT_LABEL, Preconditions.checkStringNotEmpty( shortLabel, "shortLabel cannot be empty").toString()); } return this; @@ -322,13 +373,50 @@ public class AppSearchShortcutInfo extends GenericDocument { * @hide */ @NonNull - public Builder setText(@Nullable final CharSequence longLabel) { + public Builder setShortLabelResId(@Nullable final int shortLabelResId) { + setPropertyLong(KEY_SHORT_LABEL_RES_ID, shortLabelResId); + return this; + } + + /** + * @hide + */ + public Builder setShortLabelResName(@Nullable final String shortLabelResName) { + if (!TextUtils.isEmpty(shortLabelResName)) { + setPropertyString(KEY_SHORT_LABEL_RES_NAME, shortLabelResName); + } + return this; + } + + /** + * @hide + */ + @NonNull + public Builder setLongLabel(@Nullable final CharSequence longLabel) { if (!TextUtils.isEmpty(longLabel)) { - setPropertyString(KEY_TEXT, Preconditions.checkStringNotEmpty( + setPropertyString(KEY_LONG_LABEL, Preconditions.checkStringNotEmpty( longLabel, "longLabel cannot be empty").toString()); } return this; + } + + /** + * @hide + */ + @NonNull + public Builder setLongLabelResId(@Nullable final int longLabelResId) { + setPropertyLong(KEY_LONG_LABEL_RES_ID, longLabelResId); + return this; + } + /** + * @hide + */ + public Builder setLongLabelResName(@Nullable final String longLabelResName) { + if (!TextUtils.isEmpty(longLabelResName)) { + setPropertyString(KEY_LONG_LABEL_RES_NAME, longLabelResName); + } + return this; } /** @@ -347,6 +435,25 @@ public class AppSearchShortcutInfo extends GenericDocument { * @hide */ @NonNull + public Builder setDisabledMessageResId(@Nullable final int disabledMessageResId) { + setPropertyLong(KEY_DISABLED_MESSAGE_RES_ID, disabledMessageResId); + return this; + } + + /** + * @hide + */ + public Builder setDisabledMessageResName(@Nullable final String disabledMessageResName) { + if (!TextUtils.isEmpty(disabledMessageResName)) { + setPropertyString(KEY_DISABLED_MESSAGE_RES_NAME, disabledMessageResName); + } + return this; + } + + /** + * @hide + */ + @NonNull public Builder setCategories(@Nullable final Set<String> categories) { if (categories != null && !categories.isEmpty()) { setPropertyString(KEY_CATEGORIES, categories.stream().toArray(String[]::new)); @@ -384,9 +491,8 @@ public class AppSearchShortcutInfo extends GenericDocument { intentExtrases[i] = extras == null ? new byte[0] : transformToByteArray(new PersistableBundle(extras)); } - - setPropertyString(KEY_INTENTS, Arrays.stream(intents).map(it -> - it.toUri(0)).toArray(String[]::new)); + setPropertyString(KEY_INTENTS, Arrays.stream(intents).map(it -> it.toUri(0)) + .toArray(String[]::new)); setPropertyBytes(KEY_INTENT_PERSISTABLE_EXTRAS, intentExtrases); return this; } @@ -410,10 +516,14 @@ public class AppSearchShortcutInfo extends GenericDocument { if (persons == null || persons.length == 0) { return this; } - setPropertyDocument(KEY_PERSON, - Arrays.stream(persons).map(person -> AppSearchPerson.instance( - Objects.requireNonNull(person, "persons cannot contain null")) - ).toArray(AppSearchPerson[]::new)); + final GenericDocument[] documents = new GenericDocument[persons.length]; + for (int i = 0; i < persons.length; i++) { + final Person person = persons[i]; + if (person == null) continue; + final AppSearchPerson appSearchPerson = AppSearchPerson.instance(person); + documents[i] = appSearchPerson; + } + setPropertyDocument(KEY_PERSON, documents); return this; } @@ -422,8 +532,7 @@ public class AppSearchShortcutInfo extends GenericDocument { */ @NonNull public Builder setRank(final int rank) { - Preconditions.checkArgument((0 <= rank), - "Rank cannot be negative or bigger than MAX_RANK"); + Preconditions.checkArgument((0 <= rank), "Rank cannot be negative"); setPropertyLong(KEY_RANK, rank); return this; } @@ -432,6 +541,15 @@ public class AppSearchShortcutInfo extends GenericDocument { * @hide */ @NonNull + public Builder setImplicitRank(final int rank) { + setPropertyLong(KEY_IMPLICIT_RANK, rank); + return this; + } + + /** + * @hide + */ + @NonNull public Builder setExtras(@Nullable final PersistableBundle extras) { if (extras != null) { setPropertyBytes(KEY_EXTRAS, transformToByteArray(extras)); @@ -444,7 +562,7 @@ public class AppSearchShortcutInfo extends GenericDocument { */ public Builder setPackageName(@Nullable final String packageName) { if (!TextUtils.isEmpty(packageName)) { - setPropertyString(KEY_PACKAGE_NAME, packageName); + setNamespace(packageName); } return this; } @@ -579,7 +697,14 @@ public class AppSearchShortcutInfo extends GenericDocument { @NonNull private static Person[] parsePerson(@Nullable final GenericDocument[] persons) { - return persons == null ? new Person[0] : Arrays.stream(persons).map(it -> - ((AppSearchPerson) it).toPerson()).toArray(Person[]::new); + if (persons == null) return new Person[0]; + final Person[] ret = new Person[persons.length]; + for (int i = 0; i < persons.length; i++) { + final GenericDocument document = persons[i]; + if (document == null) continue; + final AppSearchPerson person = new AppSearchPerson(document); + ret[i] = person.toPerson(); + } + return ret; } } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 01ff4326a800..0aa1be94d279 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -136,6 +136,18 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public int fullBackupContent = 0; /** + * Applications can set this attribute to an xml resource within their app where they specified + * the rules determining which files and directories can be copied from the device as part of + * backup or transfer operations. + *<p> + * Set from the {@link android.R.styleable#AndroidManifestApplication_dataExtractionRules} + * attribute in the manifest. + * + * @hide + */ + public int dataExtractionRulesRes = 0; + + /** * <code>true</code> if the package is capable of presenting a unified interface representing * multiple profiles. * @hide @@ -1105,19 +1117,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * <p> * This property is the compile-time equivalent of * {@link android.os.Build.VERSION#CODENAME Build.VERSION.SDK_INT}. - * - * @hide For platform use only; we don't expect developers to need to read this value. */ public int compileSdkVersion; /** - * The development codename (ex. "O", "REL") of the framework against which the application + * The development codename (ex. "S", "REL") of the framework against which the application * claims to have been compiled, or {@code null} if not specified. * <p> * This property is the compile-time equivalent of * {@link android.os.Build.VERSION#CODENAME Build.VERSION.CODENAME}. - * - * @hide For platform use only; we don't expect developers to need to read this value. */ @Nullable public String compileSdkVersionCodename; @@ -1524,6 +1532,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { pw.println(prefix + "fullBackupContent=" + (fullBackupContent < 0 ? "false" : "true")); } + if (dataExtractionRulesRes != 0) { + pw.println(prefix + "dataExtractionRules=@xml/" + dataExtractionRulesRes); + } pw.println(prefix + "crossProfile=" + (crossProfile ? "true" : "false")); if (networkSecurityConfigRes != 0) { pw.println(prefix + "networkSecurityConfigRes=0x" @@ -1753,6 +1764,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { uiOptions = orig.uiOptions; backupAgentName = orig.backupAgentName; fullBackupContent = orig.fullBackupContent; + dataExtractionRulesRes = orig.dataExtractionRulesRes; crossProfile = orig.crossProfile; networkSecurityConfigRes = orig.networkSecurityConfigRes; category = orig.category; @@ -1840,6 +1852,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeInt(descriptionRes); dest.writeInt(uiOptions); dest.writeInt(fullBackupContent); + dest.writeInt(dataExtractionRulesRes); dest.writeBoolean(crossProfile); dest.writeInt(networkSecurityConfigRes); dest.writeInt(category); @@ -1924,6 +1937,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { descriptionRes = source.readInt(); uiOptions = source.readInt(); fullBackupContent = source.readInt(); + dataExtractionRulesRes = source.readInt(); crossProfile = source.readBoolean(); networkSecurityConfigRes = source.readInt(); category = source.readInt(); diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index b290679c9fcc..48b634e52846 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -19,6 +19,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.Activity; import android.app.AppOpsManager.Mode; import android.content.ComponentName; @@ -418,6 +419,7 @@ public class CrossProfileApps { * * @hide */ + @TestApi public boolean canConfigureInteractAcrossProfiles(@NonNull String packageName) { try { return mService.canConfigureInteractAcrossProfiles(packageName); diff --git a/core/java/android/content/pm/DataLoaderParamsParcel.aidl b/core/java/android/content/pm/DataLoaderParamsParcel.aidl index d40012fd5718..29d472e6e927 100644 --- a/core/java/android/content/pm/DataLoaderParamsParcel.aidl +++ b/core/java/android/content/pm/DataLoaderParamsParcel.aidl @@ -23,7 +23,7 @@ import android.content.pm.DataLoaderType; * @hide */ parcelable DataLoaderParamsParcel { - DataLoaderType type; + DataLoaderType type = DataLoaderType.NONE; @utf8InCpp String packageName; @utf8InCpp String className; @utf8InCpp String arguments; diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl index 29a55b7a74da..b34574811bca 100644 --- a/core/java/android/content/pm/IShortcutService.aidl +++ b/core/java/android/content/pm/IShortcutService.aidl @@ -78,4 +78,7 @@ interface IShortcutService { ParceledListSlice getShortcuts(String packageName, int matchFlags, int userId); void pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId); + + void updateShortcutVisibility(String callingPkg, String packageName, in byte[] certificate, + in boolean visible, int userId); }
\ No newline at end of file diff --git a/core/java/android/content/pm/InstallationFileParcel.aidl b/core/java/android/content/pm/InstallationFileParcel.aidl index b7efc1947cc3..09d1a3291b69 100644 --- a/core/java/android/content/pm/InstallationFileParcel.aidl +++ b/core/java/android/content/pm/InstallationFileParcel.aidl @@ -24,7 +24,7 @@ import android.content.pm.InstallationFileLocation; */ parcelable InstallationFileParcel { String name; - InstallationFileLocation location; + InstallationFileLocation location = InstallationFileLocation.UNKNOWN; long size; byte[] metadata; byte[] signature; diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 567501cba70b..42cbe3586db3 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1173,7 +1173,6 @@ public class PackageInstaller { * {@hide} */ @SystemApi - @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2) public @Nullable DataLoaderParams getDataLoaderParams() { try { DataLoaderParamsParcel data = mSession.getDataLoaderParams(); @@ -1213,7 +1212,6 @@ public class PackageInstaller { * {@hide} */ @SystemApi - @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2) public void addFile(@FileLocation int location, @NonNull String name, long lengthBytes, @NonNull byte[] metadata, @Nullable byte[] signature) { try { @@ -1237,7 +1235,6 @@ public class PackageInstaller { * {@hide} */ @SystemApi - @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2) public void removeFile(@FileLocation int location, @NonNull String name) { try { mSession.removeFile(location, name); @@ -2050,9 +2047,7 @@ public class PackageInstaller { * {@hide} */ @SystemApi - @RequiresPermission(allOf = { - Manifest.permission.INSTALL_PACKAGES, - Manifest.permission.USE_INSTALLER_V2}) + @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) public void setDataLoaderParams(@NonNull DataLoaderParams dataLoaderParams) { this.dataLoaderParams = dataLoaderParams; } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index fdb00c690ffe..29dea6bb09db 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3601,7 +3601,7 @@ public abstract class PackageManager { * 1 - IncFs v1, core features, no PerUid support. Optional in R. * 2 - IncFs v2, PerUid support, fs-verity support. Required in S. * - * @see IncrementalManager#isFeatureEnabled and IncrementalManager#isV2() + * @see IncrementalManager#getVersion() * @hide */ @SystemApi @@ -3623,11 +3623,26 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for - * camera. When sensory privacy for the camera is enabled no camera data is send to clients, + * microphone. When sensory privacy for the microphone is enabled no microphone data is sent to + * clients, e.g. all audio data is silent. + * + * @hide + */ + @SystemApi + @TestApi + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_MICROPHONE_TOGGLE = "android.hardware.microphone.toggle"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for + * camera. When sensory privacy for the camera is enabled no camera data is sent to clients, * e.g. the view finder in a camera app would appear blank. * * @hide */ + @SystemApi + @TestApi @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle"; @@ -4060,18 +4075,10 @@ public abstract class PackageManager { public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 17; /** - * Permission flag: The permission is restricted but the app is exempt - * from the restriction and is allowed to hold this permission in its - * full form and the exemption is provided by the held roles. - * - * @hide - */ - @SystemApi - public static final int FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT = 1 << 18; - - /** * Permission flag: This location permission is selected as the level of granularity of * location accuracy. + * Example: If this flag is set for ACCESS_FINE_LOCATION, FINE location is the selected location + * accuracy for location permissions. * * @hide */ @@ -4096,8 +4103,7 @@ public abstract class PackageManager { public static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT = FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT | FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT - | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT - | FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; + | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; /** * Mask for all permission flags. @@ -4182,20 +4188,11 @@ public abstract class PackageManager { */ public static final int FLAG_PERMISSION_WHITELIST_UPGRADE = 1 << 2; - /** - * Permission allowlist flag: permissions exempted by the system - * when being granted a role. - * Permissions can also be exempted by the installer, the system, or on - * upgrade. - */ - public static final int FLAG_PERMISSION_ALLOWLIST_ROLE = 1 << 3; - /** @hide */ @IntDef(flag = true, prefix = {"FLAG_PERMISSION_WHITELIST_"}, value = { FLAG_PERMISSION_WHITELIST_SYSTEM, FLAG_PERMISSION_WHITELIST_INSTALLER, - FLAG_PERMISSION_WHITELIST_UPGRADE, - FLAG_PERMISSION_ALLOWLIST_ROLE + FLAG_PERMISSION_WHITELIST_UPGRADE }) @Retention(RetentionPolicy.SOURCE) public @interface PermissionWhitelistFlags {} @@ -5227,10 +5224,6 @@ public abstract class PackageManager { * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_INSTALLER} flag. * Can be accessed by pre-installed holders of a dedicated permission or the * installer on record. - * - * <li>one for cases where the system exempts the permission when granting a role. - * This list corresponds to the {@link #FLAG_PERMISSION_ALLOWLIST_ROLE} flag. Can - * be accessed by pre-installed holders of a dedicated permission. * </ol> * * <p> @@ -5249,7 +5242,6 @@ public abstract class PackageManager { * @see #FLAG_PERMISSION_WHITELIST_SYSTEM * @see #FLAG_PERMISSION_WHITELIST_UPGRADE * @see #FLAG_PERMISSION_WHITELIST_INSTALLER - * @see #FLAG_PERMISSION_ALLOWLIST_ROLE * * @throws SecurityException if you try to access a whitelist that you have no access to. */ @@ -5289,10 +5281,6 @@ public abstract class PackageManager { * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_INSTALLER} flag. * Can be modified by pre-installed holders of a dedicated permission or the installer * on record. - * - * <li>one for cases where the system exempts the permission when permission when - * granting a role. This list corresponds to the {@link #FLAG_PERMISSION_ALLOWLIST_ROLE} - * flag. Can be modified by pre-installed holders of a dedicated permission. * </ol> * * <p>You need to specify the whitelists for which to set the whitelisted permissions @@ -5316,7 +5304,6 @@ public abstract class PackageManager { * @see #FLAG_PERMISSION_WHITELIST_SYSTEM * @see #FLAG_PERMISSION_WHITELIST_UPGRADE * @see #FLAG_PERMISSION_WHITELIST_INSTALLER - * @see #FLAG_PERMISSION_ALLOWLIST_ROLE * * @throws SecurityException if you try to modify a whitelist that you have no access to. */ @@ -5386,7 +5373,6 @@ public abstract class PackageManager { * @see #FLAG_PERMISSION_WHITELIST_SYSTEM * @see #FLAG_PERMISSION_WHITELIST_UPGRADE * @see #FLAG_PERMISSION_WHITELIST_INSTALLER - * @see #FLAG_PERMISSION_ALLOWLIST_ROLE * * @throws SecurityException if you try to modify a whitelist that you have no access to. */ diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index 0e70a3e4e600..83baca668d55 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -411,14 +411,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_IMMUTABLY_RESTRICTED = 1<<4; /** - * Flag for {@link #flags}, corresponding to <code>installerExemptIgnored</code> - * value of {@link android.R.attr#permissionFlags}. - * - * <p> Modifier for permission restriction. This permission cannot be exempted by the installer. - */ - public static final int FLAG_INSTALLER_EXEMPT_IGNORED = 1 << 5; - - /** * Flag for {@link #flags}, indicating that this permission has been * installed into the system's globally defined permissions. */ @@ -724,11 +716,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { } /** @hide */ - public boolean isInstallerExemptIgnored() { - return (flags & PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED) != 0; - } - - /** @hide */ public boolean isAppOp() { return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0; } diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 5f80ba110773..275e81c0e868 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -68,7 +68,8 @@ public final class ShortcutInfo implements Parcelable { private static final int IMPLICIT_RANK_MASK = 0x7fffffff; - private static final int RANK_CHANGED_BIT = ~IMPLICIT_RANK_MASK; + /** @hide */ + public static final int RANK_CHANGED_BIT = ~IMPLICIT_RANK_MASK; /** @hide */ public static final int RANK_NOT_SET = Integer.MAX_VALUE; @@ -1595,6 +1596,9 @@ public final class ShortcutInfo implements Parcelable { */ @Nullable public Intent[] getIntents() { + if (mIntents == null) { + return null; + } final Intent[] ret = new Intent[mIntents.length]; for (int i = 0; i < ret.length; i++) { diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index 35c99a13a152..d3bac79aa2b9 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -39,6 +39,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import com.android.internal.annotations.VisibleForTesting; @@ -771,4 +772,20 @@ public class ShortcutManager { } } + /** + * Granting another app the access to the shortcuts you own. You must provide the package name + * and their SHA256 certificate digest in order to granting the access. + * + * Once granted, the other app can retain a copy of all the shortcuts you own when calling + * {@link LauncherApps#getShortcuts(LauncherApps.ShortcutQuery, UserHandle)}. + */ + public void updateShortcutVisibility(@NonNull final String packageName, + @Nullable final byte[] certificate, final boolean visible) { + try { + mService.updateShortcutVisibility(mContext.getPackageName(), packageName, certificate, + visible, injectMyUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index d81dff8f2908..cfb6e1b572aa 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -18,6 +18,7 @@ package android.content.pm; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.TestApi; import android.annotation.UserIdInt; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; @@ -47,6 +48,7 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@TestApi public class UserInfo implements Parcelable { /** diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java index bc5d14a533e2..0d5b33cd8672 100644 --- a/core/java/android/content/pm/dex/DexMetadataHelper.java +++ b/core/java/android/content/pm/dex/DexMetadataHelper.java @@ -24,16 +24,16 @@ import android.content.pm.parsing.ApkLiteParseUtils; import android.content.pm.parsing.PackageLite; import android.os.SystemProperties; import android.util.ArrayMap; -import android.util.jar.StrictJarFile; import android.util.JsonReader; import android.util.Log; +import android.util.jar.StrictJarFile; import com.android.internal.annotations.VisibleForTesting; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.file.Files; import java.nio.file.Paths; @@ -53,7 +53,10 @@ public class DexMetadataHelper { /** $> adb shell 'setprop log.tag.DexMetadataHelper VERBOSE' */ public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); /** $> adb shell 'setprop pm.dexopt.dm.require_manifest true' */ - private static String PROPERTY_DM_JSON_MANIFEST_REQUIRED = "pm.dexopt.dm.require_manifest"; + private static final String PROPERTY_DM_JSON_MANIFEST_REQUIRED = + "pm.dexopt.dm.require_manifest"; + /** $> adb shell 'setprop pm.dexopt.dm.require_fsverity true' */ + private static final String PROPERTY_DM_FSVERITY_REQUIRED = "pm.dexopt.dm.require_fsverity"; private static final String DEX_METADATA_FILE_EXTENSION = ".dm"; @@ -70,6 +73,13 @@ public class DexMetadataHelper { } /** + * Returns whether fs-verity is required to install a dex metadata + */ + public static boolean isFsVerityRequired() { + return SystemProperties.getBoolean(PROPERTY_DM_FSVERITY_REQUIRED, false); + } + + /** * Return the size (in bytes) of all dex metadata files associated with the given package. */ public static long getPackageDexMetadataSize(PackageLite pkg) { diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 7a01392a24e8..29edd405be6b 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -260,6 +260,8 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage setFullBackupContent(int fullBackupContent); + ParsingPackage setDataExtractionRules(int dataExtractionRules); + ParsingPackage setHasDomainUrls(boolean hasDomainUrls); ParsingPackage setIconRes(int iconRes); diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index c1a93d8c2428..067787d725d9 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -334,6 +334,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { private int descriptionRes; private int fullBackupContent; + private int dataExtractionRules; private int iconRes; private int installLocation = ParsingPackageUtils.PARSE_DEFAULT_INSTALL_LOCATION; private int labelRes; @@ -1015,6 +1016,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { appInfo.enabled = getBoolean(Booleans.ENABLED); // appInfo.enabledSetting appInfo.fullBackupContent = fullBackupContent; + appInfo.dataExtractionRulesRes = dataExtractionRules; // TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy // appInfo.mHiddenApiPolicy // appInfo.hiddenUntilInstalled @@ -1163,6 +1165,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeInt(this.compatibleWidthLimitDp); dest.writeInt(this.descriptionRes); dest.writeInt(this.fullBackupContent); + dest.writeInt(this.dataExtractionRules); dest.writeInt(this.iconRes); dest.writeInt(this.installLocation); dest.writeInt(this.labelRes); @@ -1284,6 +1287,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.compatibleWidthLimitDp = in.readInt(); this.descriptionRes = in.readInt(); this.fullBackupContent = in.readInt(); + this.dataExtractionRules = in.readInt(); this.iconRes = in.readInt(); this.installLocation = in.readInt(); this.labelRes = in.readInt(); @@ -1808,6 +1812,11 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override + public int getDataExtractionRules() { + return dataExtractionRules; + } + + @Override public int getIconRes() { return iconRes; } @@ -2264,6 +2273,12 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { } @Override + public ParsingPackageImpl setDataExtractionRules(int value) { + dataExtractionRules = value; + return this; + } + + @Override public ParsingPackageImpl setIconRes(int value) { iconRes = value; return this; diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java index ff4cebdd1533..f7f3e19efdf3 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageRead.java +++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java @@ -587,6 +587,11 @@ public interface ParsingPackageRead extends Parcelable { */ int getFullBackupContent(); + /** + * @see R.styleable#AndroidManifestApplication_dataExtractionRules + */ + int getDataExtractionRules(); + /** @see ApplicationInfo#PRIVATE_FLAG_HAS_DOMAIN_URLS */ boolean isHasDomainUrls(); diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index b7aa30f00691..0c033fddf069 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -2168,6 +2168,8 @@ public class ParsingPackageUtils { .setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa)) .setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa)) .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa)) + .setDataExtractionRules( + resId(R.styleable.AndroidManifestApplication_dataExtractionRules, sa)) // Strings .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa)) .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa)) diff --git a/core/java/android/content/pm/verify/domain/DomainOwner.aidl b/core/java/android/content/pm/verify/domain/DomainOwner.aidl new file mode 100644 index 000000000000..41366d1a29b2 --- /dev/null +++ b/core/java/android/content/pm/verify/domain/DomainOwner.aidl @@ -0,0 +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 android.content.pm.verify.domain; + +parcelable DomainOwner; diff --git a/core/java/android/content/pm/verify/domain/DomainOwner.java b/core/java/android/content/pm/verify/domain/DomainOwner.java new file mode 100644 index 000000000000..b050f5da7928 --- /dev/null +++ b/core/java/android/content/pm/verify/domain/DomainOwner.java @@ -0,0 +1,219 @@ +/* + * 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.content.pm.verify.domain; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.util.Set; +import java.util.UUID; + +/** + * @hide + */ +@SystemApi +@DataClass(genParcelable = true, genEqualsHashCode = true, genAidl = true, genToString = true) +public final class DomainOwner implements Parcelable { + + /** + * Package name of that owns the domain. + */ + @NonNull + private final String mPackageName; + + /** + * Whether or not this owner can be automatically overridden. + * + * @see DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean) + */ + private final boolean mOverrideable; + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainOwner.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new DomainOwner. + * + * @param packageName + * Package name of that owns the domain. + * @param overrideable + * Whether or not this owner can be automatically overridden. If all owners for a domain are + * overrideable, then calling + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, + * Set, boolean)} to enable the domain will disable all other owners. On the other hand, if any + * of the owners are non-overrideable, then + * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, + * boolean)} must be called with false to disable all of the other owners before this domain can + * be taken by a new owner through + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, + * Set, boolean)}. + */ + @DataClass.Generated.Member + public DomainOwner( + @NonNull String packageName, + boolean overrideable) { + this.mPackageName = packageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageName); + this.mOverrideable = overrideable; + + // onConstructed(); // You can define this method to get a callback + } + + /** + * Package name of that owns the domain. + */ + @DataClass.Generated.Member + public @NonNull String getPackageName() { + return mPackageName; + } + + /** + * Whether or not this owner can be automatically overridden. If all owners for a domain are + * overrideable, then calling + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, + * Set, boolean)} to enable the domain will disable all other owners. On the other hand, if any + * of the owners are non-overrideable, then + * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, + * boolean)} must be called with false to disable all of the other owners before this domain can + * be taken by a new owner through + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, + * Set, boolean)}. + */ + @DataClass.Generated.Member + public boolean isOverrideable() { + return mOverrideable; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "DomainOwner { " + + "packageName = " + mPackageName + ", " + + "overrideable = " + mOverrideable + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(DomainOwner other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + DomainOwner that = (DomainOwner) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mPackageName, that.mPackageName) + && mOverrideable == that.mOverrideable; + } + + @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 + java.util.Objects.hashCode(mPackageName); + _hash = 31 * _hash + Boolean.hashCode(mOverrideable); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mOverrideable) flg |= 0x2; + dest.writeByte(flg); + dest.writeString(mPackageName); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ DomainOwner(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + boolean overrideable = (flg & 0x2) != 0; + String packageName = in.readString(); + + this.mPackageName = packageName; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mPackageName); + this.mOverrideable = overrideable; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<DomainOwner> CREATOR + = new Parcelable.Creator<DomainOwner>() { + @Override + public DomainOwner[] newArray(int size) { + return new DomainOwner[size]; + } + + @Override + public DomainOwner createFromParcel(@NonNull android.os.Parcel in) { + return new DomainOwner(in); + } + }; + + @DataClass.Generated( + time = 1614119379978L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainOwner.java", + inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final boolean mOverrideable\nclass DomainOwner extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genEqualsHashCode=true, genAidl=true, genToString=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/verify/domain/DomainSet.aidl b/core/java/android/content/pm/verify/domain/DomainSet.aidl new file mode 100644 index 000000000000..fab131dfa317 --- /dev/null +++ b/core/java/android/content/pm/verify/domain/DomainSet.aidl @@ -0,0 +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 android.content.pm.verify.domain; + +parcelable DomainSet; diff --git a/core/java/android/content/pm/verify/domain/DomainSet.java b/core/java/android/content/pm/verify/domain/DomainSet.java new file mode 100644 index 000000000000..243ff0820e24 --- /dev/null +++ b/core/java/android/content/pm/verify/domain/DomainSet.java @@ -0,0 +1,160 @@ +/* + * 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.content.pm.verify.domain; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.util.Set; + +/** + * Wraps an input set of domains from the client process, to be sent to the server. Handles cases + * where the data size is too large by writing data using {@link Parcel#writeBlob(byte[])}. + * + * @hide + */ +@DataClass(genParcelable = true, genAidl = true, genEqualsHashCode = true) +public class DomainSet implements Parcelable { + + @NonNull + private final Set<String> mDomains; + + private void parcelDomains(@NonNull Parcel dest, @SuppressWarnings("unused") int flags) { + DomainVerificationUtils.writeHostSet(dest, mDomains); + } + + private Set<String> unparcelDomains(@NonNull Parcel in) { + return DomainVerificationUtils.readHostSet(in); + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain + // /DomainSet.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + public DomainSet( + @NonNull Set<String> domains) { + this.mDomains = domains; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mDomains); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public @NonNull Set<String> getDomains() { + return mDomains; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@android.annotation.Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(DomainSet other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + DomainSet that = (DomainSet) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mDomains, that.mDomains); + } + + @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 + java.util.Objects.hashCode(mDomains); + 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) { ... } + + parcelDomains(dest, flags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected DomainSet(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + Set<String> domains = unparcelDomains(in); + + this.mDomains = domains; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mDomains); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<DomainSet> CREATOR + = new Parcelable.Creator<DomainSet>() { + @Override + public DomainSet[] newArray(int size) { + return new DomainSet[size]; + } + + @Override + public DomainSet createFromParcel(@NonNull Parcel in) { + return new DomainSet(in); + } + }; + + @DataClass.Generated( + time = 1613169242020L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainSet.java", + inputSignatures = "private final @android.annotation.NonNull java.util.Set<java.lang.String> mDomains\nprivate void parcelDomains(android.os.Parcel,int)\nprivate java.util.Set<java.lang.String> unparcelDomains(android.os.Parcel)\nclass DomainSet extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java index 7afbe1fcb69f..809587524f58 100644 --- a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java @@ -19,7 +19,9 @@ package android.content.pm.verify.domain; import android.annotation.NonNull; import android.annotation.SystemApi; import android.content.pm.PackageManager; +import android.os.Parcel; import android.os.Parcelable; +import android.util.ArrayMap; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; @@ -34,12 +36,12 @@ import java.util.UUID; * against the digital asset links response from the server hosting that domain. * <p> * These values for each domain can be modified through - * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}. + * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, + * Set, int)}. * * @hide */ @SystemApi -@SuppressWarnings("DefaultAnnotationParam") @DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true, genEqualsHashCode = true) public final class DomainVerificationInfo implements Parcelable { @@ -71,22 +73,30 @@ public final class DomainVerificationInfo implements Parcelable { private final String mPackageName; /** - * Map of host names to their current state. State is an integer, which defaults to - * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the - * domain verification agent (the intended consumer of this API), which can be equal - * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or - * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for - * any unsuccessful response. + * Map of host names to their current state. State is an integer, which defaults to {@link + * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain + * verification agent (the intended consumer of this API), which can be equal to {@link + * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link + * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response. * <p> - * Any value non-inclusive between those 2 values are reserved for use by the system. - * The domain verification agent may be able to act on these reserved values, and this - * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}. - * It is expected that the agent attempt to verify all domains that it can modify the - * state of, even if it does not understand the meaning of those values. + * Any value non-inclusive between those 2 values are reserved for use by the system. The domain + * verification agent may be able to act on these reserved values, and this ability can be + * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that + * the agent attempt to verify all domains that it can modify the state of, even if it does not + * understand the meaning of those values. */ @NonNull private final Map<String, Integer> mHostToStateMap; + private void parcelHostToStateMap(Parcel dest, @SuppressWarnings("unused") int flags) { + DomainVerificationUtils.writeHostMap(dest, mHostToStateMap); + } + + private Map<String, Integer> unparcelHostToStateMap(Parcel in) { + return DomainVerificationUtils.readHostMap(in, new ArrayMap<>(), + DomainVerificationUserSelection.class.getClassLoader()); + } + // Code below generated by codegen v1.0.22. @@ -95,7 +105,8 @@ public final class DomainVerificationInfo implements Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain + // /DomainVerificationInfo.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -123,18 +134,17 @@ public final class DomainVerificationInfo implements Parcelable { * @param packageName * The package name that this data corresponds to. * @param hostToStateMap - * Map of host names to their current state. State is an integer, which defaults to - * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the - * domain verification agent (the intended consumer of this API), which can be equal - * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or - * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for - * any unsuccessful response. + * Map of host names to their current state. State is an integer, which defaults to {@link + * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain + * verification agent (the intended consumer of this API), which can be equal to {@link + * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link + * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response. * <p> - * Any value non-inclusive between those 2 values are reserved for use by the system. - * The domain verification agent may be able to act on these reserved values, and this - * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}. - * It is expected that the agent attempt to verify all domains that it can modify the - * state of, even if it does not understand the meaning of those values. + * Any value non-inclusive between those 2 values are reserved for use by the system. The domain + * verification agent may be able to act on these reserved values, and this ability can be + * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that + * the agent attempt to verify all domains that it can modify the state of, even if it does not + * understand the meaning of those values. * @hide */ @DataClass.Generated.Member @@ -185,18 +195,17 @@ public final class DomainVerificationInfo implements Parcelable { } /** - * Map of host names to their current state. State is an integer, which defaults to - * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the - * domain verification agent (the intended consumer of this API), which can be equal - * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or - * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for - * any unsuccessful response. + * Map of host names to their current state. State is an integer, which defaults to {@link + * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain + * verification agent (the intended consumer of this API), which can be equal to {@link + * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link + * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response. * <p> - * Any value non-inclusive between those 2 values are reserved for use by the system. - * The domain verification agent may be able to act on these reserved values, and this - * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}. - * It is expected that the agent attempt to verify all domains that it can modify the - * state of, even if it does not understand the meaning of those values. + * Any value non-inclusive between those 2 values are reserved for use by the system. The domain + * verification agent may be able to act on these reserved values, and this ability can be + * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that + * the agent attempt to verify all domains that it can modify the state of, even if it does not + * understand the meaning of those values. */ @DataClass.Generated.Member public @NonNull Map<String,Integer> getHostToStateMap() { @@ -260,13 +269,13 @@ public final class DomainVerificationInfo implements Parcelable { @Override @DataClass.Generated.Member - public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } sParcellingForIdentifier.parcel(mIdentifier, dest, flags); dest.writeString(mPackageName); - dest.writeMap(mHostToStateMap); + parcelHostToStateMap(dest, flags); } @Override @@ -276,14 +285,13 @@ public final class DomainVerificationInfo implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - /* package-private */ DomainVerificationInfo(@NonNull android.os.Parcel in) { + /* package-private */ DomainVerificationInfo(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } UUID identifier = sParcellingForIdentifier.unparcel(in); String packageName = in.readString(); - Map<String,Integer> hostToStateMap = new java.util.LinkedHashMap<>(); - in.readMap(hostToStateMap, Integer.class.getClassLoader()); + Map<String,Integer> hostToStateMap = unparcelHostToStateMap(in); this.mIdentifier = identifier; com.android.internal.util.AnnotationValidations.validate( @@ -307,16 +315,16 @@ public final class DomainVerificationInfo implements Parcelable { } @Override - public DomainVerificationInfo createFromParcel(@NonNull android.os.Parcel in) { + public DomainVerificationInfo createFromParcel(@NonNull Parcel in) { return new DomainVerificationInfo(in); } }; @DataClass.Generated( - time = 1611862790369L, + time = 1613002530369L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java", - inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)") + inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate void parcelHostToStateMap(android.os.Parcel,int)\nprivate java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java index cbb3baaa6700..11402afac8b6 100644 --- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java @@ -239,7 +239,15 @@ public interface DomainVerificationManager { * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used. * * Enabling an unverified domain will allow an application to open it, but this can only occur - * if no other app on the device is approved for the domain. + * if no other app on the device is approved for a higher approval level. This can queried + * using {@link #getOwnersForDomain(String)}. + * + * If all owners for a domain are {@link DomainOwner#isOverrideable()}, then calling this to + * enable that domain will disable all other owners. + * + * On the other hand, if any of the owners are non-overrideable, then this must be called with + * false for all of the other owners to disable them before the domain can be taken by a new + * owner. * * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}. * @param domains The domains to toggle the state of. @@ -276,6 +284,19 @@ public interface DomainVerificationManager { throws NameNotFoundException; /** + * For the given domain, return all apps which are approved to open it in a + * greater than 0 priority. This does not mean that all apps can actually open + * an Intent with that domain. That will be decided by the set of apps which + * are the highest priority level, ignoring all lower priority levels. + * + * By default the list will be returned ordered from lowest to highest + * priority. + */ + @NonNull + @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) + List<DomainOwner> getOwnersForDomain(@NonNull String domain); + + /** * Thrown if a {@link DomainVerificationInfo#getIdentifier()}} or an associated set of domains * provided by the caller is no longer valid. This may be recoverable, and the caller should * re-query the package name associated with the ID using diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java b/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java index 5938def5c83c..8b9865c2b436 100644 --- a/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java @@ -21,11 +21,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.verify.domain.IDomainVerificationManager; import android.os.RemoteException; import android.os.ServiceSpecificException; -import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; @@ -89,7 +87,7 @@ public class DomainVerificationManagerImpl implements DomainVerificationManager int state) throws IllegalArgumentException, NameNotFoundException { try { mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(), - new ArrayList<>(domains), state); + new DomainSet(domains), state); } catch (Exception e) { Exception converted = rethrow(e, domainSetId); if (converted instanceof NameNotFoundException) { @@ -126,7 +124,7 @@ public class DomainVerificationManagerImpl implements DomainVerificationManager throws IllegalArgumentException, NameNotFoundException { try { mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(), - new ArrayList<>(domains), enabled, mContext.getUserId()); + new DomainSet(domains), enabled, mContext.getUserId()); } catch (Exception e) { Exception converted = rethrow(e, domainSetId); if (converted instanceof NameNotFoundException) { @@ -158,6 +156,16 @@ public class DomainVerificationManagerImpl implements DomainVerificationManager } } + @NonNull + @Override + public List<DomainOwner> getOwnersForDomain(@NonNull String domain) { + try { + return mDomainVerificationManager.getOwnersForDomain(domain, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private Exception rethrow(Exception exception, @Nullable UUID domainSetId) { return rethrow(exception, domainSetId, null); } diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java b/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java index 473abce26d81..65f6d7c18135 100644 --- a/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java @@ -19,6 +19,7 @@ package android.content.pm.verify.domain; import android.annotation.NonNull; import android.annotation.SystemApi; import android.content.Intent; +import android.os.Parcel; import android.os.Parcelable; import com.android.internal.util.DataClass; @@ -27,11 +28,11 @@ import com.android.internal.util.Parcelling; import java.util.Set; /** - * Request object sent in the {@link Intent} that's broadcast to the domain verification - * agent, retrieved through {@link DomainVerificationManager#EXTRA_VERIFICATION_REQUEST}. + * Request object sent in the {@link Intent} that's broadcast to the domain verification agent, + * retrieved through {@link DomainVerificationManager#EXTRA_VERIFICATION_REQUEST}. * <p> - * This contains the set of packages which have been invalidated and will require - * re-verification. The exact domains can be retrieved with + * This contains the set of packages which have been invalidated and will require re-verification. + * The exact domains can be retrieved with * {@link DomainVerificationManager#getDomainVerificationInfo(String)} * * @hide @@ -42,14 +43,22 @@ import java.util.Set; public final class DomainVerificationRequest implements Parcelable { /** - * The package names of the apps that need to be verified. The receiver should call - * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of - * these values to get the actual set of domains that need to be acted on. + * The package names of the apps that need to be verified. The receiver should call {@link + * DomainVerificationManager#getDomainVerificationInfo(String)} with each of these values to get + * the actual set of domains that need to be acted on. */ @NonNull @DataClass.ParcelWith(Parcelling.BuiltIn.ForStringSet.class) private final Set<String> mPackageNames; + private void parcelPackageNames(@NonNull Parcel dest, @SuppressWarnings("unused") int flags) { + DomainVerificationUtils.writeHostSet(dest, mPackageNames); + } + + private Set<String> unparcelPackageNames(@NonNull Parcel in) { + return DomainVerificationUtils.readHostSet(in); + } + // Code below generated by codegen v1.0.22. @@ -58,7 +67,8 @@ public final class DomainVerificationRequest implements Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain + // /DomainVerificationRequest.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -69,9 +79,9 @@ public final class DomainVerificationRequest implements Parcelable { * Creates a new DomainVerificationRequest. * * @param packageNames - * The package names of the apps that need to be verified. The receiver should call - * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of - * these values to get the actual set of domains that need to be acted on. + * The package names of the apps that need to be verified. The receiver should call {@link + * DomainVerificationManager#getDomainVerificationInfo(String)} with each of these values to get + * the actual set of domains that need to be acted on. * @hide */ @DataClass.Generated.Member @@ -85,9 +95,9 @@ public final class DomainVerificationRequest implements Parcelable { } /** - * The package names of the apps that need to be verified. The receiver should call - * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of - * these values to get the actual set of domains that need to be acted on. + * The package names of the apps that need to be verified. The receiver should call {@link + * DomainVerificationManager#getDomainVerificationInfo(String)} with each of these values to get + * the actual set of domains that need to be acted on. */ @DataClass.Generated.Member public @NonNull Set<String> getPackageNames() { @@ -134,11 +144,11 @@ public final class DomainVerificationRequest implements Parcelable { @Override @DataClass.Generated.Member - public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } - sParcellingForPackageNames.parcel(mPackageNames, dest, flags); + parcelPackageNames(dest, flags); } @Override @@ -148,11 +158,11 @@ public final class DomainVerificationRequest implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - /* package-private */ DomainVerificationRequest(@NonNull android.os.Parcel in) { + /* package-private */ DomainVerificationRequest(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } - Set<String> packageNames = sParcellingForPackageNames.unparcel(in); + Set<String> packageNames = unparcelPackageNames(in); this.mPackageNames = packageNames; com.android.internal.util.AnnotationValidations.validate( @@ -170,16 +180,16 @@ public final class DomainVerificationRequest implements Parcelable { } @Override - public DomainVerificationRequest createFromParcel(@NonNull android.os.Parcel in) { + public DomainVerificationRequest createFromParcel(@NonNull Parcel in) { return new DomainVerificationRequest(in); } }; @DataClass.Generated( - time = 1611862814990L, + time = 1613169505495L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java", - inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForStringSet.class) java.util.Set<java.lang.String> mPackageNames\nclass DomainVerificationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genAidl=false, genEqualsHashCode=true)") + inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForStringSet.class) java.util.Set<java.lang.String> mPackageNames\nprivate void parcelPackageNames(android.os.Parcel,int)\nprivate java.util.Set<java.lang.String> unparcelPackageNames(android.os.Parcel)\nclass DomainVerificationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genAidl=false, genEqualsHashCode=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java index 73346ef0273b..d23f5f133841 100644 --- a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java @@ -20,8 +20,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Context; +import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; +import android.util.ArrayMap; import com.android.internal.util.DataClass; import com.android.internal.util.Parcelling; @@ -40,29 +42,46 @@ import java.util.UUID; * toggle affects <b>all</b> links and is not based on the verification state of the domains. * <p> * Assuming the toggle is enabled, the user can also select additional unverified domains to grant - * to the application to open, which is reflected in {@link #getHostToUserSelectionMap()}. But only - * a single application can be approved for a domain unless the applications are both approved. If - * another application is approved, the user will not be allowed to enable the domain. + * to the application to open, which is reflected in {@link #getHostToStateMap()}. But only a single + * application can be approved for a domain unless the applications are both approved. If another + * application is approved, the user will not be allowed to enable the domain. * <p> * These values can be changed through the * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, - * boolean)} and - * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, + * boolean)} and {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, * boolean)} APIs. * <p> - * Note that because state is per user, if a different user needs to be changed, one will - * need to use {@link Context#createContextAsUser(UserHandle, int)} and hold the - * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission. + * Note that because state is per user, if a different user needs to be changed, one will need to + * use {@link Context#createContextAsUser(UserHandle, int)} and hold the {@link + * android.Manifest.permission#INTERACT_ACROSS_USERS} permission. * * @hide */ @SystemApi @SuppressWarnings("DefaultAnnotationParam") @DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true, - genEqualsHashCode = true) + genEqualsHashCode = true, genHiddenConstDefs = true) public final class DomainVerificationUserSelection implements Parcelable { /** + * The domain is unverified and unselected, and the application is unable to open web links + * that resolve to the domain. + */ + public static final int DOMAIN_STATE_NONE = 0; + + /** + * The domain has been selected through the + * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)} + * API, under the assumption it has not been reset by the system. + */ + public static final int DOMAIN_STATE_SELECTED = 1; + + /** + * The domain has been previously verified by the domain verification agent. + */ + public static final int DOMAIN_STATE_VERIFIED = 2; + + /** * @see DomainVerificationInfo#getIdentifier */ @NonNull @@ -88,15 +107,20 @@ public final class DomainVerificationUserSelection implements Parcelable { private final boolean mLinkHandlingAllowed; /** - * Retrieve the existing user selection state for the matching - * {@link #getPackageName()}, as was previously set by - * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, - * boolean)}. - * - * @return Map of hosts to enabled state for the given package and user. + * Mapping of domain host to state, as defined by {@link DomainState}. */ @NonNull - private final Map<String, Boolean> mHostToUserSelectionMap; + private final Map<String, Integer> mHostToStateMap; + + private void parcelHostToStateMap(Parcel dest, @SuppressWarnings("unused") int flags) { + DomainVerificationUtils.writeHostMap(dest, mHostToStateMap); + } + + @NonNull + private Map<String, Integer> unparcelHostToStateMap(Parcel in) { + return DomainVerificationUtils.readHostMap(in, new ArrayMap<>(), + DomainVerificationUserSelection.class.getClassLoader()); + } @@ -106,14 +130,37 @@ public final class DomainVerificationUserSelection implements Parcelable { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain - // /DomainVerificationUserSelection.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control //@formatter:off + /** @hide */ + @android.annotation.IntDef(prefix = "DOMAIN_STATE_", value = { + DOMAIN_STATE_NONE, + DOMAIN_STATE_SELECTED, + DOMAIN_STATE_VERIFIED + }) + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface DomainState {} + + /** @hide */ + @DataClass.Generated.Member + public static String domainStateToString(@DomainState int value) { + switch (value) { + case DOMAIN_STATE_NONE: + return "DOMAIN_STATE_NONE"; + case DOMAIN_STATE_SELECTED: + return "DOMAIN_STATE_SELECTED"; + case DOMAIN_STATE_VERIFIED: + return "DOMAIN_STATE_VERIFIED"; + default: return Integer.toHexString(value); + } + } + /** * Creates a new DomainVerificationUserSelection. * @@ -123,11 +170,8 @@ public final class DomainVerificationUserSelection implements Parcelable { * The user that this data corresponds to. * @param linkHandlingAllowed * Whether or not this package is allowed to open links. - * @param hostToUserSelectionMap - * Retrieve the existing user selection state for the matching - * {@link #getPackageName()}, as was previously set by - * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, - * boolean)}. + * @param hostToStateMap + * Mapping of domain host to state, as defined by {@link DomainState}. * @hide */ @DataClass.Generated.Member @@ -136,7 +180,7 @@ public final class DomainVerificationUserSelection implements Parcelable { @NonNull String packageName, @NonNull UserHandle user, @NonNull boolean linkHandlingAllowed, - @NonNull Map<String,Boolean> hostToUserSelectionMap) { + @NonNull Map<String,Integer> hostToStateMap) { this.mIdentifier = identifier; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mIdentifier); @@ -149,9 +193,9 @@ public final class DomainVerificationUserSelection implements Parcelable { this.mLinkHandlingAllowed = linkHandlingAllowed; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mLinkHandlingAllowed); - this.mHostToUserSelectionMap = hostToUserSelectionMap; + this.mHostToStateMap = hostToStateMap; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mHostToUserSelectionMap); + NonNull.class, null, mHostToStateMap); // onConstructed(); // You can define this method to get a callback } @@ -189,16 +233,11 @@ public final class DomainVerificationUserSelection implements Parcelable { } /** - * Retrieve the existing user selection state for the matching - * {@link #getPackageName()}, as was previously set by - * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, - * boolean)}. - * - * @return Map of hosts to enabled state for the given package and user. + * Mapping of domain host to state, as defined by {@link DomainState}. */ @DataClass.Generated.Member - public @NonNull Map<String,Boolean> getHostToUserSelectionMap() { - return mHostToUserSelectionMap; + public @NonNull Map<String,Integer> getHostToStateMap() { + return mHostToStateMap; } @Override @@ -212,7 +251,7 @@ public final class DomainVerificationUserSelection implements Parcelable { "packageName = " + mPackageName + ", " + "user = " + mUser + ", " + "linkHandlingAllowed = " + mLinkHandlingAllowed + ", " + - "hostToUserSelectionMap = " + mHostToUserSelectionMap + + "hostToStateMap = " + mHostToStateMap + " }"; } @@ -233,7 +272,7 @@ public final class DomainVerificationUserSelection implements Parcelable { && java.util.Objects.equals(mPackageName, that.mPackageName) && java.util.Objects.equals(mUser, that.mUser) && mLinkHandlingAllowed == that.mLinkHandlingAllowed - && java.util.Objects.equals(mHostToUserSelectionMap, that.mHostToUserSelectionMap); + && java.util.Objects.equals(mHostToStateMap, that.mHostToStateMap); } @Override @@ -247,7 +286,7 @@ public final class DomainVerificationUserSelection implements Parcelable { _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName); _hash = 31 * _hash + java.util.Objects.hashCode(mUser); _hash = 31 * _hash + Boolean.hashCode(mLinkHandlingAllowed); - _hash = 31 * _hash + java.util.Objects.hashCode(mHostToUserSelectionMap); + _hash = 31 * _hash + java.util.Objects.hashCode(mHostToStateMap); return _hash; } @@ -264,7 +303,7 @@ public final class DomainVerificationUserSelection implements Parcelable { @Override @DataClass.Generated.Member - public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } @@ -274,7 +313,7 @@ public final class DomainVerificationUserSelection implements Parcelable { sParcellingForIdentifier.parcel(mIdentifier, dest, flags); dest.writeString(mPackageName); dest.writeTypedObject(mUser, flags); - dest.writeMap(mHostToUserSelectionMap); + parcelHostToStateMap(dest, flags); } @Override @@ -284,7 +323,7 @@ public final class DomainVerificationUserSelection implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - /* package-private */ DomainVerificationUserSelection(@NonNull android.os.Parcel in) { + /* package-private */ DomainVerificationUserSelection(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } @@ -293,8 +332,7 @@ public final class DomainVerificationUserSelection implements Parcelable { UUID identifier = sParcellingForIdentifier.unparcel(in); String packageName = in.readString(); UserHandle user = (UserHandle) in.readTypedObject(UserHandle.CREATOR); - Map<String,Boolean> hostToUserSelectionMap = new java.util.LinkedHashMap<>(); - in.readMap(hostToUserSelectionMap, Boolean.class.getClassLoader()); + Map<String,Integer> hostToStateMap = unparcelHostToStateMap(in); this.mIdentifier = identifier; com.android.internal.util.AnnotationValidations.validate( @@ -308,9 +346,9 @@ public final class DomainVerificationUserSelection implements Parcelable { this.mLinkHandlingAllowed = linkHandlingAllowed; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mLinkHandlingAllowed); - this.mHostToUserSelectionMap = hostToUserSelectionMap; + this.mHostToStateMap = hostToStateMap; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mHostToUserSelectionMap); + NonNull.class, null, mHostToStateMap); // onConstructed(); // You can define this method to get a callback } @@ -324,16 +362,16 @@ public final class DomainVerificationUserSelection implements Parcelable { } @Override - public DomainVerificationUserSelection createFromParcel(@NonNull android.os.Parcel in) { + public DomainVerificationUserSelection createFromParcel(@NonNull Parcel in) { return new DomainVerificationUserSelection(in); } }; @DataClass.Generated( - time = 1612829797220L, + time = 1613683603297L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java", - inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Boolean> mHostToUserSelectionMap\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)") + inputSignatures = "public static final int DOMAIN_STATE_NONE\npublic static final int DOMAIN_STATE_SELECTED\npublic static final int DOMAIN_STATE_VERIFIED\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate void parcelHostToStateMap(android.os.Parcel,int)\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUtils.java b/core/java/android/content/pm/verify/domain/DomainVerificationUtils.java new file mode 100644 index 000000000000..93005fae1772 --- /dev/null +++ b/core/java/android/content/pm/verify/domain/DomainVerificationUtils.java @@ -0,0 +1,181 @@ +/* + * 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.content.pm.verify.domain; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.IBinder; +import android.os.Parcel; +import android.util.ArraySet; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * @hide + */ +public class DomainVerificationUtils { + + private static final int STRINGS_TARGET_BYTE_SIZE = IBinder.getSuggestedMaxIpcSizeBytes() / 2; + + /** + * Write a map containing web hosts to the given parcel, using {@link Parcel#writeBlob(byte[])} + * if the limit exceeds {@link IBinder#getSuggestedMaxIpcSizeBytes()} / 2. This assumes that the + * written map is the only data structure in the caller that varies based on the host data set. + * Other data that will be written to the parcel after this method will not be considered in the + * calculation. + */ + public static void writeHostMap(@NonNull Parcel dest, @NonNull Map<String, ?> map) { + boolean targetSizeExceeded = false; + int totalSize = dest.dataSize(); + for (String host : map.keySet()) { + totalSize += estimatedByteSizeOf(host); + if (totalSize > STRINGS_TARGET_BYTE_SIZE) { + targetSizeExceeded = true; + break; + } + } + + dest.writeBoolean(targetSizeExceeded); + + if (!targetSizeExceeded) { + dest.writeMap(map); + return; + } + + Parcel data = Parcel.obtain(); + try { + data.writeMap(map); + dest.writeBlob(data.marshall()); + } finally { + data.recycle(); + } + } + + /** + * Retrieve a map previously written by {@link #writeHostMap(Parcel, Map)}. + */ + @NonNull + @SuppressWarnings("rawtypes") + public static <T extends Map> T readHostMap(@NonNull Parcel in, @NonNull T map, + @NonNull ClassLoader classLoader) { + boolean targetSizeExceeded = in.readBoolean(); + + if (!targetSizeExceeded) { + in.readMap(map, classLoader); + return map; + } + + Parcel data = Parcel.obtain(); + try { + byte[] blob = in.readBlob(); + data.unmarshall(blob, 0, blob.length); + data.setDataPosition(0); + data.readMap(map, classLoader); + } finally { + data.recycle(); + } + + return map; + } + + /** + * {@link ArraySet} variant of {@link #writeHostMap(Parcel, Map)}. + */ + public static void writeHostSet(@NonNull Parcel dest, @NonNull Set<String> set) { + boolean targetSizeExceeded = false; + int totalSize = dest.dataSize(); + for (String host : set) { + totalSize += estimatedByteSizeOf(host); + if (totalSize > STRINGS_TARGET_BYTE_SIZE) { + targetSizeExceeded = true; + break; + } + } + + dest.writeBoolean(targetSizeExceeded); + + if (!targetSizeExceeded) { + writeSet(dest, set); + return; + } + + Parcel data = Parcel.obtain(); + try { + writeSet(data, set); + dest.writeBlob(data.marshall()); + } finally { + data.recycle(); + } + } + + /** + * {@link ArraySet} variant of {@link #readHostMap(Parcel, Map, ClassLoader)}. + */ + @NonNull + public static Set<String> readHostSet(@NonNull Parcel in) { + boolean targetSizeExceeded = in.readBoolean(); + + if (!targetSizeExceeded) { + return readSet(in); + } + + Parcel data = Parcel.obtain(); + try { + byte[] blob = in.readBlob(); + data.unmarshall(blob, 0, blob.length); + data.setDataPosition(0); + return readSet(data); + } finally { + data.recycle(); + } + } + + private static void writeSet(@NonNull Parcel dest, @Nullable Set<String> set) { + if (set == null) { + dest.writeInt(-1); + return; + } + dest.writeInt(set.size()); + for (String string : set) { + dest.writeString(string); + } + } + + @NonNull + private static Set<String> readSet(@NonNull Parcel in) { + int size = in.readInt(); + if (size == -1) { + return Collections.emptySet(); + } + + ArraySet<String> set = new ArraySet<>(size); + for (int count = 0; count < size; count++) { + set.add(in.readString()); + } + return set; + } + + /** + * Ballpark the size of domains to avoid unnecessary allocation of ashmem when sending domains + * across the client-server API. + */ + public static int estimatedByteSizeOf(String string) { + return string.length() * 2 + 12; + } +} diff --git a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl index 21dd623b46bc..701af320fb01 100644 --- a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl +++ b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl @@ -16,6 +16,8 @@ package android.content.pm.verify.domain; +import android.content.pm.verify.domain.DomainOwner; +import android.content.pm.verify.domain.DomainSet; import android.content.pm.verify.domain.DomainVerificationInfo; import android.content.pm.verify.domain.DomainVerificationUserSelection; import java.util.List; @@ -35,10 +37,13 @@ interface IDomainVerificationManager { DomainVerificationUserSelection getDomainVerificationUserSelection(String packageName, int userId); - void setDomainVerificationStatus(String domainSetId, in List<String> domains, int state); + @nullable + List<DomainOwner> getOwnersForDomain(String domain, int userId); + + void setDomainVerificationStatus(String domainSetId, in DomainSet domains, int state); void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed, int userId); - void setDomainVerificationUserSelection(String domainSetId, in List<String> domains, + void setDomainVerificationUserSelection(String domainSetId, in DomainSet domains, boolean enabled, int userId); } diff --git a/core/java/android/content/pm/verify/domain/OWNERS b/core/java/android/content/pm/verify/domain/OWNERS new file mode 100644 index 000000000000..c669112e0512 --- /dev/null +++ b/core/java/android/content/pm/verify/domain/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 36137 + +chiuwinson@google.com +patb@google.com +toddke@google.com
\ No newline at end of file diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 2f7aeb80986b..b66f048b829d 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -1956,7 +1956,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(mnc); fixUpLocaleList(); - dest.writeParcelable(mLocaleList, flags); + dest.writeTypedObject(mLocaleList, flags); if(userSetLocale) { dest.writeInt(1); @@ -1980,7 +1980,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(compatScreenWidthDp); dest.writeInt(compatScreenHeightDp); dest.writeInt(compatSmallestScreenWidthDp); - dest.writeValue(windowConfiguration); + windowConfiguration.writeToParcel(dest, flags); dest.writeInt(assetsSeq); dest.writeInt(seq); dest.writeInt(fontWeightAdjustment); @@ -1991,7 +1991,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration mcc = source.readInt(); mnc = source.readInt(); - mLocaleList = source.readParcelable(LocaleList.class.getClassLoader()); + mLocaleList = source.readTypedObject(LocaleList.CREATOR); locale = mLocaleList.get(0); userSetLocale = (source.readInt()==1); @@ -2012,7 +2012,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration compatScreenWidthDp = source.readInt(); compatScreenHeightDp = source.readInt(); compatSmallestScreenWidthDp = source.readInt(); - windowConfiguration.setTo((WindowConfiguration) source.readValue(null)); + windowConfiguration.readFromParcel(source); assetsSeq = source.readInt(); seq = source.readInt(); fontWeightAdjustment = source.readInt(); diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java index e512cf1bbb1f..429eef95f952 100644 --- a/core/java/android/graphics/fonts/FontManager.java +++ b/core/java/android/graphics/fonts/FontManager.java @@ -20,7 +20,6 @@ import android.Manifest; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -29,7 +28,6 @@ import android.content.Context; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.text.FontConfig; -import android.util.Log; import com.android.internal.graphics.fonts.IFontManager; @@ -198,12 +196,11 @@ public class FontManager { * @return The current font configuration. null if failed to fetch information from the system * service. */ - public @Nullable FontConfig getFontConfig() { + public @NonNull FontConfig getFontConfig() { try { return mIFontManager.getFontConfig(); } catch (RemoteException e) { - Log.e(TAG, "Failed to call getFontConfig", e); - return null; + throw e.rethrowAsRuntimeException(); } } diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS index 3295042b0b35..2b4e4a106cec 100644 --- a/core/java/android/hardware/OWNERS +++ b/core/java/android/hardware/OWNERS @@ -3,3 +3,6 @@ per-file *Camera*=cychen@google.com,epeev@google.com,etalvala@google.com,shuzhen # Sensor Privacy per-file *SensorPrivacy* = file:platform/frameworks/native:/libs/sensorprivacy/OWNERS + +# Sensors framework +per-file *Sensor*,*Trigger* = file:platform/frameworks/native:/services/sensorservice/OWNERS diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java index f4f9e1775d1a..e03c1f48773a 100644 --- a/core/java/android/hardware/SensorPrivacyManager.java +++ b/core/java/android/hardware/SensorPrivacyManager.java @@ -19,6 +19,7 @@ package android.hardware; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; @@ -33,6 +34,7 @@ import com.android.internal.annotations.GuardedBy; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; /** * This class provides access to the sensor privacy services; sensor privacy allows the @@ -42,9 +44,21 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@SystemApi @TestApi @SystemService(Context.SENSOR_PRIVACY_SERVICE) public final class SensorPrivacyManager { + + /** + * @hide + */ + public static final boolean USE_MICROPHONE_TOGGLE = true; + + /** + * @hide + */ + public static final boolean USE_CAMERA_TOGGLE = true; + /** * Unique Id of this manager to identify to the service * @hide @@ -58,28 +72,39 @@ public final class SensorPrivacyManager { public static final String EXTRA_SENSOR = SensorPrivacyManager.class.getName() + ".extra.sensor"; - /** Microphone - * @hide */ - @TestApi - public static final int INDIVIDUAL_SENSOR_MICROPHONE = - SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; - - /** Camera - * @hide */ - @TestApi - public static final int INDIVIDUAL_SENSOR_CAMERA = - SensorPrivacyIndividualEnabledSensorProto.CAMERA; - /** - * Individual sensors not listed in {@link Sensor} + * Individual sensors not listed in {@link Sensors} * @hide */ - @IntDef(prefix = "INDIVIDUAL_SENSOR_", value = { - INDIVIDUAL_SENSOR_MICROPHONE, - INDIVIDUAL_SENSOR_CAMERA - }) - @Retention(RetentionPolicy.SOURCE) - public @interface IndividualSensor {} + @SystemApi + @TestApi + public static class Sensors { + + private Sensors() {} + + /** Microphone + * @hide */ + @SystemApi + @TestApi + public static final int MICROPHONE = SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; + /** Camera + * @hide */ + @SystemApi + @TestApi + public static final int CAMERA = SensorPrivacyIndividualEnabledSensorProto.CAMERA; + + /** + * Individual sensors not listed in {@link Sensors} + * + * @hide + */ + @IntDef(value = { + MICROPHONE, + CAMERA + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Sensor {} + } /** * A class implementing this interface can register with the {@link @@ -88,6 +113,8 @@ public final class SensorPrivacyManager { * * @hide */ + @SystemApi + @TestApi public interface OnSensorPrivacyChangedListener { /** * Callback invoked when the sensor privacy state changes. @@ -165,7 +192,8 @@ public final class SensorPrivacyManager { * * @hide */ - public void addSensorPrivacyListener(final OnSensorPrivacyChangedListener listener) { + @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public void addSensorPrivacyListener(@NonNull final OnSensorPrivacyChangedListener listener) { synchronized (mListeners) { ISensorPrivacyListener iListener = mListeners.get(listener); if (iListener == null) { @@ -196,15 +224,37 @@ public final class SensorPrivacyManager { * * @hide */ - public void addSensorPrivacyListener(@IndividualSensor int sensor, - final OnSensorPrivacyChangedListener listener) { + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public void addSensorPrivacyListener(@Sensors.Sensor int sensor, + @NonNull OnSensorPrivacyChangedListener listener) { + addSensorPrivacyListener(sensor, mContext.getMainExecutor(), listener); + } + + /** + * Registers a new listener to receive notification when the state of sensor privacy + * changes. + * + * @param sensor the sensor to listen to changes to + * @param executor the executor to dispatch the callback on + * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor + * privacy changes. + * + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor, + @NonNull OnSensorPrivacyChangedListener listener) { synchronized (mListeners) { ISensorPrivacyListener iListener = mListeners.get(listener); if (iListener == null) { iListener = new ISensorPrivacyListener.Stub() { @Override public void onSensorPrivacyChanged(boolean enabled) { - listener.onSensorPrivacyChanged(enabled); + executor.execute(() -> listener.onSensorPrivacyChanged(enabled)); } }; mListeners.put(listener, iListener); @@ -228,7 +278,10 @@ public final class SensorPrivacyManager { * * @hide */ - public void removeSensorPrivacyListener(OnSensorPrivacyChangedListener listener) { + @SystemApi + @TestApi + @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public void removeSensorPrivacyListener(@NonNull OnSensorPrivacyChangedListener listener) { synchronized (mListeners) { ISensorPrivacyListener iListener = mListeners.get(listener); if (iListener != null) { @@ -249,6 +302,7 @@ public final class SensorPrivacyManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled() { try { return mService.isSensorPrivacyEnabled(); @@ -264,8 +318,10 @@ public final class SensorPrivacyManager { * * @hide */ + @SystemApi @TestApi - public boolean isIndividualSensorPrivacyEnabled(@IndividualSensor int sensor) { + @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) + public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) { try { return mService.isIndividualSensorPrivacyEnabled(mContext.getUserId(), sensor); } catch (RemoteException e) { @@ -283,8 +339,7 @@ public final class SensorPrivacyManager { */ @TestApi @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) - public void setIndividualSensorPrivacy(@IndividualSensor int sensor, - boolean enable) { + public void setSensorPrivacy(@Sensors.Sensor int sensor, boolean enable) { try { mService.setIndividualSensorPrivacy(mContext.getUserId(), sensor, enable); } catch (RemoteException e) { @@ -303,7 +358,7 @@ public final class SensorPrivacyManager { */ @TestApi @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) - public void setIndividualSensorPrivacyForProfileGroup(@IndividualSensor int sensor, + public void setSensorPrivacyForProfileGroup(@Sensors.Sensor int sensor, boolean enable) { try { mService.setIndividualSensorPrivacyForProfileGroup(mContext.getUserId(), sensor, @@ -321,7 +376,8 @@ public final class SensorPrivacyManager { * * @hide */ - public void suppressIndividualSensorPrivacyReminders(@NonNull String packageName, + @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) + public void suppressSensorPrivacyReminders(@NonNull String packageName, boolean suppress) { try { mService.suppressIndividualSensorPrivacyReminders(mContext.getUserId(), packageName, diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 1ffd18fc1ac8..788afe3bdb8e 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -667,7 +667,7 @@ public class SystemSensorManager extends SensorManager { private abstract static class BaseEventQueue { private static native long nativeInitBaseEventQueue(long nativeManager, WeakReference<BaseEventQueue> eventQWeak, MessageQueue msgQ, - String packageName, int mode, String opPackageName); + String packageName, int mode, String opPackageName, String attributionTag); private static native int nativeEnableSensor(long eventQ, int handle, int rateUs, int maxBatchReportLatencyUs); private static native int nativeDisableSensor(long eventQ, int handle); @@ -689,7 +689,8 @@ public class SystemSensorManager extends SensorManager { if (packageName == null) packageName = ""; mNativeSensorEventQueue = nativeInitBaseEventQueue(manager.mNativeInstance, new WeakReference<>(this), looper.getQueue(), - packageName, mode, manager.mContext.getOpPackageName()); + packageName, mode, manager.mContext.getOpPackageName(), + manager.mContext.getAttributionTag()); mCloseGuard.open("dispose"); mManager = manager; } diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java index 76d50bdf414c..43ef33e1f420 100644 --- a/core/java/android/hardware/biometrics/BiometricConstants.java +++ b/core/java/android/hardware/biometrics/BiometricConstants.java @@ -145,6 +145,12 @@ public interface BiometricConstants { int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; /** + * Authentication cannot proceed because re-enrollment is required. + * @hide + */ + int BIOMETRIC_ERROR_RE_ENROLL = 16; + + /** * This constant is only used by SystemUI. It notifies SystemUI that authentication was paused * because the authentication attempt was unsuccessful. * @hide diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java index eafcf529de62..4385b1dac7a0 100644 --- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java @@ -153,6 +153,12 @@ public interface BiometricFaceConstants { int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; /** + * Authentication cannot proceed because re-enrollment is required. + * @hide + */ + int BIOMETRIC_ERROR_RE_ENROLL = 16; + + /** * @hide */ int FACE_ERROR_VENDOR_BASE = 1000; diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java index 01f0e71a7c33..30e24d2ec8db 100644 --- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -166,6 +166,12 @@ public interface BiometricFingerprintConstants { public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; /** + * Authentication cannot proceed because re-enrollment is required. + * @hide + */ + int BIOMETRIC_ERROR_RE_ENROLL = 16; + + /** * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index f9eecaec5bb2..ac6ba0a4ac58 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -378,9 +378,10 @@ public abstract class CameraDevice implements AutoCloseable { * released, continuous repeating requests stopped and any pending * multi-frame capture requests flushed.</p> * - * <p>Note that the CameraExtensionSession currently supports at most two - * multi frame capture surface formats: ImageFormat.YUV_420_888 and - * ImageFormat.JPEG. Clients must query the multi-frame capture format support using + * <p>Note that the CameraExtensionSession currently supports at most wo + * multi frame capture surface formats: ImageFormat.JPEG will be supported + * by all extensions and ImageFormat.YUV_420_888 may or may not be supported. + * Clients must query the multi-frame capture format support using * {@link CameraExtensionCharacteristics#getExtensionSupportedSizes(int, int)}. * For repeating requests CameraExtensionSession supports only * {@link android.graphics.SurfaceTexture} as output. Clients can query the supported resolution diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index d3eb3779189b..6121cd260dc3 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -35,6 +35,7 @@ import android.util.Log; import android.util.Pair; import android.util.Size; +import java.util.HashSet; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -153,12 +154,8 @@ public final class CameraExtensionCharacteristics { mChars = chars; } - private static List<Size> generateSupportedSizes(List<SizeList> sizesList, - Integer format, - StreamConfigurationMap streamMap) { - // Per API contract it is assumed that the extension is able to support all - // camera advertised sizes for a given format in case it doesn't return - // a valid non-empty size list. + private static ArrayList<Size> getSupportedSizes(List<SizeList> sizesList, + Integer format) { ArrayList<Size> ret = new ArrayList<>(); if ((sizesList != null) && (!sizesList.isEmpty())) { for (SizeList entry : sizesList) { @@ -170,13 +167,36 @@ public final class CameraExtensionCharacteristics { } } } + + return ret; + } + + private static List<Size> generateSupportedSizes(List<SizeList> sizesList, + Integer format, + StreamConfigurationMap streamMap) { + // Per API contract it is assumed that the extension is able to support all + // camera advertised sizes for a given format in case it doesn't return + // a valid non-empty size list. + ArrayList<Size> ret = getSupportedSizes(sizesList, format); Size[] supportedSizes = streamMap.getOutputSizes(format); - if (supportedSizes != null) { + if ((ret.isEmpty()) && (supportedSizes != null)) { ret.addAll(Arrays.asList(supportedSizes)); } return ret; } + private static List<Size> generateJpegSupportedSizes(List<SizeList> sizesList, + StreamConfigurationMap streamMap) { + ArrayList<Size> extensionSizes = getSupportedSizes(sizesList, ImageFormat.YUV_420_888); + HashSet<Size> supportedSizes = extensionSizes.isEmpty() ? new HashSet<>(Arrays.asList( + streamMap.getOutputSizes(ImageFormat.YUV_420_888))) : new HashSet<>(extensionSizes); + HashSet<Size> supportedJpegSizes = new HashSet<>(Arrays.asList(streamMap.getOutputSizes( + ImageFormat.JPEG))); + supportedSizes.retainAll(supportedJpegSizes); + + return new ArrayList<>(supportedSizes); + } + /** * A per-process global camera extension manager instance, to track and * initialize/release extensions depending on client activity. @@ -488,8 +508,8 @@ public final class CameraExtensionCharacteristics { * {@link StreamConfigurationMap#getOutputSizes}.</p> * * <p>Device-specific extensions currently support at most two - * multi-frame capture surface formats, ImageFormat.YUV_420_888 or - * ImageFormat.JPEG.</p> + * multi-frame capture surface formats. ImageFormat.JPEG will be supported by all + * extensions and ImageFormat.YUV_420_888 may or may not be supported.</p> * * @param extension the extension type * @param format device-specific extension output format @@ -526,14 +546,17 @@ public final class CameraExtensionCharacteristics { format, streamMap); } else if (format == ImageFormat.JPEG) { extenders.second.init(mCameraId, mChars.getNativeMetadata()); - if (extenders.second.getCaptureProcessor() == null) { + if (extenders.second.getCaptureProcessor() != null) { + // The framework will perform the additional encoding pass on the + // processed YUV_420 buffers. + return generateJpegSupportedSizes( + extenders.second.getSupportedResolutions(), streamMap); + } else { return generateSupportedSizes(null, format, streamMap); } - - return new ArrayList<>(); + } else { + throw new IllegalArgumentException("Unsupported format: " + format); } - - throw new IllegalArgumentException("Unsupported format: " + format); } finally { unregisterClient(clientId); } diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java index 877dfbc49df2..e1b817768461 100644 --- a/core/java/android/hardware/camera2/CameraExtensionSession.java +++ b/core/java/android/hardware/camera2/CameraExtensionSession.java @@ -238,8 +238,10 @@ public abstract class CameraExtensionSession implements AutoCloseable { * from the camera device, to produce a single high-quality output result. * * <p>Note that single capture requests currently do not support - * client parameters. Settings included in the request will - * be entirely overridden by the device-specific extension. </p> + * client parameters except for {@link CaptureRequest#JPEG_ORIENTATION orientation} and + * {@link CaptureRequest#JPEG_QUALITY quality} in case of ImageFormat.JPEG output target. + * The rest of the settings included in the request will be entirely overridden by + * the device-specific extension. </p> * * <p>The {@link CaptureRequest.Builder#addTarget} supports only one * ImageFormat.YUV_420_888 or ImageFormat.JPEG target surface. {@link CaptureRequest} diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java new file mode 100644 index 000000000000..936734b0c711 --- /dev/null +++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java @@ -0,0 +1,312 @@ +/* + * 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.hardware.camera2.impl; + +import android.annotation.NonNull; +import android.graphics.ImageFormat; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.extension.CaptureBundle; +import android.hardware.camera2.extension.ICaptureProcessorImpl; +import android.media.Image; +import android.media.Image.Plane; +import android.media.ImageReader; +import android.media.ImageWriter; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.view.Surface; + +import java.nio.ByteBuffer; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; + +// Jpeg compress input YUV and queue back in the client target surface. +public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl { + public final static String TAG = "CameraExtensionJpeg"; + private final static int JPEG_QUEUE_SIZE = 1; + private final static int JPEG_DEFAULT_QUALITY = 100; + private final static int JPEG_DEFAULT_ROTATION = 0; + + private final Handler mHandler; + private final HandlerThread mHandlerThread; + private final ICaptureProcessorImpl mProcessor; + + private ImageReader mYuvReader = null; + private android.hardware.camera2.extension.Size mResolution = null; + private int mFormat = -1; + private Surface mOutputSurface = null; + private ImageWriter mOutputWriter = null; + + private static final class JpegParameters { + public HashSet<Long> mTimeStamps = new HashSet<>(); + public int mRotation = JPEG_DEFAULT_ROTATION; // CCW multiple of 90 degrees + public int mQuality = JPEG_DEFAULT_QUALITY; // [0..100] + } + + private ConcurrentLinkedQueue<JpegParameters> mJpegParameters = new ConcurrentLinkedQueue<>(); + + public CameraExtensionJpegProcessor(@NonNull ICaptureProcessorImpl processor) { + mProcessor = processor; + mHandlerThread = new HandlerThread(TAG); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + } + + public void close() { + mHandlerThread.quitSafely(); + + if (mOutputWriter != null) { + mOutputWriter.close(); + mOutputWriter = null; + } + + if (mYuvReader != null) { + mYuvReader.close(); + mYuvReader = null; + } + } + + private static JpegParameters getJpegParameters(List<CaptureBundle> captureBundles) { + JpegParameters ret = new JpegParameters(); + if (!captureBundles.isEmpty()) { + // The quality and orientation settings must be equal for requests in a burst + + Byte jpegQuality = captureBundles.get(0).captureResult.get(CaptureResult.JPEG_QUALITY); + if (jpegQuality != null) { + ret.mQuality = jpegQuality; + } else { + Log.w(TAG, "No jpeg quality set, using default: " + JPEG_DEFAULT_QUALITY); + } + + Integer orientation = captureBundles.get(0).captureResult.get( + CaptureResult.JPEG_ORIENTATION); + if (orientation != null) { + ret.mRotation = orientation / 90; + } else { + Log.w(TAG, "No jpeg rotation set, using default: " + JPEG_DEFAULT_ROTATION); + } + + for (CaptureBundle bundle : captureBundles) { + Long timeStamp = bundle.captureResult.get(CaptureResult.SENSOR_TIMESTAMP); + if (timeStamp != null) { + ret.mTimeStamps.add(timeStamp); + } else { + Log.e(TAG, "Capture bundle without valid sensor timestamp!"); + } + } + } + + return ret; + } + + /** + * Compresses a YCbCr image to jpeg, applying a crop and rotation. + * <p> + * The input is defined as a set of 3 planes of 8-bit samples, one plane for + * each channel of Y, Cb, Cr.<br> + * The Y plane is assumed to have the same width and height of the entire + * image.<br> + * The Cb and Cr planes are assumed to be downsampled by a factor of 2, to + * have dimensions (floor(width / 2), floor(height / 2)).<br> + * Each plane is specified by a direct java.nio.ByteBuffer, a pixel-stride, + * and a row-stride. So, the sample at coordinate (x, y) can be retrieved + * from byteBuffer[x * pixel_stride + y * row_stride]. + * <p> + * The pre-compression transformation is applied as follows: + * <ol> + * <li>The image is cropped to the rectangle from (cropLeft, cropTop) to + * (cropRight - 1, cropBottom - 1). So, a cropping-rectangle of (0, 0) - + * (width, height) is a no-op.</li> + * <li>The rotation is applied counter-clockwise relative to the coordinate + * space of the image, so a CCW rotation will appear CW when the image is + * rendered in scanline order. Only rotations which are multiples of + * 90-degrees are suppored, so the parameter 'rot90' specifies which + * multiple of 90 to rotate the image.</li> + * </ol> + * + * @param width the width of the image to compress + * @param height the height of the image to compress + * @param yBuf the buffer containing the Y component of the image + * @param yPStride the stride between adjacent pixels in the same row in + * yBuf + * @param yRStride the stride between adjacent rows in yBuf + * @param cbBuf the buffer containing the Cb component of the image + * @param cbPStride the stride between adjacent pixels in the same row in + * cbBuf + * @param cbRStride the stride between adjacent rows in cbBuf + * @param crBuf the buffer containing the Cr component of the image + * @param crPStride the stride between adjacent pixels in the same row in + * crBuf + * @param crRStride the stride between adjacent rows in crBuf + * @param outBuf a direct java.nio.ByteBuffer to hold the compressed jpeg. + * This must have enough capacity to store the result, or an + * error code will be returned. + * @param outBufCapacity the capacity of outBuf + * @param quality the jpeg-quality (1-100) to use + * @param cropLeft left-edge of the bounds of the image to crop to before + * rotation + * @param cropTop top-edge of the bounds of the image to crop to before + * rotation + * @param cropRight right-edge of the bounds of the image to crop to before + * rotation + * @param cropBottom bottom-edge of the bounds of the image to crop to + * before rotation + * @param rot90 the multiple of 90 to rotate the image CCW (after cropping) + */ + private static native int compressJpegFromYUV420pNative( + int width, int height, + ByteBuffer yBuf, int yPStride, int yRStride, + ByteBuffer cbBuf, int cbPStride, int cbRStride, + ByteBuffer crBuf, int crPStride, int crRStride, + ByteBuffer outBuf, int outBufCapacity, + int quality, + int cropLeft, int cropTop, int cropRight, int cropBottom, + int rot90); + + public void process(List<CaptureBundle> captureBundle) throws RemoteException { + JpegParameters jpegParams = getJpegParameters(captureBundle); + try { + mJpegParameters.add(jpegParams); + mProcessor.process(captureBundle); + } catch (Exception e) { + mJpegParameters.remove(jpegParams); + throw e; + } + } + + public void onOutputSurface(Surface surface, int format) throws RemoteException { + if (format != ImageFormat.JPEG) { + Log.e(TAG, "Unsupported output format: " + format); + return; + } + mOutputSurface = surface; + initializePipeline(); + } + + @Override + public void onResolutionUpdate(android.hardware.camera2.extension.Size size) + throws RemoteException { + mResolution = size; + initializePipeline(); + } + + public void onImageFormatUpdate(int format) throws RemoteException { + if (format != ImageFormat.YUV_420_888) { + Log.e(TAG, "Unsupported input format: " + format); + return; + } + mFormat = format; + initializePipeline(); + } + + private void initializePipeline() throws RemoteException { + if ((mFormat != -1) && (mOutputSurface != null) && (mResolution != null) && + (mYuvReader == null)) { + // Jpeg/blobs are expected to be configured with (w*h)x1 + mOutputWriter = ImageWriter.newInstance(mOutputSurface, 1 /*maxImages*/, + ImageFormat.JPEG, mResolution.width * mResolution.height, 1); + mYuvReader = ImageReader.newInstance(mResolution.width, mResolution.height, mFormat, + JPEG_QUEUE_SIZE); + mYuvReader.setOnImageAvailableListener(new YuvCallback(), mHandler); + mProcessor.onOutputSurface(mYuvReader.getSurface(), mFormat); + mProcessor.onResolutionUpdate(mResolution); + mProcessor.onImageFormatUpdate(mFormat); + } + } + + @Override + public IBinder asBinder() { + throw new UnsupportedOperationException("Binder IPC not supported!"); + } + + private class YuvCallback implements ImageReader.OnImageAvailableListener { + @Override + public void onImageAvailable(ImageReader reader) { + Image yuvImage = null; + Image jpegImage = null; + try { + yuvImage = mYuvReader.acquireNextImage(); + jpegImage = mOutputWriter.dequeueInputImage(); + } catch (IllegalStateException e) { + if (yuvImage != null) { + yuvImage.close(); + } + if (jpegImage != null) { + jpegImage.close(); + } + Log.e(TAG, "Failed to acquire processed yuv image or jpeg image!"); + return; + } + + ByteBuffer jpegBuffer = jpegImage.getPlanes()[0].getBuffer(); + jpegBuffer.clear(); + // Jpeg/blobs are expected to be configured with (w*h)x1 + int jpegCapacity = jpegImage.getWidth(); + + Plane lumaPlane = yuvImage.getPlanes()[0]; + Plane crPlane = yuvImage.getPlanes()[1]; + Plane cbPlane = yuvImage.getPlanes()[2]; + + Iterator<JpegParameters> jpegIter = mJpegParameters.iterator(); + JpegParameters jpegParams = null; + while(jpegIter.hasNext()) { + JpegParameters currentParams = jpegIter.next(); + if (currentParams.mTimeStamps.contains(yuvImage.getTimestamp())) { + jpegParams = currentParams; + jpegIter.remove(); + break; + } + } + if (jpegParams == null) { + if (mJpegParameters.isEmpty()) { + Log.w(TAG, "Empty jpeg settings queue! Using default jpeg orientation" + + " and quality!"); + jpegParams = new JpegParameters(); + jpegParams.mRotation = JPEG_DEFAULT_ROTATION; + jpegParams.mQuality = JPEG_DEFAULT_QUALITY; + } else { + Log.w(TAG, "No jpeg settings found with matching timestamp for current" + + " processed input!"); + Log.w(TAG, "Using values from the top of the queue!"); + jpegParams = mJpegParameters.poll(); + } + } + + compressJpegFromYUV420pNative( + yuvImage.getWidth(), yuvImage.getHeight(), + lumaPlane.getBuffer(), lumaPlane.getPixelStride(), lumaPlane.getRowStride(), + crPlane.getBuffer(), crPlane.getPixelStride(), crPlane.getRowStride(), + cbPlane.getBuffer(), cbPlane.getPixelStride(), cbPlane.getRowStride(), + jpegBuffer, jpegCapacity, jpegParams.mQuality, + 0, 0, yuvImage.getWidth(), yuvImage.getHeight(), + jpegParams.mRotation); + yuvImage.close(); + + try { + mOutputWriter.queueInputImage(jpegImage); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to queue encoded result!"); + } finally { + jpegImage.close(); + } + } + } +} diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index 8451dedb6c37..0a561716d076 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -91,6 +91,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { private ImageReader mStubCaptureImageReader = null; private ImageWriter mRepeatingRequestImageWriter = null; + private CameraExtensionJpegProcessor mImageJpegProcessor = null; private ICaptureProcessorImpl mImageProcessor = null; private CameraExtensionForwardProcessor mPreviewImageProcessor = null; private IRequestUpdateProcessorImpl mPreviewRequestUpdateProcessor = null; @@ -413,6 +414,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { if (mImageProcessor != null) { if (mClientCaptureSurface != null) { SurfaceInfo surfaceInfo = querySurface(mClientCaptureSurface); + if (surfaceInfo.mFormat == ImageFormat.JPEG) { + mImageJpegProcessor = new CameraExtensionJpegProcessor(mImageProcessor); + mImageProcessor = mImageJpegProcessor; + } mBurstCaptureImageReader = ImageReader.newInstance(surfaceInfo.mWidth, surfaceInfo.mHeight, CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT, mImageExtender.getMaxCaptureStage()); @@ -570,14 +575,16 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { return null; } - // Set user supported jpeg quality and rotation parameters + // This will override the extension capture stage jpeg parameters with the user set + // jpeg quality and rotation. This will guarantee that client configured jpeg + // parameters always have highest priority. Integer jpegRotation = clientRequest.get(CaptureRequest.JPEG_ORIENTATION); if (jpegRotation != null) { - requestBuilder.set(CaptureRequest.JPEG_ORIENTATION, jpegRotation); + captureStage.parameters.set(CaptureRequest.JPEG_ORIENTATION, jpegRotation); } Byte jpegQuality = clientRequest.get(CaptureRequest.JPEG_QUALITY); if (jpegQuality != null) { - requestBuilder.set(CaptureRequest.JPEG_QUALITY, jpegQuality); + captureStage.parameters.set(CaptureRequest.JPEG_QUALITY, jpegQuality); } requestBuilder.addTarget(target); @@ -753,6 +760,11 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { mPreviewImageProcessor = null; } + if (mImageJpegProcessor != null) { + mImageJpegProcessor.close(); + mImageJpegProcessor = null; + } + mCaptureSession = null; mImageProcessor = null; mCameraRepeatingSurface = mClientRepeatingRequestSurface = null; @@ -1014,7 +1026,10 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { mCaptureRequestMap.clear(); mCapturePendingMap.clear(); boolean processStatus = true; - List<CaptureBundle> captureList = initializeParcelable(mCaptureStageMap); + Byte jpegQuality = mClientRequest.get(CaptureRequest.JPEG_QUALITY); + Integer jpegOrientation = mClientRequest.get(CaptureRequest.JPEG_ORIENTATION); + List<CaptureBundle> captureList = initializeParcelable(mCaptureStageMap, + jpegOrientation, jpegQuality); try { mImageProcessor.process(captureList); } catch (RemoteException e) { @@ -1444,10 +1459,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } for (int i = idx; i >= 0; i--) { if (previewMap.valueAt(i).first != null) { - Log.w(TAG, "Discard pending buffer with timestamp: " + previewMap.keyAt(i)); previewMap.valueAt(i).first.close(); } else { - Log.w(TAG, "Discard pending result with timestamp: " + previewMap.keyAt(i)); if (mClientNotificationsEnabled && ((i != idx) || notifyCurrentIndex)) { Log.w(TAG, "Preview frame drop with timestamp: " + previewMap.keyAt(i)); final long ident = Binder.clearCallingIdentity(); @@ -1639,7 +1652,8 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } private static List<CaptureBundle> initializeParcelable( - HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap) { + HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap, Integer jpegOrientation, + Byte jpegQuality) { ArrayList<CaptureBundle> ret = new ArrayList<>(); for (Integer stagetId : captureMap.keySet()) { Pair<Image, TotalCaptureResult> entry = captureMap.get(stagetId); @@ -1648,6 +1662,12 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { bundle.captureImage = initializeParcelImage(entry.first); bundle.sequenceId = entry.second.getSequenceId(); bundle.captureResult = entry.second.getNativeMetadata(); + if (jpegOrientation != null) { + bundle.captureResult.set(CaptureResult.JPEG_ORIENTATION, jpegOrientation); + } + if (jpegQuality != null) { + bundle.captureResult.set(CaptureResult.JPEG_QUALITY, jpegQuality); + } ret.add(bundle); } diff --git a/core/java/android/hardware/devicestate/DeviceStateInfo.aidl b/core/java/android/hardware/devicestate/DeviceStateInfo.aidl new file mode 100644 index 000000000000..e85679243994 --- /dev/null +++ b/core/java/android/hardware/devicestate/DeviceStateInfo.aidl @@ -0,0 +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 android.hardware.devicestate; + +parcelable DeviceStateInfo; diff --git a/core/java/android/hardware/devicestate/DeviceStateInfo.java b/core/java/android/hardware/devicestate/DeviceStateInfo.java new file mode 100644 index 000000000000..bc6af37afc45 --- /dev/null +++ b/core/java/android/hardware/devicestate/DeviceStateInfo.java @@ -0,0 +1,162 @@ +/* + * 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.hardware.devicestate; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Objects; +import java.util.concurrent.Executor; + + +/** + * Information about the state of the device. + * + * @hide + */ +public final class DeviceStateInfo implements Parcelable { + /** Bit that indicates the {@link #supportedStates} field has changed. */ + public static final int CHANGED_SUPPORTED_STATES = 1 << 0; + + /** Bit that indicates the {@link #baseState} field has changed. */ + public static final int CHANGED_BASE_STATE = 1 << 1; + + /** Bit that indicates the {@link #currentState} field has changed. */ + public static final int CHANGED_CURRENT_STATE = 1 << 2; + + @IntDef(prefix = {"CHANGED_"}, flag = true, value = { + CHANGED_SUPPORTED_STATES, + CHANGED_BASE_STATE, + CHANGED_CURRENT_STATE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ChangeFlags {} + + /** + * The list of states supported by the device. + */ + @NonNull + public final int[] supportedStates; + + /** + * The base (non-override) state of the device. The base state is the state of the device + * ignoring any override requests made through a call to {@link DeviceStateManager#requestState( + * DeviceStateRequest, Executor, DeviceStateRequest.Callback)}. + */ + public final int baseState; + + /** + * The state of the device. + */ + public final int currentState; + + /** + * Creates a new instance of {@link DeviceStateInfo}. + * <p> + * NOTE: Unlike {@link #DeviceStateInfo(DeviceStateInfo)}, this constructor does not copy the + * supplied parameters. + */ + public DeviceStateInfo(@NonNull int[] supportedStates, int baseState, int state) { + this.supportedStates = supportedStates; + this.baseState = baseState; + this.currentState = state; + } + + /** + * Creates a new instance of {@link DeviceStateInfo} copying the fields of {@code info} into + * the fields of the returned instance. + */ + public DeviceStateInfo(@NonNull DeviceStateInfo info) { + this(Arrays.copyOf(info.supportedStates, info.supportedStates.length), + info.baseState, info.currentState); + } + + @Override + public boolean equals(@Nullable Object other) { + if (this == other) return true; + if (other == null || getClass() != other.getClass()) return false; + DeviceStateInfo that = (DeviceStateInfo) other; + return baseState == that.baseState + && currentState == that.currentState + && Arrays.equals(supportedStates, that.supportedStates); + } + + @Override + public int hashCode() { + int result = Objects.hash(baseState, currentState); + result = 31 * result + Arrays.hashCode(supportedStates); + return result; + } + + /** Returns a bitmask of the differences between this instance and {@code other}. */ + @ChangeFlags + public int diff(@NonNull DeviceStateInfo other) { + int diff = 0; + if (!Arrays.equals(supportedStates, other.supportedStates)) { + diff |= CHANGED_SUPPORTED_STATES; + } + if (baseState != other.baseState) { + diff |= CHANGED_BASE_STATE; + } + if (currentState != other.currentState) { + diff |= CHANGED_CURRENT_STATE; + } + return diff; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(supportedStates.length); + for (int i = 0; i < supportedStates.length; i++) { + dest.writeInt(supportedStates[i]); + } + + dest.writeInt(baseState); + dest.writeInt(currentState); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<DeviceStateInfo> CREATOR = new Creator<DeviceStateInfo>() { + @Override + public DeviceStateInfo createFromParcel(Parcel source) { + final int numberOfSupportedStates = source.readInt(); + final int[] supportedStates = new int[numberOfSupportedStates]; + for (int i = 0; i < numberOfSupportedStates; i++) { + supportedStates[i] = source.readInt(); + } + final int baseState = source.readInt(); + final int currentState = source.readInt(); + + return new DeviceStateInfo(supportedStates, baseState, currentState); + } + + @Override + public DeviceStateInfo[] newArray(int size) { + return new DeviceStateInfo[size]; + } + }; +} diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java index 2d4b2ccd7514..250145e92b89 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManager.java +++ b/core/java/android/hardware/devicestate/DeviceStateManager.java @@ -111,38 +111,63 @@ public final class DeviceStateManager { } /** - * Registers a listener to receive notifications about changes in device state. + * Registers a callback to receive notifications about changes in device state. * * @param executor the executor to process notifications. - * @param listener the listener to register. + * @param callback the callback to register. * - * @see DeviceStateListener + * @see DeviceStateCallback */ - public void addDeviceStateListener(@NonNull @CallbackExecutor Executor executor, - @NonNull DeviceStateListener listener) { - mGlobal.registerDeviceStateListener(listener, executor); + public void registerCallback(@NonNull @CallbackExecutor Executor executor, + @NonNull DeviceStateCallback callback) { + mGlobal.registerDeviceStateCallback(callback, executor); } /** - * Unregisters a listener previously registered with - * {@link #addDeviceStateListener(Executor, DeviceStateListener)}. + * Unregisters a callback previously registered with + * {@link #registerCallback(Executor, DeviceStateCallback)}. */ - public void removeDeviceStateListener(@NonNull DeviceStateListener listener) { - mGlobal.unregisterDeviceStateListener(listener); + public void unregisterCallback(@NonNull DeviceStateCallback callback) { + mGlobal.unregisterDeviceStateCallback(callback); } - /** - * Listens for changes in device states. - */ - public interface DeviceStateListener { + /** Callback to receive notifications about changes in device state. */ + public interface DeviceStateCallback { + /** + * Called in response to a change in the states supported by the device. + * <p> + * Guaranteed to be called once on registration of the callback with the initial value and + * then on every subsequent change in the supported states. + * + * @param supportedStates the new supported states. + * + * @see DeviceStateManager#getSupportedStates() + */ + default void onSupportedStatesChanged(@NonNull int[] supportedStates) {} + + /** + * Called in response to a change in the base device state. + * <p> + * The base state is the state of the device without considering any requests made through + * calls to {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)} + * from any client process. The base state is guaranteed to match the state provided with a + * call to {@link #onStateChanged(int)} when there are no active requests from any process. + * <p> + * Guaranteed to be called once on registration of the callback with the initial value and + * then on every subsequent change in the non-override state. + * + * @param state the new base device state. + */ + default void onBaseStateChanged(int state) {} + /** * Called in response to device state changes. * <p> - * Guaranteed to be called once on registration of the listener with the - * initial value and then on every subsequent change in device state. + * Guaranteed to be called once on registration of the callback with the initial value and + * then on every subsequent change in device state. * - * @param deviceState the new device state. + * @param state the new device state. */ - void onDeviceStateChanged(int deviceState); + void onStateChanged(int state); } } diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java index b9ae88ea840f..1b37fb9fdad3 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java @@ -19,7 +19,7 @@ package android.hardware.devicestate; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.hardware.devicestate.DeviceStateManager.DeviceStateListener; +import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -31,12 +31,14 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import java.util.ArrayList; +import java.util.Arrays; import java.util.concurrent.Executor; /** * Provides communication with the device state system service on behalf of applications. * * @see DeviceStateManager + * * @hide */ @VisibleForTesting(visibility = Visibility.PACKAGE) @@ -68,13 +70,13 @@ public final class DeviceStateManagerGlobal { private DeviceStateManagerCallback mCallback; @GuardedBy("mLock") - private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>(); + private final ArrayList<DeviceStateCallbackWrapper> mCallbacks = new ArrayList<>(); @GuardedBy("mLock") private final ArrayMap<IBinder, DeviceStateRequestWrapper> mRequests = new ArrayMap<>(); @Nullable @GuardedBy("mLock") - private Integer mLastReceivedState; + private DeviceStateInfo mLastReceivedInfo; @VisibleForTesting public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) { @@ -87,18 +89,31 @@ public final class DeviceStateManagerGlobal { * @see DeviceStateManager#getSupportedStates() */ public int[] getSupportedStates() { - try { - return mDeviceStateManager.getSupportedDeviceStates(); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); + synchronized (mLock) { + final DeviceStateInfo currentInfo; + if (mLastReceivedInfo != null) { + // If we have mLastReceivedInfo a callback is registered for this instance and it + // is receiving the most recent info from the server. Use that info here. + currentInfo = mLastReceivedInfo; + } else { + // If mLastReceivedInfo is null there is no registered callback so we manually + // fetch the current info. + try { + currentInfo = mDeviceStateManager.getDeviceStateInfo(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + return Arrays.copyOf(currentInfo.supportedStates, currentInfo.supportedStates.length); } } /** * Submits a {@link DeviceStateRequest request} to modify the device state. * - * @see DeviceStateManager#requestState(DeviceStateRequest, - * Executor, DeviceStateRequest.Callback) + * @see DeviceStateManager#requestState(DeviceStateRequest, Executor, + * DeviceStateRequest.Callback) * @see DeviceStateRequest */ public void requestState(@NonNull DeviceStateRequest request, @@ -157,49 +172,56 @@ public final class DeviceStateManagerGlobal { } /** - * Registers a listener to receive notifications about changes in device state. + * Registers a callback to receive notifications about changes in device state. * - * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener) + * @see DeviceStateManager#registerCallback(Executor, DeviceStateCallback) */ @VisibleForTesting(visibility = Visibility.PACKAGE) - public void registerDeviceStateListener(@NonNull DeviceStateListener listener, + public void registerDeviceStateCallback(@NonNull DeviceStateCallback callback, @NonNull Executor executor) { - Integer stateToReport; - DeviceStateListenerWrapper wrapper; + DeviceStateCallbackWrapper wrapper; + DeviceStateInfo currentInfo; synchronized (mLock) { - registerCallbackIfNeededLocked(); - - int index = findListenerLocked(listener); + int index = findCallbackLocked(callback); if (index != -1) { - // This listener is already registered. + // This callback is already registered. return; } - wrapper = new DeviceStateListenerWrapper(listener, executor); - mListeners.add(wrapper); - stateToReport = mLastReceivedState; - } + registerCallbackIfNeededLocked(); + if (mLastReceivedInfo == null) { + // Initialize the last received info with the current info if this is the first + // callback being registered. + try { + mLastReceivedInfo = mDeviceStateManager.getDeviceStateInfo(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } - if (stateToReport != null) { - // Notify the listener with the most recent device state from the server. If the state - // to report is null this is likely the first listener added and we're still waiting - // from the callback from the server. - wrapper.notifyDeviceStateChanged(stateToReport); + currentInfo = new DeviceStateInfo(mLastReceivedInfo); + + wrapper = new DeviceStateCallbackWrapper(callback, executor); + mCallbacks.add(wrapper); } + + wrapper.notifySupportedStatesChanged(currentInfo.supportedStates); + wrapper.notifyBaseStateChanged(currentInfo.baseState); + wrapper.notifyStateChanged(currentInfo.currentState); } /** - * Unregisters a listener previously registered with - * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}. + * Unregisters a callback previously registered with + * {@link #registerDeviceStateCallback(DeviceStateCallback, Executor)}}. * - * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener) + * @see DeviceStateManager#unregisterCallback(DeviceStateCallback) */ @VisibleForTesting(visibility = Visibility.PACKAGE) - public void unregisterDeviceStateListener(DeviceStateListener listener) { + public void unregisterDeviceStateCallback(@NonNull DeviceStateCallback callback) { synchronized (mLock) { - int indexToRemove = findListenerLocked(listener); + int indexToRemove = findCallbackLocked(callback); if (indexToRemove != -1) { - mListeners.remove(indexToRemove); + mCallbacks.remove(indexToRemove); } } } @@ -210,14 +232,15 @@ public final class DeviceStateManagerGlobal { try { mDeviceStateManager.registerCallback(mCallback); } catch (RemoteException ex) { + mCallback = null; throw ex.rethrowFromSystemServer(); } } } - private int findListenerLocked(DeviceStateListener listener) { - for (int i = 0; i < mListeners.size(); i++) { - if (mListeners.get(i).mDeviceStateListener.equals(listener)) { + private int findCallbackLocked(DeviceStateCallback callback) { + for (int i = 0; i < mCallbacks.size(); i++) { + if (mCallbacks.get(i).mDeviceStateCallback.equals(callback)) { return i; } } @@ -234,16 +257,34 @@ public final class DeviceStateManagerGlobal { return null; } - /** Handles a call from the server that the device state has changed. */ - private void handleDeviceStateChanged(int newDeviceState) { - ArrayList<DeviceStateListenerWrapper> listeners; + /** Handles a call from the server that the device state info has changed. */ + private void handleDeviceStateInfoChanged(@NonNull DeviceStateInfo info) { + ArrayList<DeviceStateCallbackWrapper> callbacks; + DeviceStateInfo oldInfo; synchronized (mLock) { - mLastReceivedState = newDeviceState; - listeners = new ArrayList<>(mListeners); + oldInfo = mLastReceivedInfo; + mLastReceivedInfo = info; + callbacks = new ArrayList<>(mCallbacks); } - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).notifyDeviceStateChanged(newDeviceState); + final int diff = oldInfo == null ? 1 : info.diff(oldInfo); + if ((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0) { + for (int i = 0; i < callbacks.size(); i++) { + // Copy the array to prevent callbacks from modifying the internal state. + final int[] supportedStates = Arrays.copyOf(info.supportedStates, + info.supportedStates.length); + callbacks.get(i).notifySupportedStatesChanged(supportedStates); + } + } + if ((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0) { + for (int i = 0; i < callbacks.size(); i++) { + callbacks.get(i).notifyBaseStateChanged(info.baseState); + } + } + if ((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0) { + for (int i = 0; i < callbacks.size(); i++) { + callbacks.get(i).notifyStateChanged(info.currentState); + } } } @@ -291,8 +332,8 @@ public final class DeviceStateManagerGlobal { private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub { @Override - public void onDeviceStateChanged(int deviceState) { - handleDeviceStateChanged(deviceState); + public void onDeviceStateInfoChanged(DeviceStateInfo info) { + handleDeviceStateInfoChanged(info); } @Override @@ -311,17 +352,29 @@ public final class DeviceStateManagerGlobal { } } - private static final class DeviceStateListenerWrapper { - private final DeviceStateListener mDeviceStateListener; + private static final class DeviceStateCallbackWrapper { + @NonNull + private final DeviceStateCallback mDeviceStateCallback; + @NonNull private final Executor mExecutor; - DeviceStateListenerWrapper(DeviceStateListener listener, Executor executor) { - mDeviceStateListener = listener; + DeviceStateCallbackWrapper(@NonNull DeviceStateCallback callback, + @NonNull Executor executor) { + mDeviceStateCallback = callback; mExecutor = executor; } - void notifyDeviceStateChanged(int newDeviceState) { - mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState)); + void notifySupportedStatesChanged(int[] newSupportedStates) { + mExecutor.execute(() -> + mDeviceStateCallback.onSupportedStatesChanged(newSupportedStates)); + } + + void notifyBaseStateChanged(int newBaseState) { + mExecutor.execute(() -> mDeviceStateCallback.onBaseStateChanged(newBaseState)); + } + + void notifyStateChanged(int newDeviceState) { + mExecutor.execute(() -> mDeviceStateCallback.onStateChanged(newDeviceState)); } } diff --git a/core/java/android/hardware/devicestate/DeviceStateRequest.java b/core/java/android/hardware/devicestate/DeviceStateRequest.java index 70f7002597ed..df488d2f6df1 100644 --- a/core/java/android/hardware/devicestate/DeviceStateRequest.java +++ b/core/java/android/hardware/devicestate/DeviceStateRequest.java @@ -116,7 +116,7 @@ public final class DeviceStateRequest { * requested state. * <p> * Guaranteed to be called after a call to - * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)} with a state + * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)} with a state * matching the requested state. */ default void onRequestActivated(@NonNull DeviceStateRequest request) {} @@ -125,7 +125,7 @@ public final class DeviceStateRequest { * Called to indicate the request has been temporarily suspended. * <p> * Guaranteed to be called before a call to - * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}. + * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}. */ default void onRequestSuspended(@NonNull DeviceStateRequest request) {} @@ -135,7 +135,7 @@ public final class DeviceStateRequest { * DeviceStateRequest.Callback)}. * <p> * Guaranteed to be called before a call to - * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}. + * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}. * <p> * Note: A call to {@link #onRequestSuspended(DeviceStateRequest)} is not guaranteed to * occur before this method. diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl index 323ad21e4884..14ed03d09fd0 100644 --- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl +++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl @@ -16,25 +16,26 @@ package android.hardware.devicestate; +import android.hardware.devicestate.DeviceStateInfo; import android.hardware.devicestate.IDeviceStateManagerCallback; /** @hide */ interface IDeviceStateManager { + /** Returns the current device state info. */ + DeviceStateInfo getDeviceStateInfo(); + /** * Registers a callback to receive notifications from the device state manager. Only one * callback can be registered per-process. * <p> * As the callback mechanism is used to alert the caller of changes to request status a callback * <b>MUST</b> be registered before calling {@link #requestState(IBinder, int, int)} or - * {@link #cancelRequest(IBinder)}. Otherwise an exception will be thrown. + * {@link #cancelRequest(IBinder)}, otherwise an exception will be thrown. * * @throws SecurityException if a callback is already registered for the calling process. */ void registerCallback(in IDeviceStateManagerCallback callback); - /** Returns the array of supported device state identifiers. */ - int[] getSupportedDeviceStates(); - /** * Requests that the device enter the supplied {@code state}. A callback <b>MUST</b> have been * previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a diff --git a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl index ee2a071741ef..593be867fe28 100644 --- a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl +++ b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl @@ -16,20 +16,23 @@ package android.hardware.devicestate; +import android.hardware.devicestate.DeviceStateInfo; + /** @hide */ interface IDeviceStateManagerCallback { /** - * Called in response to a change in device state. Guaranteed to be called once with the initial - * value on registration of the callback. + * Called in response to a change in {@link DeviceStateInfo}. + * + * @param info the new device state info. * - * @param deviceState the new state of the device. + * @see DeviceStateInfo */ - oneway void onDeviceStateChanged(int deviceState); + oneway void onDeviceStateInfoChanged(in DeviceStateInfo info); /** * Called to notify the callback that a request has become active. Guaranteed to be called - * after a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming active - * resulted in a device state change. + * after a subsequent call to {@link #onDeviceStateInfoChanged(DeviceStateInfo)} if the request + * becoming active resulted in a change of device state info. * * @param token the request token previously registered with * {@link IDeviceStateManager#requestState(IBinder, int, int)} @@ -38,8 +41,8 @@ interface IDeviceStateManagerCallback { /** * Called to notify the callback that a request has become suspended. Guaranteed to be called - * before a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming - * suspended resulted in a device state change. + * before a subsequent call to {@link #onDeviceStateInfoChanged(DeviceStateInfo)} if the request + * becoming suspended resulted in a change of device state info. * * @param token the request token previously registered with * {@link IDeviceStateManager#requestState(IBinder, int, int)} @@ -49,8 +52,8 @@ interface IDeviceStateManagerCallback { /** * Called to notify the callback that a request has become canceled. No further callbacks will * be triggered for this request. Guaranteed to be called before a subsequent call to - * {@link #onDeviceStateChanged(int)} if the request becoming canceled resulted in a device - * state change. + * {@link #onDeviceStateInfoChanged(DeviceStateInfo)} if the request becoming canceled resulted + * in a change of device state info. * * @param token the request token previously registered with * {@link IDeviceStateManager#requestState(IBinder, int, int)} diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 48b05b7d9e15..2d58520a942e 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -67,12 +67,6 @@ public abstract class DisplayManagerInternal { public abstract boolean isProximitySensorAvailable(); /** - * Returns the id of the {@link com.android.server.display.DisplayGroup} to which the provided - * display belongs. - */ - public abstract int getDisplayGroupId(int displayId); - - /** * Registers a display group listener which will be informed of the addition, removal, or change * of display groups. * @@ -469,7 +463,7 @@ public abstract class DisplayManagerInternal { void onStateChanged(); void onProximityPositive(); void onProximityNegative(); - void onDisplayStateChange(int state); // one of the Display state constants + void onDisplayStateChange(boolean allInactive, boolean allOff); void acquireSuspendBlocker(); void releaseSuspendBlocker(); diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index a9bcdeff7e47..0256b7bc6de0 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -831,6 +831,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan case BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED: return context.getString( com.android.internal.R.string.face_error_security_update_required); + case BIOMETRIC_ERROR_RE_ENROLL: + return context.getString( + com.android.internal.R.string.face_recalibrate_notification_content); case FACE_ERROR_VENDOR: { String[] msgArray = context.getResources().getStringArray( com.android.internal.R.array.face_error_vendor); @@ -1389,7 +1392,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan case FACE_ACQUIRED_PAN_TOO_EXTREME: case FACE_ACQUIRED_TILT_TOO_EXTREME: case FACE_ACQUIRED_ROLL_TOO_EXTREME: - return context.getString(R.string.face_acquired_not_detected); + return context.getString(R.string.face_acquired_poor_gaze); // Provide more detailed feedback for other soft errors. case FACE_ACQUIRED_INSUFFICIENT: @@ -1445,13 +1448,17 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan case FACE_ACQUIRED_TOO_FAR: return context.getString(R.string.face_acquired_too_far); case FACE_ACQUIRED_TOO_HIGH: - return context.getString(R.string.face_acquired_too_high); - case FACE_ACQUIRED_TOO_LOW: + // TODO(b/181269243): Change back once error codes are fixed. return context.getString(R.string.face_acquired_too_low); + case FACE_ACQUIRED_TOO_LOW: + // TODO(b/181269243) Change back once error codes are fixed. + return context.getString(R.string.face_acquired_too_high); case FACE_ACQUIRED_TOO_RIGHT: - return context.getString(R.string.face_acquired_too_right); - case FACE_ACQUIRED_TOO_LEFT: + // TODO(b/181269243) Change back once error codes are fixed. return context.getString(R.string.face_acquired_too_left); + case FACE_ACQUIRED_TOO_LEFT: + // TODO(b/181269243) Change back once error codes are fixed. + return context.getString(R.string.face_acquired_too_right); case FACE_ACQUIRED_POOR_GAZE: return context.getString(R.string.face_acquired_poor_gaze); case FACE_ACQUIRED_NOT_DETECTED: diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 9d086cf203e2..fc795d8a0488 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -114,6 +114,21 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ public static final int SENSOR_ID_ANY = -1; + private static class RemoveTracker { + static final int REMOVE_SINGLE = 1; + static final int REMOVE_ALL = 2; + @IntDef({REMOVE_SINGLE, REMOVE_ALL}) + @interface RemoveRequest {} + + final @RemoveRequest int mRemoveRequest; + @Nullable final Fingerprint mSingleFingerprint; + + RemoveTracker(@RemoveRequest int request, @Nullable Fingerprint fingerprint) { + mRemoveRequest = request; + mSingleFingerprint = fingerprint; + } + } + private IFingerprintService mService; private Context mContext; private IBinder mToken = new Binder(); @@ -123,10 +138,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing private RemovalCallback mRemovalCallback; private GenerateChallengeCallback mGenerateChallengeCallback; private CryptoObject mCryptoObject; - private Fingerprint mRemovalFingerprint; + @Nullable private RemoveTracker mRemoveTracker; private Handler mHandler; - /** * Retrieves a list of properties for all fingerprint sensors on the device. * @hide @@ -736,7 +750,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing public void remove(Fingerprint fp, int userId, RemovalCallback callback) { if (mService != null) try { mRemovalCallback = callback; - mRemovalFingerprint = fp; + mRemoveTracker = new RemoveTracker(RemoveTracker.REMOVE_SINGLE, fp); mService.remove(mToken, fp.getBiometricId(), userId, mServiceReceiver, mContext.getOpPackageName()); } catch (RemoteException e) { @@ -753,6 +767,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing if (mService != null) { try { mRemovalCallback = callback; + mRemoveTracker = new RemoveTracker(RemoveTracker.REMOVE_ALL, null /* fp */); mService.removeAll(mToken, userId, mServiceReceiver, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1056,16 +1071,29 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing if (mRemovalCallback == null) { return; } - if (fingerprint == null) { - Slog.e(TAG, "Received MSG_REMOVED, but fingerprint is null"); + + if (mRemoveTracker == null) { + Slog.w(TAG, "Removal tracker is null"); return; } - int fingerId = fingerprint.getBiometricId(); - int reqFingerId = mRemovalFingerprint.getBiometricId(); - if (reqFingerId != 0 && fingerId != 0 && fingerId != reqFingerId) { - Slog.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId); - return; + if (mRemoveTracker.mRemoveRequest == RemoveTracker.REMOVE_SINGLE) { + if (fingerprint == null) { + Slog.e(TAG, "Received MSG_REMOVED, but fingerprint is null"); + return; + } + + if (mRemoveTracker.mSingleFingerprint == null) { + Slog.e(TAG, "Missing fingerprint"); + return; + } + + final int fingerId = fingerprint.getBiometricId(); + int reqFingerId = mRemoveTracker.mSingleFingerprint.getBiometricId(); + if (reqFingerId != 0 && fingerId != 0 && fingerId != reqFingerId) { + Slog.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId); + return; + } } mRemovalCallback.onRemovalSucceeded(fingerprint, remaining); @@ -1122,7 +1150,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing mAuthenticationCallback.onAuthenticationError(clientErrMsgId, getErrorString(mContext, errMsgId, vendorCode)); } else if (mRemovalCallback != null) { - mRemovalCallback.onRemovalError(mRemovalFingerprint, clientErrMsgId, + final Fingerprint fp = mRemoveTracker != null + ? mRemoveTracker.mSingleFingerprint : null; + mRemovalCallback.onRemovalError(fp, clientErrMsgId, getErrorString(mContext, errMsgId, vendorCode)); } } diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java index 663a70452b24..51addc95ac79 100644 --- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java +++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java @@ -85,8 +85,8 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna boolean resetLockoutRequiresHardwareAuthToken) { // TODO(b/179175438): Value should be provided from the HAL this(sensorId, strength, maxEnrollmentsPerUser, sensorType, - resetLockoutRequiresHardwareAuthToken, 0 /* sensorLocationX */, - 0 /* sensorLocationY */, 0 /* sensorRadius */); + resetLockoutRequiresHardwareAuthToken, 540 /* sensorLocationX */, + 1636 /* sensorLocationY */, 130 /* sensorRadius */); } /** diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index eaa38f3e862c..4743fee3257b 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -25,6 +25,8 @@ import android.hardware.input.TouchCalibration; import android.os.CombinedVibrationEffect; import android.hardware.input.IInputSensorEventListener; import android.hardware.input.InputSensorInfo; +import android.hardware.lights.Light; +import android.hardware.lights.LightState; import android.os.IBinder; import android.os.IVibratorStateListener; import android.os.VibrationEffect; @@ -127,4 +129,14 @@ interface IInputManager { void disableSensor(int deviceId, int sensorType); boolean flushSensor(int deviceId, int sensorType); + + List<Light> getLights(int deviceId); + + LightState getLightState(int deviceId, int lightId); + + void setLightStates(int deviceId, in int[] lightIds, in LightState[] states, in IBinder token); + + void openLightSession(int deviceId, String opPkg, in IBinder token); + + void closeLightSession(int deviceId, in IBinder token); } diff --git a/core/java/android/hardware/input/InputDeviceLightsManager.java b/core/java/android/hardware/input/InputDeviceLightsManager.java new file mode 100644 index 000000000000..a3b91a99fdb7 --- /dev/null +++ b/core/java/android/hardware/input/InputDeviceLightsManager.java @@ -0,0 +1,139 @@ +/* + * 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.hardware.input; + +import android.annotation.NonNull; +import android.app.ActivityThread; +import android.hardware.lights.Light; +import android.hardware.lights.LightState; +import android.hardware.lights.LightsManager; +import android.hardware.lights.LightsRequest; +import android.util.CloseGuard; + +import com.android.internal.util.Preconditions; + +import java.lang.ref.Reference; +import java.util.List; + +/** + * LightsManager manages an input device's lights {@link android.hardware.input.Light}. + */ +class InputDeviceLightsManager extends LightsManager { + private static final String TAG = "InputDeviceLightsManager"; + private static final boolean DEBUG = false; + + private final InputManager mInputManager; + + // The input device ID. + private final int mDeviceId; + // Package name + private final String mPackageName; + + InputDeviceLightsManager(InputManager inputManager, int deviceId) { + super(ActivityThread.currentActivityThread().getSystemContext()); + mInputManager = inputManager; + mDeviceId = deviceId; + mPackageName = ActivityThread.currentPackageName(); + } + + /** + * Returns the lights available on the device. + * + * @return A list of available lights + */ + @Override + public @NonNull List<Light> getLights() { + return mInputManager.getLights(mDeviceId); + } + + /** + * Returns the state of a specified light. + * + * @hide + */ + @Override + public @NonNull LightState getLightState(@NonNull Light light) { + Preconditions.checkNotNull(light); + return mInputManager.getLightState(mDeviceId, light); + } + + /** + * Creates a new LightsSession that can be used to control the device lights. + */ + @Override + public @NonNull LightsSession openSession() { + final LightsSession session = new InputDeviceLightsSession(); + mInputManager.openLightSession(mDeviceId, mPackageName, session.getToken()); + return session; + } + + /** + * Encapsulates a session that can be used to control device lights and represents the lifetime + * of the requests. + */ + public final class InputDeviceLightsSession extends LightsManager.LightsSession + implements AutoCloseable { + + private final CloseGuard mCloseGuard = new CloseGuard(); + private boolean mClosed = false; + + /** + * Instantiated by {@link LightsManager#openSession()}. + */ + private InputDeviceLightsSession() { + mCloseGuard.open("close"); + } + + /** + * Sends a request to modify the states of multiple lights. + * + * @param request the settings for lights that should change + */ + @Override + public void requestLights(@NonNull LightsRequest request) { + Preconditions.checkNotNull(request); + Preconditions.checkArgument(!mClosed); + + mInputManager.requestLights(mDeviceId, request, getToken()); + } + + /** + * Closes the session, reverting all changes made through it. + */ + @Override + public void close() { + if (!mClosed) { + mInputManager.closeLightSession(mDeviceId, getToken()); + mClosed = true; + mCloseGuard.close(); + } + Reference.reachabilityFence(this); + } + + /** @hide */ + @Override + protected void finalize() throws Throwable { + try { + mCloseGuard.warnIfOpen(); + close(); + } finally { + super.finalize(); + } + } + } + +} diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index 8a01c660ebd0..e15d6298d63d 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -30,6 +30,10 @@ import android.compat.annotation.ChangeId; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.hardware.SensorManager; +import android.hardware.lights.Light; +import android.hardware.lights.LightState; +import android.hardware.lights.LightsManager; +import android.hardware.lights.LightsRequest; import android.os.BlockUntrustedTouchesMode; import android.os.Build; import android.os.CombinedVibrationEffect; @@ -1409,7 +1413,7 @@ public final class InputManager { } /** - * Gets a vibrator service associated with an input device, always create a new instance. + * Gets a vibrator service associated with an input device, always creates a new instance. * @return The vibrator, never null. * @hide */ @@ -1418,7 +1422,7 @@ public final class InputManager { } /** - * Gets a vibrator manager service associated with an input device, always create a new + * Gets a vibrator manager service associated with an input device, always creates a new * instance. * @return The vibrator manager, never null. * @hide @@ -1486,10 +1490,8 @@ public final class InputManager { /** * Register input device vibrator state listener - * - * @hide */ - public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) { + boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) { try { return mIm.registerVibratorStateListener(deviceId, listener); } catch (RemoteException ex) { @@ -1499,10 +1501,8 @@ public final class InputManager { /** * Unregister input device vibrator state listener - * - * @hide */ - public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) { + boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) { try { return mIm.unregisterVibratorStateListener(deviceId, listener); } catch (RemoteException ex) { @@ -1511,7 +1511,7 @@ public final class InputManager { } /** - * Gets a sensor manager service associated with an input device, always create a new instance. + * Gets a sensor manager service associated with an input device, always creates a new instance. * @return The sensor manager, never null. * @hide */ @@ -1533,6 +1533,86 @@ public final class InputManager { } /** + * Gets a lights manager associated with an input device, always creates a new instance. + * @return The lights manager, never null. + * @hide + */ + @NonNull + public LightsManager getInputDeviceLightsManager(int deviceId) { + return new InputDeviceLightsManager(InputManager.this, deviceId); + } + + /** + * Gets a list of light objects associated with an input device. + * @return The list of lights, never null. + */ + @NonNull List<Light> getLights(int deviceId) { + try { + return mIm.getLights(deviceId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the state of an input device light. + * @return the light state + */ + @NonNull LightState getLightState(int deviceId, @NonNull Light light) { + try { + return mIm.getLightState(deviceId, light.getId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Request to modify the states of multiple lights. + * + * @param request the settings for lights that should change + */ + void requestLights(int deviceId, @NonNull LightsRequest request, IBinder token) { + try { + List<Integer> lightIdList = request.getLights(); + int[] lightIds = new int[lightIdList.size()]; + for (int i = 0; i < lightIds.length; i++) { + lightIds[i] = lightIdList.get(i); + } + List<LightState> lightStateList = request.getLightStates(); + mIm.setLightStates(deviceId, lightIds, + lightStateList.toArray(new LightState[lightStateList.size()]), + token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Open light session for input device manager + * + * @param token The token for the light session + */ + void openLightSession(int deviceId, String opPkg, @NonNull IBinder token) { + try { + mIm.openLightSession(deviceId, opPkg, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Close light session + * + */ + void closeLightSession(int deviceId, @NonNull IBinder token) { + try { + mIm.closeLightSession(deviceId, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Listens for changes in input devices. */ public interface InputDeviceListener { diff --git a/core/java/android/hardware/input/OWNERS b/core/java/android/hardware/input/OWNERS index 25e02e1aa6f3..c390b33fa174 100644 --- a/core/java/android/hardware/input/OWNERS +++ b/core/java/android/hardware/input/OWNERS @@ -1,6 +1,3 @@ # Bug component: 136048 include /services/core/java/com/android/server/input/OWNERS - -michaelwr@google.com -svv@google.com diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java index da270182052d..7bfff5d3af97 100644 --- a/core/java/android/hardware/lights/Light.java +++ b/core/java/android/hardware/lights/Light.java @@ -16,22 +16,56 @@ package android.hardware.lights; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Represents a logical light on the device. * - * @hide */ -@SystemApi public final class Light implements Parcelable { + // These enum values copy the values from {@link com.android.server.lights.LightsManager} + // and the light HAL. Since 0-7 are lights reserved for system use, 8 for microphone light is + // defined in {@link android.hardware.lights.LightsManager}, following types are available + // through this API. + /** Type for lights that indicate microphone usage */ + public static final int LIGHT_TYPE_MICROPHONE = 8; + + /** + * Type for lights that indicate a monochrome color LED light. + */ + public static final int LIGHT_TYPE_INPUT_SINGLE = 9; + + /** + * Type for lights that indicate a group of LED lights representing player ID. + */ + public static final int LIGHT_TYPE_INPUT_PLAYER_ID = 10; + + /** + * Type for lights that indicate a color LED light. + */ + public static final int LIGHT_TYPE_INPUT_RGB = 11; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"LIGHT_TYPE_"}, + value = { + LIGHT_TYPE_INPUT_PLAYER_ID, + LIGHT_TYPE_INPUT_SINGLE, + LIGHT_TYPE_INPUT_RGB, + }) + public @interface LightType {} + private final int mId; private final int mOrdinal; private final int mType; + private final String mName; /** * Creates a new light with the given data. @@ -39,15 +73,26 @@ public final class Light implements Parcelable { * @hide */ public Light(int id, int ordinal, int type) { + this(id, ordinal, type, "Light"); + } + + /** + * Creates a new light with the given data. + * + * @hide + */ + public Light(int id, int ordinal, int type, String name) { mId = id; mOrdinal = ordinal; mType = type; + mName = name; } private Light(@NonNull Parcel in) { mId = in.readInt(); mOrdinal = in.readInt(); mType = in.readInt(); + mName = in.readString(); } /** Implement the Parcelable interface */ @@ -56,6 +101,7 @@ public final class Light implements Parcelable { dest.writeInt(mId); dest.writeInt(mOrdinal); dest.writeInt(mType); + dest.writeString(mName); } /** Implement the Parcelable interface */ @@ -100,6 +146,14 @@ public final class Light implements Parcelable { } /** + * Returns the name of the light. + */ + @NonNull + public String getName() { + return mName; + } + + /** * Returns the ordinal of the light. * * <p>This is a sort key that represents the physical order of lights on the device with the diff --git a/core/java/android/hardware/lights/LightState.java b/core/java/android/hardware/lights/LightState.java index cd39e6df91a9..650b383eeb0f 100644 --- a/core/java/android/hardware/lights/LightState.java +++ b/core/java/android/hardware/lights/LightState.java @@ -32,36 +32,93 @@ import android.os.Parcelable; * will be converted to only a brightness value and that will be used for the light's single * channel. * - * @hide */ -@SystemApi public final class LightState implements Parcelable { private final int mColor; + private final int mPlayerId; /** - * Creates a new LightState with the desired color and intensity. + * Creates a new LightState with the desired color and intensity, for a light type + * of RBG color or monochrome color. * * @param color the desired color and intensity in ARGB format. + * @deprecated this has been replaced with {@link android.hardware.lights.LightState#forColor } + * @hide */ + @Deprecated + @SystemApi public LightState(@ColorInt int color) { + this(color, 0); + } + + /** + * Creates a new LightState with the desired color and intensity, and the player Id. + * Player Id will only be applied on Light type + * {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID} + * + * @param color the desired color and intensity in ARGB format. + * @hide + */ + public LightState(@ColorInt int color, int playerId) { mColor = color; + mPlayerId = playerId; + } + + /** + * Creates a new LightState with the desired color and intensity, for a light type + * of RBG color or single monochrome color. + * + * @param color the desired color and intensity in ARGB format. + * @return The LightState object contains the color. + */ + @NonNull + public static LightState forColor(@ColorInt int color) { + return new LightState(color, 0); + } + + /** + * Creates a new LightState with the desired player id, for a light of type + * {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID}. + * + * @param playerId the desired player id. + * @return The LightState object contains the player id. + */ + @NonNull + public static LightState forPlayerId(int playerId) { + return new LightState(0, playerId); } + /** + * Creates a new LightState from a parcel object. + */ private LightState(@NonNull Parcel in) { mColor = in.readInt(); + mPlayerId = in.readInt(); } /** - * Return the color and intensity associated with this LightState. - * @return the color and intensity in ARGB format. The A channel is ignored. + * Returns the color and intensity associated with this LightState. + * @return the color and intensity in ARGB format. The A channel is ignored. return 0 when + * calling LightsManager.getLightState with LIGHT_TYPE_INPUT_PLAYER_ID. */ public @ColorInt int getColor() { return mColor; } + /** + * Returns the player ID associated with this LightState for Light type + * {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID}, + * or 0 for other types. + * @return the player ID. + */ + public int getPlayerId() { + return mPlayerId; + } + @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mColor); + dest.writeInt(mPlayerId); } @Override @@ -69,6 +126,12 @@ public final class LightState implements Parcelable { return 0; } + @Override + public String toString() { + return "LightState{Color=0x" + Integer.toHexString(mColor) + ", PlayerId=" + + mPlayerId + "}"; + } + public static final @NonNull Parcelable.Creator<LightState> CREATOR = new Parcelable.Creator<LightState>() { public LightState createFromParcel(Parcel in) { diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java index 33e5fcaf2abb..8fd56db33c4b 100644 --- a/core/java/android/hardware/lights/LightsManager.java +++ b/core/java/android/hardware/lights/LightsManager.java @@ -16,43 +16,38 @@ package android.hardware.lights; -import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.content.Context; import android.os.Binder; import android.os.IBinder; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.ServiceManager.ServiceNotFoundException; -import android.util.CloseGuard; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.lang.ref.Reference; import java.util.List; /** * The LightsManager class allows control over device lights. * - * @hide */ -@SystemApi @SystemService(Context.LIGHTS_SERVICE) -public final class LightsManager { +public abstract class LightsManager { private static final String TAG = "LightsManager"; + @NonNull private final Context mContext; // These enum values copy the values from {@link com.android.server.lights.LightsManager} // and the light HAL. Since 0-7 are lights reserved for system use, only the microphone light - // is available through this API. - /** Type for lights that indicate microphone usage */ + // and following types are available through this API. + /** Type for lights that indicate microphone usage + * @deprecated this has been moved to {@link android.hardware.lights.Light } + * @hide + */ + @Deprecated + @SystemApi public static final int LIGHT_TYPE_MICROPHONE = 8; /** @hide */ @@ -63,28 +58,11 @@ public final class LightsManager { }) public @interface LightType {} - @NonNull private final Context mContext; - @NonNull private final ILightsManager mService; - /** - * Creates a LightsManager. - * - * @hide + * @hide to prevent subclassing from outside of the framework */ - public LightsManager(@NonNull Context context) throws ServiceNotFoundException { - this(context, ILightsManager.Stub.asInterface( - ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE))); - } - - /** - * Creates a LightsManager with a provided service implementation. - * - * @hide - */ - @VisibleForTesting - public LightsManager(@NonNull Context context, @NonNull ILightsManager service) { + public LightsManager(Context context) { mContext = Preconditions.checkNotNull(context); - mService = Preconditions.checkNotNull(service); } /** @@ -92,112 +70,44 @@ public final class LightsManager { * * @return A list of available lights */ - @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) - public @NonNull List<Light> getLights() { - try { - return mService.getLights(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } + public @NonNull abstract List<Light> getLights(); /** * Returns the state of a specified light. * - * @hide */ - @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) - @TestApi - public @NonNull LightState getLightState(@NonNull Light light) { - Preconditions.checkNotNull(light); - try { - return mService.getLightState(light.getId()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } + public abstract @NonNull LightState getLightState(@NonNull Light light); /** * Creates a new LightsSession that can be used to control the device lights. */ - @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) - public @NonNull LightsSession openSession() { - try { - final LightsSession session = new LightsSession(); - mService.openSession(session.mToken); - return session; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } + public abstract @NonNull LightsSession openSession(); /** * Encapsulates a session that can be used to control device lights and represents the lifetime * of the requests. */ - public final class LightsSession implements AutoCloseable { - + public abstract static class LightsSession implements AutoCloseable { private final IBinder mToken = new Binder(); - - private final CloseGuard mCloseGuard = new CloseGuard(); - private boolean mClosed = false; - - /** - * Instantiated by {@link LightsManager#openSession()}. - */ - @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) - private LightsSession() { - mCloseGuard.open("close"); - } - /** * Sends a request to modify the states of multiple lights. * - * <p>This method only controls lights that aren't overridden by higher-priority sessions. - * Additionally, lights not controlled by this session can be controlled by lower-priority - * sessions. - * * @param request the settings for lights that should change */ - @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) - public void requestLights(@NonNull LightsRequest request) { - Preconditions.checkNotNull(request); - if (!mClosed) { - try { - mService.setLightStates(mToken, request.mLightIds, request.mLightStates); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - } + public abstract void requestLights(@NonNull LightsRequest request); - /** - * Closes the session, reverting all changes made through it. - */ - @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) @Override - public void close() { - if (!mClosed) { - try { - mService.closeSession(mToken); - mClosed = true; - mCloseGuard.close(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - Reference.reachabilityFence(this); - } + public abstract void close(); - /** @hide */ - @Override - protected void finalize() throws Throwable { - try { - mCloseGuard.warnIfOpen(); - close(); - } finally { - super.finalize(); - } + /** + * Get the token of a light session. + * + * @return Binder token of the light session. + * @hide + */ + public @NonNull IBinder getToken() { + return mToken; } } + } diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java index a318992c35ee..2626a461aaf5 100644 --- a/core/java/android/hardware/lights/LightsRequest.java +++ b/core/java/android/hardware/lights/LightsRequest.java @@ -17,17 +17,17 @@ package android.hardware.lights; import android.annotation.NonNull; -import android.annotation.SystemApi; import android.util.SparseArray; import com.android.internal.util.Preconditions; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** * Encapsulates a request to modify the state of multiple lights. * - * @hide */ -@SystemApi public final class LightsRequest { /** Visible to {@link LightsManager.Session}. */ @@ -50,6 +50,30 @@ public final class LightsRequest { } /** + * Get a list of Light as ids. The ids will returned in same order as the lights passed + * in Builder. + * + * @return List of light ids + */ + public @NonNull List<Integer> getLights() { + List<Integer> lightList = new ArrayList<Integer>(mLightIds.length); + for (int i = 0; i < mLightIds.length; i++) { + lightList.add(mLightIds[i]); + } + return lightList; + } + + /** + * Get a list of LightState. The states will be returned in same order as the light states + * passed in Builder. + * + * @return List of light states + */ + public @NonNull List<LightState> getLightStates() { + return Arrays.asList(mLightStates); + } + + /** * Builder for creating device light change requests. */ public static final class Builder { @@ -62,7 +86,7 @@ public final class LightsRequest { * @param light the light to modify * @param state the desired color and intensity of the light */ - public @NonNull Builder setLight(@NonNull Light light, @NonNull LightState state) { + public @NonNull Builder addLight(@NonNull Light light, @NonNull LightState state) { Preconditions.checkNotNull(light); Preconditions.checkNotNull(state); mChanges.put(light.getId(), state); diff --git a/core/java/android/hardware/lights/SystemLightsManager.java b/core/java/android/hardware/lights/SystemLightsManager.java new file mode 100644 index 000000000000..726a61359c01 --- /dev/null +++ b/core/java/android/hardware/lights/SystemLightsManager.java @@ -0,0 +1,181 @@ +/* + * 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.hardware.lights; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.content.Context; +import android.hardware.lights.LightsManager.LightsSession; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; +import android.util.CloseGuard; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +import java.lang.ref.Reference; +import java.util.List; + +/** + * The LightsManager class allows control over device lights. + * + * @hide + */ +public final class SystemLightsManager extends LightsManager { + private static final String TAG = "LightsManager"; + + @NonNull private final ILightsManager mService; + + /** + * Creates a SystemLightsManager. + * + * @hide + */ + public SystemLightsManager(@NonNull Context context) throws ServiceNotFoundException { + this(context, ILightsManager.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE))); + } + + /** + * Creates a SystemLightsManager with a provided service implementation. + * + * @hide + */ + @VisibleForTesting + public SystemLightsManager(@NonNull Context context, @NonNull ILightsManager service) { + super(context); + mService = Preconditions.checkNotNull(service); + } + + /** + * Returns the lights available on the device. + * + * @return A list of available lights + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + @Override + public @NonNull List<Light> getLights() { + try { + return mService.getLights(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the state of a specified light. + * + * @hide + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + @Override + public @NonNull LightState getLightState(@NonNull Light light) { + Preconditions.checkNotNull(light); + try { + return mService.getLightState(light.getId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Creates a new LightsSession that can be used to control the device lights. + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + @Override + public @NonNull LightsSession openSession() { + try { + final LightsSession session = new SystemLightsSession(); + mService.openSession(session.getToken()); + return session; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Encapsulates a session that can be used to control device lights and represents the lifetime + * of the requests. + */ + public final class SystemLightsSession extends LightsManager.LightsSession + implements AutoCloseable { + + private final CloseGuard mCloseGuard = new CloseGuard(); + private boolean mClosed = false; + + /** + * Instantiated by {@link LightsManager#openSession()}. + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + private SystemLightsSession() { + mCloseGuard.open("close"); + } + + /** + * Sends a request to modify the states of multiple lights. + * + * <p>This method only controls lights that aren't overridden by higher-priority sessions. + * Additionally, lights not controlled by this session can be controlled by lower-priority + * sessions. + * + * @param request the settings for lights that should change + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + @Override + public void requestLights(@NonNull LightsRequest request) { + Preconditions.checkNotNull(request); + if (!mClosed) { + try { + mService.setLightStates(getToken(), request.mLightIds, request.mLightStates); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Closes the session, reverting all changes made through it. + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + @Override + public void close() { + if (!mClosed) { + try { + mService.closeSession(getToken()); + mClosed = true; + mCloseGuard.close(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + Reference.reachabilityFence(this); + } + + /** @hide */ + @Override + protected void finalize() throws Throwable { + try { + mCloseGuard.warnIfOpen(); + close(); + } finally { + super.finalize(); + } + } + } +} diff --git a/core/java/android/hardware/soundtrigger/OWNERS b/core/java/android/hardware/soundtrigger/OWNERS index 816bc6bba639..e5d037003ac4 100644 --- a/core/java/android/hardware/soundtrigger/OWNERS +++ b/core/java/android/hardware/soundtrigger/OWNERS @@ -1 +1,2 @@ -include /core/java/android/media/soundtrigger/OWNERS +ytai@google.com +elaurent@google.com diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index 32b19a462218..303a40755d4e 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -18,6 +18,7 @@ package android.net; import static android.net.ConnectivityManager.TYPE_WIFI; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.net.wifi.WifiInfo; @@ -41,6 +42,22 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { public static final int SUBTYPE_COMBINED = -1; + /** + * Network has no {@code NetworkCapabilities#NET_CAPABILITY_OEM_*}. + * @hide + */ + public static final int OEM_NONE = 0x0; + /** + * Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID}. + * @hide + */ + public static final int OEM_PAID = 0x1; + /** + * Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE}. + * @hide + */ + public static final int OEM_PRIVATE = 0x2; + final int mType; final int mSubType; final String mSubscriberId; @@ -48,10 +65,11 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { final boolean mRoaming; final boolean mMetered; final boolean mDefaultNetwork; + final int mOemManaged; public NetworkIdentity( int type, int subType, String subscriberId, String networkId, boolean roaming, - boolean metered, boolean defaultNetwork) { + boolean metered, boolean defaultNetwork, int oemManaged) { mType = type; mSubType = subType; mSubscriberId = subscriberId; @@ -59,12 +77,13 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { mRoaming = roaming; mMetered = metered; mDefaultNetwork = defaultNetwork; + mOemManaged = oemManaged; } @Override public int hashCode() { return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered, - mDefaultNetwork); + mDefaultNetwork, mOemManaged); } @Override @@ -75,7 +94,8 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { && Objects.equals(mSubscriberId, ident.mSubscriberId) && Objects.equals(mNetworkId, ident.mNetworkId) && mMetered == ident.mMetered - && mDefaultNetwork == ident.mDefaultNetwork; + && mDefaultNetwork == ident.mDefaultNetwork + && mOemManaged == ident.mOemManaged; } return false; } @@ -102,6 +122,8 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { } builder.append(", metered=").append(mMetered); builder.append(", defaultNetwork=").append(mDefaultNetwork); + // TODO(180557699): Print a human readable string for OEM managed state. + builder.append(", oemManaged=").append(mOemManaged); return builder.append("}").toString(); } @@ -120,6 +142,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { proto.write(NetworkIdentityProto.ROAMING, mRoaming); proto.write(NetworkIdentityProto.METERED, mMetered); proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork); + proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK, mOemManaged); proto.end(start); } @@ -152,6 +175,10 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { return mDefaultNetwork; } + public int getOemManaged() { + return mOemManaged; + } + /** * Build a {@link NetworkIdentity} from the given {@link NetworkState} and {@code subType}, * assuming that any mobile networks are using the current IMSI. The subType if applicable, @@ -171,6 +198,8 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { subscriberId = state.subscriberId; + final int oemManaged = getOemBitfield(state.networkCapabilities); + if (legacyType == TYPE_WIFI) { if (state.networkCapabilities.getSsid() != null) { networkId = state.networkCapabilities.getSsid(); @@ -185,7 +214,24 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { } return new NetworkIdentity(legacyType, subType, subscriberId, networkId, roaming, metered, - defaultNetwork); + defaultNetwork, oemManaged); + } + + /** + * Builds a bitfield of {@code NetworkIdentity.OEM_*} based on {@link NetworkCapabilities}. + * @hide + */ + public static int getOemBitfield(NetworkCapabilities nc) { + int oemManaged = OEM_NONE; + + if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID)) { + oemManaged |= OEM_PAID; + } + if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE)) { + oemManaged |= OEM_PRIVATE; + } + + return oemManaged; } @Override @@ -209,6 +255,9 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { if (res == 0) { res = Boolean.compare(mDefaultNetwork, another.mDefaultNetwork); } + if (res == 0) { + res = Integer.compare(mOemManaged, another.mOemManaged); + } return res; } } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 6353a25e745f..664120698971 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -16,14 +16,17 @@ package android.net; +import static android.app.ActivityManager.procStateToString; import static android.content.pm.PackageManager.GET_SIGNATURES; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.annotation.TestApi; import android.app.ActivityManager; +import android.app.ActivityManager.ProcessCapability; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; @@ -617,8 +620,18 @@ public class NetworkPolicyManager { * to access network when the device is idle or in battery saver mode. Otherwise, false. * @hide */ - public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) { - return procState <= FOREGROUND_THRESHOLD_STATE; + public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(@Nullable UidState uidState) { + if (uidState == null) { + return false; + } + return isProcStateAllowedWhileIdleOrPowerSaveMode(uidState.procState, uidState.capability); + } + + /** @hide */ + public static boolean isProcStateAllowedWhileIdleOrPowerSaveMode( + int procState, @ProcessCapability int capability) { + return procState <= FOREGROUND_THRESHOLD_STATE + || (capability & ActivityManager.PROCESS_CAPABILITY_NETWORK) != 0; } /** @@ -626,11 +639,44 @@ public class NetworkPolicyManager { * to access network when the device is in data saver mode. Otherwise, false. * @hide */ + public static boolean isProcStateAllowedWhileOnRestrictBackground(@Nullable UidState uidState) { + if (uidState == null) { + return false; + } + return isProcStateAllowedWhileOnRestrictBackground(uidState.procState); + } + + /** @hide */ public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) { + // Data saver and bg policy restrictions will only take procstate into account. return procState <= FOREGROUND_THRESHOLD_STATE; } /** @hide */ + public static final class UidState { + public int uid; + public int procState; + public int capability; + + public UidState(int uid, int procState, int capability) { + this.uid = uid; + this.procState = procState; + this.capability = capability; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("{procState="); + sb.append(procStateToString(procState)); + sb.append(",cap="); + ActivityManager.printCapabilitiesSummary(sb, capability); + sb.append("}"); + return sb.toString(); + } + } + + /** @hide */ @TestApi @NonNull public static String resolveNetworkId(@NonNull WifiConfiguration config) { diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java index 79f9e6ef2a97..dbb312720373 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -15,9 +15,6 @@ */ package android.net; -import static android.Manifest.permission.NETWORK_STACK; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -26,8 +23,7 @@ import android.content.Context; import android.os.IBinder; import android.os.ServiceManager; -import java.util.ArrayList; -import java.util.Arrays; +import com.android.net.module.util.PermissionUtils; /** * Constants and utilities for client code communicating with the network stack service. * @hide @@ -79,9 +75,14 @@ public class NetworkStack { * @param context {@link android.content.Context} for the process. * * @hide + * + * @deprecated Use {@link PermissionUtils#enforceNetworkStackPermission} instead. + * + * TODO: remove this method and let the users call to PermissionUtils directly. */ + @Deprecated public static void checkNetworkStackPermission(final @NonNull Context context) { - checkNetworkStackPermissionOr(context); + PermissionUtils.enforceNetworkStackPermission(context); } /** @@ -92,31 +93,14 @@ public class NetworkStack { * @param otherPermissions The set of permissions that could be the candidate permissions , or * empty string if none of other permissions needed. * @hide + * + * @deprecated Use {@link PermissionUtils#enforceNetworkStackPermissionOr} instead. + * + * TODO: remove this method and let the users call to PermissionUtils directly. */ + @Deprecated public static void checkNetworkStackPermissionOr(final @NonNull Context context, final @NonNull String... otherPermissions) { - ArrayList<String> permissions = new ArrayList<String>(Arrays.asList(otherPermissions)); - permissions.add(NETWORK_STACK); - permissions.add(PERMISSION_MAINLINE_NETWORK_STACK); - enforceAnyPermissionOf(context, permissions.toArray(new String[0])); + PermissionUtils.enforceNetworkStackPermissionOr(context, otherPermissions); } - - private static void enforceAnyPermissionOf(final @NonNull Context context, - final @NonNull String... permissions) { - if (!checkAnyPermissionOf(context, permissions)) { - throw new SecurityException("Requires one of the following permissions: " - + String.join(", ", permissions) + "."); - } - } - - private static boolean checkAnyPermissionOf(final @NonNull Context context, - final @NonNull String... permissions) { - for (String permission : permissions) { - if (context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { - return true; - } - } - return false; - } - } diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java index e466d2e626be..813fde1c15f2 100644 --- a/core/java/android/net/NetworkState.java +++ b/core/java/android/net/NetworkState.java @@ -41,7 +41,6 @@ public class NetworkState implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public final Network network; public final String subscriberId; - public final String networkId; public final int legacyNetworkType; private NetworkState() { @@ -50,35 +49,33 @@ public class NetworkState implements Parcelable { networkCapabilities = null; network = null; subscriberId = null; - networkId = null; legacyNetworkType = 0; } public NetworkState(int legacyNetworkType, @NonNull LinkProperties linkProperties, @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network, - @Nullable String subscriberId, @Nullable String networkId) { + @Nullable String subscriberId) { this(legacyNetworkType, new NetworkInfo(legacyNetworkType, 0, null, null), linkProperties, - networkCapabilities, network, subscriberId, networkId); + networkCapabilities, network, subscriberId); } // Constructor that used internally in ConnectivityService mainline module. public NetworkState(@NonNull NetworkInfo networkInfo, @NonNull LinkProperties linkProperties, @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network, - String subscriberId, String networkId) { + @Nullable String subscriberId) { this(networkInfo.getType(), networkInfo, linkProperties, - networkCapabilities, network, subscriberId, networkId); + networkCapabilities, network, subscriberId); } public NetworkState(int legacyNetworkType, @NonNull NetworkInfo networkInfo, @NonNull LinkProperties linkProperties, @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network, - String subscriberId, String networkId) { + @Nullable String subscriberId) { this.networkInfo = networkInfo; this.linkProperties = linkProperties; this.networkCapabilities = networkCapabilities; this.network = network; this.subscriberId = subscriberId; - this.networkId = networkId; this.legacyNetworkType = legacyNetworkType; // This object is an atomic view of a network, so the various components @@ -99,7 +96,6 @@ public class NetworkState implements Parcelable { networkCapabilities = in.readParcelable(null); network = in.readParcelable(null); subscriberId = in.readString(); - networkId = in.readString(); legacyNetworkType = in.readInt(); } @@ -115,7 +111,6 @@ public class NetworkState implements Parcelable { out.writeParcelable(networkCapabilities, flags); out.writeParcelable(network, flags); out.writeString(subscriberId); - out.writeString(networkId); out.writeInt(legacyNetworkType); } diff --git a/core/java/android/net/NetworkStateSnapshot.aidl b/core/java/android/net/NetworkStateSnapshot.aidl new file mode 100644 index 000000000000..cb602d7927ce --- /dev/null +++ b/core/java/android/net/NetworkStateSnapshot.aidl @@ -0,0 +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 android.net; + +parcelable NetworkStateSnapshot; diff --git a/core/java/android/net/NetworkStateSnapshot.java b/core/java/android/net/NetworkStateSnapshot.java new file mode 100644 index 000000000000..881b373fa241 --- /dev/null +++ b/core/java/android/net/NetworkStateSnapshot.java @@ -0,0 +1,106 @@ +/* + * 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.net; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Snapshot of network state. + * + * @hide + */ +public final class NetworkStateSnapshot implements Parcelable { + @NonNull + public final LinkProperties linkProperties; + @NonNull + public final NetworkCapabilities networkCapabilities; + @NonNull + public final Network network; + @Nullable + public final String subscriberId; + public final int legacyType; + + public NetworkStateSnapshot(@NonNull LinkProperties linkProperties, + @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network, + @Nullable String subscriberId, int legacyType) { + this.linkProperties = Objects.requireNonNull(linkProperties); + this.networkCapabilities = Objects.requireNonNull(networkCapabilities); + this.network = Objects.requireNonNull(network); + this.subscriberId = subscriberId; + this.legacyType = legacyType; + } + + public NetworkStateSnapshot(@NonNull Parcel in) { + linkProperties = in.readParcelable(null); + networkCapabilities = in.readParcelable(null); + network = in.readParcelable(null); + subscriberId = in.readString(); + legacyType = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeParcelable(linkProperties, flags); + out.writeParcelable(networkCapabilities, flags); + out.writeParcelable(network, flags); + out.writeString(subscriberId); + out.writeInt(legacyType); + } + + @NonNull + public static final Creator<NetworkStateSnapshot> CREATOR = + new Creator<NetworkStateSnapshot>() { + @NonNull + @Override + public NetworkStateSnapshot createFromParcel(@NonNull Parcel in) { + return new NetworkStateSnapshot(in); + } + + @NonNull + @Override + public NetworkStateSnapshot[] newArray(int size) { + return new NetworkStateSnapshot[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof NetworkStateSnapshot)) return false; + NetworkStateSnapshot that = (NetworkStateSnapshot) o; + return legacyType == that.legacyType + && Objects.equals(linkProperties, that.linkProperties) + && Objects.equals(networkCapabilities, that.networkCapabilities) + && Objects.equals(network, that.network) + && Objects.equals(subscriberId, that.subscriberId); + } + + @Override + public int hashCode() { + return Objects.hash(linkProperties, networkCapabilities, network, subscriberId, legacyType); + } +} diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index aa61e03b285c..c83dd99c2a3b 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -23,6 +23,7 @@ import static android.net.ConnectivityManager.TYPE_PROXY; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI_P2P; import static android.net.ConnectivityManager.TYPE_WIMAX; +import static android.net.NetworkIdentity.OEM_NONE; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.DEFAULT_NETWORK_YES; @@ -99,6 +100,22 @@ public class NetworkTemplate implements Parcelable { */ public static final int NETWORK_TYPE_5G_NSA = -2; + /** + * Value to match both OEM managed and unmanaged networks (all networks). + * @hide + */ + public static final int OEM_MANAGED_ALL = -1; + /** + * Value to match networks which are not OEM managed. + * @hide + */ + public static final int OEM_MANAGED_NO = OEM_NONE; + /** + * Value to match any OEM managed network. + * @hide + */ + public static final int OEM_MANAGED_YES = -2; + private static boolean isKnownMatchRule(final int rule) { switch (rule) { case MATCH_MOBILE: @@ -151,10 +168,10 @@ public class NetworkTemplate implements Parcelable { @NetworkType int ratType) { if (TextUtils.isEmpty(subscriberId)) { return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null, - METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType); + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL); } return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null, - METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType); + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL); } /** @@ -235,6 +252,9 @@ public class NetworkTemplate implements Parcelable { private final int mDefaultNetwork; private final int mSubType; + // Bitfield containing OEM network properties{@code NetworkIdentity#OEM_*}. + private final int mOemManaged; + @UnsupportedAppUsage public NetworkTemplate(int matchRule, String subscriberId, String networkId) { this(matchRule, subscriberId, new String[] { subscriberId }, networkId); @@ -243,11 +263,12 @@ public class NetworkTemplate implements Parcelable { public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, String networkId) { this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL); + DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL); } public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds, - String networkId, int metered, int roaming, int defaultNetwork, int subType) { + String networkId, int metered, int roaming, int defaultNetwork, int subType, + int oemManaged) { mMatchRule = matchRule; mSubscriberId = subscriberId; mMatchSubscriberIds = matchSubscriberIds; @@ -256,6 +277,7 @@ public class NetworkTemplate implements Parcelable { mRoaming = roaming; mDefaultNetwork = defaultNetwork; mSubType = subType; + mOemManaged = oemManaged; if (!isKnownMatchRule(matchRule)) { Log.e(TAG, "Unknown network template rule " + matchRule @@ -272,6 +294,7 @@ public class NetworkTemplate implements Parcelable { mRoaming = in.readInt(); mDefaultNetwork = in.readInt(); mSubType = in.readInt(); + mOemManaged = in.readInt(); } @Override @@ -284,6 +307,7 @@ public class NetworkTemplate implements Parcelable { dest.writeInt(mRoaming); dest.writeInt(mDefaultNetwork); dest.writeInt(mSubType); + dest.writeInt(mOemManaged); } @Override @@ -319,13 +343,16 @@ public class NetworkTemplate implements Parcelable { if (mSubType != NETWORK_TYPE_ALL) { builder.append(", subType=").append(mSubType); } + if (mOemManaged != OEM_MANAGED_ALL) { + builder.append(", oemManaged=").append(mOemManaged); + } return builder.toString(); } @Override public int hashCode() { return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming, - mDefaultNetwork, mSubType); + mDefaultNetwork, mSubType, mOemManaged); } @Override @@ -338,7 +365,8 @@ public class NetworkTemplate implements Parcelable { && mMetered == other.mMetered && mRoaming == other.mRoaming && mDefaultNetwork == other.mDefaultNetwork - && mSubType == other.mSubType; + && mSubType == other.mSubType + && mOemManaged == other.mOemManaged; } return false; } @@ -384,6 +412,7 @@ public class NetworkTemplate implements Parcelable { if (!matchesMetered(ident)) return false; if (!matchesRoaming(ident)) return false; if (!matchesDefaultNetwork(ident)) return false; + if (!matchesOemNetwork(ident)) return false; switch (mMatchRule) { case MATCH_MOBILE: @@ -425,6 +454,13 @@ public class NetworkTemplate implements Parcelable { || (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork); } + private boolean matchesOemNetwork(NetworkIdentity ident) { + return (mOemManaged == OEM_MANAGED_ALL) + || (mOemManaged == OEM_MANAGED_YES + && ident.mOemManaged != OEM_NONE) + || (mOemManaged == ident.mOemManaged); + } + private boolean matchesCollapsedRatType(NetworkIdentity ident) { return mSubType == NETWORK_TYPE_ALL || getCollapsedRatType(mSubType) == getCollapsedRatType(ident.mSubType); diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java index 49047d3a0c87..8f6510ed3ea5 100644 --- a/core/java/android/net/NetworkWatchlistManager.java +++ b/core/java/android/net/NetworkWatchlistManager.java @@ -16,6 +16,8 @@ package android.net; +import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; @@ -29,6 +31,7 @@ import com.android.internal.util.Preconditions; * Class that manage network watchlist in system. * @hide */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @SystemService(Context.NETWORK_WATCHLIST_SERVICE) public class NetworkWatchlistManager { @@ -90,6 +93,7 @@ public class NetworkWatchlistManager { /** * Get Network Watchlist config file hash. */ + @Nullable public byte[] getWatchlistConfigHash() { try { return mNetworkWatchlistManager.getWatchlistConfigHash(); diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java index b172ccc4e370..f0e7da78d669 100644 --- a/core/java/android/net/UidRange.java +++ b/core/java/android/net/UidRange.java @@ -42,10 +42,6 @@ public final class UidRange implements Parcelable { stop = stopUid; } - public static UidRange createForUser(int userId) { - return new UidRange(userId * PER_USER_RANGE, (userId + 1) * PER_USER_RANGE - 1); - } - /** Creates a UidRange for the specified user. */ public static UidRange createForUser(UserHandle user) { final UserHandle nextUser = UserHandle.of(user.getIdentifier() + 1); diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index e43b0b6fa635..f90fbaf1e0fb 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -596,7 +596,8 @@ public class VpnService extends Service { } } } - mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null)); + mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null, null, + RouteInfo.RTN_UNICAST)); mConfig.updateAllowedFamilies(address); return this; } diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/core/java/android/net/vcn/IVcnStatusCallback.aidl index a7386718d5ae..d91cef592d10 100644 --- a/core/java/android/net/vcn/IVcnStatusCallback.aidl +++ b/core/java/android/net/vcn/IVcnStatusCallback.aidl @@ -17,6 +17,12 @@ package android.net.vcn; /** @hide */ -interface IVcnStatusCallback { +oneway interface IVcnStatusCallback { void onEnteredSafeMode(); + void onVcnStatusChanged(int statusCode); + void onGatewayConnectionError( + in int[] gatewayNetworkCapabilities, + int errorCode, + in String exceptionClass, + in String exceptionMessage); }
\ No newline at end of file diff --git a/core/java/android/net/vcn/VcnControlPlaneConfig.java b/core/java/android/net/vcn/VcnControlPlaneConfig.java index 0c6ccfee5d5d..92f6c4440377 100644 --- a/core/java/android/net/vcn/VcnControlPlaneConfig.java +++ b/core/java/android/net/vcn/VcnControlPlaneConfig.java @@ -35,8 +35,6 @@ import java.util.Objects; * * @see VcnManager * @see VcnGatewayConnectionConfig - * - * @hide */ public abstract class VcnControlPlaneConfig { /** @hide */ diff --git a/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java b/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java index 2f6e1f63b960..de086f63b14d 100644 --- a/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java +++ b/core/java/android/net/vcn/VcnControlPlaneIkeConfig.java @@ -34,8 +34,6 @@ import java.util.Objects; * configuration, authentication and authorization parameters. * * @see VcnControlPlaneConfig - * - * @hide */ public final class VcnControlPlaneIkeConfig extends VcnControlPlaneConfig { private static final String TAG = VcnControlPlaneIkeConfig.class.getSimpleName(); diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index 94dff9159bd9..9f83b21f0d0c 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -420,7 +420,6 @@ public final class VcnGatewayConnectionConfig { * * @param ctrlPlaneConfig the control plane configuration * @see VcnControlPlaneConfig - * @hide */ public Builder(@NonNull VcnControlPlaneConfig ctrlPlaneConfig) { Objects.requireNonNull(ctrlPlaneConfig, "ctrlPlaneConfig was null"); @@ -428,13 +427,6 @@ public final class VcnGatewayConnectionConfig { mCtrlPlaneConfig = ctrlPlaneConfig; } - /** Construct a Builder object. */ - // TODO: Remove this constructor when #Builder(ctrlPlaneConfig) is exposed as public API. - // This constructor is created to avoid changing API shape in this CL - public Builder() { - mCtrlPlaneConfig = null; - } - /** * Add a capability that this VCN Gateway Connection will support. * diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index aed64de52cd0..eb8c251fec78 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -17,8 +17,11 @@ package android.net.vcn; import static java.util.Objects.requireNonNull; +import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.net.LinkProperties; @@ -32,6 +35,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -68,8 +73,7 @@ import java.util.concurrent.Executor; public class VcnManager { @NonNull private static final String TAG = VcnManager.class.getSimpleName(); - private static final Map< - VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> + private static final Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); @NonNull private final Context mContext; @@ -89,13 +93,13 @@ public class VcnManager { } /** - * Get all currently registered VcnUnderlyingNetworkPolicyListeners for testing purposes. + * Get all currently registered VcnNetworkPolicyListeners for testing purposes. * * @hide */ @VisibleForTesting(visibility = Visibility.PRIVATE) @NonNull - public static Map<VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> + public static Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> getAllPolicyListeners() { return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS); } @@ -157,45 +161,126 @@ public class VcnManager { } } - // TODO: make VcnUnderlyingNetworkPolicyListener @SystemApi + // TODO(b/180537630): remove all VcnUnderlyingNetworkPolicyListener refs once Telephony is using + // the new VcnNetworkPolicyListener API /** * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components * can register to receive updates for VCN-underlying Network policies from the System Server. * * @hide */ - public interface VcnUnderlyingNetworkPolicyListener { + public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyListener {} + + /** + * Add a listener for VCN-underlying network policy updates. + * + * @param executor the Executor that will be used for invoking all calls to the specified + * Listener + * @param listener the VcnUnderlyingNetworkPolicyListener to be added + * @throws SecurityException if the caller does not have permission NETWORK_FACTORY + * @throws IllegalStateException if the specified VcnUnderlyingNetworkPolicyListener is already + * registered + * @hide + */ + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public void addVcnUnderlyingNetworkPolicyListener( + @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) { + addVcnNetworkPolicyListener(executor, listener); + } + + /** + * Remove the specified VcnUnderlyingNetworkPolicyListener from VcnManager. + * + * <p>If the specified listener is not currently registered, this is a no-op. + * + * @param listener the VcnUnderlyingNetworkPolicyListener that will be removed + * @hide + */ + public void removeVcnUnderlyingNetworkPolicyListener( + @NonNull VcnUnderlyingNetworkPolicyListener listener) { + removeVcnNetworkPolicyListener(listener); + } + + /** + * Queries the underlying network policy for a network with the given parameters. + * + * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy + * may have changed via {@link VcnUnderlyingNetworkPolicyListener#onPolicyChanged()}, a Network + * Provider MUST poll for the updated Network policy based on that Network's capabilities and + * properties. + * + * @param networkCapabilities the NetworkCapabilities to be used in determining the Network + * policy for this Network. + * @param linkProperties the LinkProperties to be used in determining the Network policy for + * this Network. + * @throws SecurityException if the caller does not have permission NETWORK_FACTORY + * @return the VcnUnderlyingNetworkPolicy to be used for this Network. + * @hide + */ + @NonNull + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy( + @NonNull NetworkCapabilities networkCapabilities, + @NonNull LinkProperties linkProperties) { + requireNonNull(networkCapabilities, "networkCapabilities must not be null"); + requireNonNull(linkProperties, "linkProperties must not be null"); + + try { + return mService.getUnderlyingNetworkPolicy(networkCapabilities, linkProperties); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * VcnNetworkPolicyListener is the interface through which internal system components (e.g. + * Network Factories) can register to receive updates for VCN-underlying Network policies from + * the System Server. + * + * <p>Any Network Factory that brings up Networks capable of being VCN-underlying Networks + * should register a VcnNetworkPolicyListener. VcnManager will then use this listener to notify + * the registrant when VCN Network policies change. Upon receiving this signal, the listener + * must check {@link VcnManager} for the current Network policy result for each of its Networks + * via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}. + * + * @hide + */ + @SystemApi + public interface VcnNetworkPolicyListener { /** * Notifies the implementation that the VCN's underlying Network policy has changed. * - * <p>After receiving this callback, implementations MUST poll VcnManager for the updated - * VcnUnderlyingNetworkPolicy via VcnManager#getUnderlyingNetworkPolicy. + * <p>After receiving this callback, implementations should get the current {@link + * VcnNetworkPolicyResult} via {@link #applyVcnNetworkPolicy(NetworkCapabilities, + * LinkProperties)}. */ void onPolicyChanged(); } /** - * Add a listener for VCN-underlying network policy updates. + * Add a listener for VCN-underlying Network policy updates. + * + * <p>A {@link VcnNetworkPolicyListener} is eligible to begin receiving callbacks once it is + * registered. No callbacks are guaranteed upon registration. * * @param executor the Executor that will be used for invoking all calls to the specified * Listener - * @param listener the VcnUnderlyingNetworkPolicyListener to be added + * @param listener the VcnNetworkPolicyListener to be added * @throws SecurityException if the caller does not have permission NETWORK_FACTORY - * @throws IllegalArgumentException if the specified VcnUnderlyingNetworkPolicyListener is - * already registered + * @throws IllegalStateException if the specified VcnNetworkPolicyListener is already registered * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) - public void addVcnUnderlyingNetworkPolicyListener( - @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) { + public void addVcnNetworkPolicyListener( + @NonNull Executor executor, @NonNull VcnNetworkPolicyListener listener) { requireNonNull(executor, "executor must not be null"); requireNonNull(listener, "listener must not be null"); VcnUnderlyingNetworkPolicyListenerBinder binder = new VcnUnderlyingNetworkPolicyListenerBinder(executor, listener); if (REGISTERED_POLICY_LISTENERS.putIfAbsent(listener, binder) != null) { - throw new IllegalArgumentException( - "Attempting to add a listener that is already in use"); + throw new IllegalStateException("listener is already registered with VcnManager"); } try { @@ -207,15 +292,15 @@ public class VcnManager { } /** - * Remove the specified VcnUnderlyingNetworkPolicyListener from VcnManager. + * Remove the specified VcnNetworkPolicyListener from VcnManager. * * <p>If the specified listener is not currently registered, this is a no-op. * - * @param listener the VcnUnderlyingNetworkPolicyListener that will be removed + * @param listener the VcnNetworkPolicyListener that will be removed * @hide */ - public void removeVcnUnderlyingNetworkPolicyListener( - @NonNull VcnUnderlyingNetworkPolicyListener listener) { + @SystemApi + public void removeVcnNetworkPolicyListener(@NonNull VcnNetworkPolicyListener listener) { requireNonNull(listener, "listener must not be null"); VcnUnderlyingNetworkPolicyListenerBinder binder = @@ -232,36 +317,121 @@ public class VcnManager { } /** - * Queries the underlying network policy for a network with the given parameters. + * Applies the network policy for a {@link android.net.Network} with the given parameters. * * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy - * may have changed via {@link VcnUnderlyingNetworkPolicyListener#onPolicyChanged()}, a Network - * Provider MUST poll for the updated Network policy based on that Network's capabilities and - * properties. + * may have changed via {@link VcnNetworkPolicyListener#onPolicyChanged()}, a Network Provider + * MUST poll for the updated Network policy based on that Network's capabilities and properties. * * @param networkCapabilities the NetworkCapabilities to be used in determining the Network - * policy for this Network. - * @param linkProperties the LinkProperties to be used in determining the Network policy for - * this Network. + * policy result for this Network. + * @param linkProperties the LinkProperties to be used in determining the Network policy result + * for this Network. * @throws SecurityException if the caller does not have permission NETWORK_FACTORY - * @return the VcnUnderlyingNetworkPolicy to be used for this Network. + * @return the {@link VcnNetworkPolicyResult} to be used for this Network. * @hide */ @NonNull + @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) - public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy( + public VcnNetworkPolicyResult applyVcnNetworkPolicy( @NonNull NetworkCapabilities networkCapabilities, @NonNull LinkProperties linkProperties) { requireNonNull(networkCapabilities, "networkCapabilities must not be null"); requireNonNull(linkProperties, "linkProperties must not be null"); - try { - return mService.getUnderlyingNetworkPolicy(networkCapabilities, linkProperties); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + final VcnUnderlyingNetworkPolicy policy = + getUnderlyingNetworkPolicy(networkCapabilities, linkProperties); + return new VcnNetworkPolicyResult( + policy.isTeardownRequested(), policy.getMergedNetworkCapabilities()); } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + VCN_STATUS_CODE_NOT_CONFIGURED, + VCN_STATUS_CODE_INACTIVE, + VCN_STATUS_CODE_ACTIVE, + VCN_STATUS_CODE_SAFE_MODE + }) + public @interface VcnStatusCode {} + + /** + * Value indicating that the VCN for the subscription group is not configured, or that the + * callback is not privileged for the subscription group. + * + * @hide + */ + public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0; + + /** + * Value indicating that the VCN for the subscription group is inactive. + * + * <p>A VCN is inactive if a {@link VcnConfig} is present for the subscription group, but the + * provisioning package is not privileged. + * + * @hide + */ + public static final int VCN_STATUS_CODE_INACTIVE = 1; + + /** + * Value indicating that the VCN for the subscription group is active. + * + * <p>A VCN is active if a {@link VcnConfig} is present for the subscription, the provisioning + * package is privileged, and the VCN is not in Safe Mode. In other words, a VCN is considered + * active while it is connecting, fully connected, and disconnecting. + * + * @hide + */ + public static final int VCN_STATUS_CODE_ACTIVE = 2; + + /** + * Value indicating that the VCN for the subscription group is in Safe Mode. + * + * <p>A VCN will be put into Safe Mode if any of the gateway connections were unable to + * establish a connection within a system-determined timeout (while underlying networks were + * available). + * + * @hide + */ + public static final int VCN_STATUS_CODE_SAFE_MODE = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + VCN_ERROR_CODE_INTERNAL_ERROR, + VCN_ERROR_CODE_CONFIG_ERROR, + VCN_ERROR_CODE_NETWORK_ERROR + }) + public @interface VcnErrorCode {} + + /** + * Value indicating that an internal failure occurred in this Gateway Connection. + * + * @hide + */ + public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0; + + /** + * Value indicating that an error with this Gateway Connection's configuration occurred. + * + * <p>For example, this error code will be returned after authentication failures. + * + * @hide + */ + public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1; + + /** + * Value indicating that a Network error occurred with this Gateway Connection. + * + * <p>For example, this error code will be returned if an underlying {@link android.net.Network} + * for this Gateway Connection is lost, or if an error occurs while resolving the connection + * endpoint address. + * + * @hide + */ + public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2; + // TODO: make VcnStatusCallback @SystemApi /** * VcnStatusCallback is the interface for Carrier apps to receive updates for their VCNs. @@ -283,8 +453,36 @@ public class VcnManager { * * <p>A VCN-configuring app may opt to exit safe mode by (re)setting the VCN configuration * via {@link #setVcnConfig(ParcelUuid, VcnConfig)}. + * + * @hide + */ + public void onEnteredSafeMode() {} + + /** + * Invoked when status of the VCN for this callback's subscription group changes. + * + * @param statusCode the code for the status change encountered by this {@link + * VcnStatusCallback}'s subscription group. + */ + public abstract void onVcnStatusChanged(@VcnStatusCode int statusCode); + + /** + * Invoked when a VCN Gateway Connection corresponding to this callback's subscription + * encounters an error. + * + * @param networkCapabilities an array of underlying NetworkCapabilities for the Gateway + * Connection that encountered the error for identification purposes. These will be a + * sorted list with no duplicates, matching one of the {@link + * VcnGatewayConnectionConfig}s set in the {@link VcnConfig} for this subscription + * group. + * @param errorCode {@link VcnErrorCode} to indicate the error that occurred + * @param detail Throwable to provide additional information about the error, or {@code + * null} if none */ - public abstract void onEnteredSafeMode(); + public abstract void onGatewayConnectionError( + @NonNull int[] networkCapabilities, + @VcnErrorCode int errorCode, + @Nullable Throwable detail); } /** @@ -298,6 +496,11 @@ public class VcnManager { * <p>A {@link VcnStatusCallback} will only be invoked if the registering package has carrier * privileges for the specified subscription at the time of invocation. * + * <p>{@link VcnStatusCallback#onVcnStatusChanged(int)} will be invoked on registration with the + * current status for the specified subscription group's VCN. If the registrant is not + * privileged for this subscription group, {@link #VCN_STATUS_CODE_NOT_CONFIGURED} will be + * returned. + * * @param subscriptionGroup The subscription group to match for callbacks * @param executor The {@link Executor} to be used for invoking callbacks * @param callback The VcnStatusCallback to be registered @@ -357,18 +560,17 @@ public class VcnManager { } /** - * Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System - * Server. + * Binder wrapper for added VcnNetworkPolicyListeners to receive signals from System Server. * * @hide */ private static class VcnUnderlyingNetworkPolicyListenerBinder extends IVcnUnderlyingNetworkPolicyListener.Stub { @NonNull private final Executor mExecutor; - @NonNull private final VcnUnderlyingNetworkPolicyListener mListener; + @NonNull private final VcnNetworkPolicyListener mListener; private VcnUnderlyingNetworkPolicyListenerBinder( - Executor executor, VcnUnderlyingNetworkPolicyListener listener) { + Executor executor, VcnNetworkPolicyListener listener) { mExecutor = executor; mListener = listener; } @@ -385,11 +587,12 @@ public class VcnManager { * * @hide */ - private class VcnStatusCallbackBinder extends IVcnStatusCallback.Stub { + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class VcnStatusCallbackBinder extends IVcnStatusCallback.Stub { @NonNull private final Executor mExecutor; @NonNull private final VcnStatusCallback mCallback; - private VcnStatusCallbackBinder( + public VcnStatusCallbackBinder( @NonNull Executor executor, @NonNull VcnStatusCallback callback) { mExecutor = executor; mCallback = callback; @@ -400,5 +603,42 @@ public class VcnManager { Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> mCallback.onEnteredSafeMode())); } + + @Override + public void onVcnStatusChanged(@VcnStatusCode int statusCode) { + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> mCallback.onVcnStatusChanged(statusCode))); + } + + // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling' + @Override + public void onGatewayConnectionError( + @NonNull int[] networkCapabilities, + @VcnErrorCode int errorCode, + @Nullable String exceptionClass, + @Nullable String exceptionMessage) { + final Throwable cause = createThrowableByClassName(exceptionClass, exceptionMessage); + + Binder.withCleanCallingIdentity( + () -> + mExecutor.execute( + () -> + mCallback.onGatewayConnectionError( + networkCapabilities, errorCode, cause))); + } + + private static Throwable createThrowableByClassName( + @Nullable String className, @Nullable String message) { + if (className == null) { + return null; + } + + try { + Class<?> c = Class.forName(className); + return (Throwable) c.getConstructor(String.class).newInstance(message); + } catch (ReflectiveOperationException | ClassCastException e) { + return new RuntimeException(className + ": " + message); + } + } } } diff --git a/core/java/android/net/vcn/VcnNetworkPolicyResult.aidl b/core/java/android/net/vcn/VcnNetworkPolicyResult.aidl new file mode 100644 index 000000000000..3f13abe869da --- /dev/null +++ b/core/java/android/net/vcn/VcnNetworkPolicyResult.aidl @@ -0,0 +1,20 @@ +/* + * 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.net.vcn; + +/** @hide */ +parcelable VcnNetworkPolicyResult; diff --git a/core/java/android/net/vcn/VcnNetworkPolicyResult.java b/core/java/android/net/vcn/VcnNetworkPolicyResult.java new file mode 100644 index 000000000000..5e938200639c --- /dev/null +++ b/core/java/android/net/vcn/VcnNetworkPolicyResult.java @@ -0,0 +1,114 @@ +/* + * 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.net.vcn; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.NetworkCapabilities; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * VcnNetworkPolicyResult represents the Network policy result for a Network transport applying its + * VCN policy via {@link VcnManager#applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}. + * + * <p>Bearers that are bringing up networks capable of acting as a VCN's underlying network should + * query for Network policy results upon any capability changes (e.g. changing of TRUSTED bit), and + * when prompted by VcnManagementService via {@link VcnManager.VcnNetworkPolicyListener}. + * + * @hide + */ +@SystemApi +public final class VcnNetworkPolicyResult implements Parcelable { + private final boolean mIsTearDownRequested; + private final NetworkCapabilities mNetworkCapabilities; + + /** + * Constructs a VcnNetworkPolicyResult with the specified parameters. + * + * @hide + */ + public VcnNetworkPolicyResult( + boolean isTearDownRequested, @NonNull NetworkCapabilities networkCapabilities) { + Objects.requireNonNull(networkCapabilities, "networkCapabilities must be non-null"); + + mIsTearDownRequested = isTearDownRequested; + mNetworkCapabilities = networkCapabilities; + } + + /** + * Returns whether this VCN policy result requires that the underlying Network should be torn + * down. + * + * <p>Upon querying for the current Network policy result, the bearer must check this method, + * and MUST tear down the corresponding Network if it returns true. + */ + public boolean isTeardownRequested() { + return mIsTearDownRequested; + } + + /** + * Returns the NetworkCapabilities that the bearer should be using for the corresponding + * Network. + */ + @NonNull + public NetworkCapabilities getNetworkCapabilities() { + return mNetworkCapabilities; + } + + @Override + public int hashCode() { + return Objects.hash(mIsTearDownRequested, mNetworkCapabilities); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VcnNetworkPolicyResult)) return false; + final VcnNetworkPolicyResult that = (VcnNetworkPolicyResult) o; + + return mIsTearDownRequested == that.mIsTearDownRequested + && mNetworkCapabilities.equals(that.mNetworkCapabilities); + } + + /** {@inheritDoc} */ + @Override + public int describeContents() { + return 0; + } + + /** {@inheritDoc} */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeBoolean(mIsTearDownRequested); + dest.writeParcelable(mNetworkCapabilities, flags); + } + + /** Implement the Parcelable interface */ + public static final @NonNull Creator<VcnNetworkPolicyResult> CREATOR = + new Creator<VcnNetworkPolicyResult>() { + public VcnNetworkPolicyResult createFromParcel(Parcel in) { + return new VcnNetworkPolicyResult(in.readBoolean(), in.readParcelable(null)); + } + + public VcnNetworkPolicyResult[] newArray(int size) { + return new VcnNetworkPolicyResult[size]; + } + }; +} diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java index dd7c86d87ff2..b47d5642419e 100644 --- a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java @@ -33,8 +33,7 @@ import java.util.Objects; * @hide */ public final class VcnUnderlyingNetworkPolicy implements Parcelable { - private final boolean mIsTearDownRequested; - private final NetworkCapabilities mMergedNetworkCapabilities; + private final VcnNetworkPolicyResult mVcnNetworkPolicyResult; /** * Constructs a VcnUnderlyingNetworkPolicy with the specified parameters. @@ -46,8 +45,13 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable { Objects.requireNonNull( mergedNetworkCapabilities, "mergedNetworkCapabilities must be nonnull"); - mIsTearDownRequested = isTearDownRequested; - mMergedNetworkCapabilities = mergedNetworkCapabilities; + mVcnNetworkPolicyResult = + new VcnNetworkPolicyResult(isTearDownRequested, mergedNetworkCapabilities); + } + + private VcnUnderlyingNetworkPolicy(@NonNull VcnNetworkPolicyResult vcnNetworkPolicyResult) { + this.mVcnNetworkPolicyResult = + Objects.requireNonNull(vcnNetworkPolicyResult, "vcnNetworkPolicyResult"); } /** @@ -55,7 +59,7 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable { * be torn down. */ public boolean isTeardownRequested() { - return mIsTearDownRequested; + return mVcnNetworkPolicyResult.isTeardownRequested(); } /** @@ -64,12 +68,12 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable { */ @NonNull public NetworkCapabilities getMergedNetworkCapabilities() { - return mMergedNetworkCapabilities; + return mVcnNetworkPolicyResult.getNetworkCapabilities(); } @Override public int hashCode() { - return Objects.hash(mIsTearDownRequested, mMergedNetworkCapabilities); + return Objects.hash(mVcnNetworkPolicyResult); } @Override @@ -78,8 +82,7 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable { if (!(o instanceof VcnUnderlyingNetworkPolicy)) return false; final VcnUnderlyingNetworkPolicy that = (VcnUnderlyingNetworkPolicy) o; - return mIsTearDownRequested == that.mIsTearDownRequested - && mMergedNetworkCapabilities.equals(that.mMergedNetworkCapabilities); + return mVcnNetworkPolicyResult.equals(that.mVcnNetworkPolicyResult); } /** {@inheritDoc} */ @@ -91,16 +94,14 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable { /** {@inheritDoc} */ @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeBoolean(mIsTearDownRequested); - dest.writeParcelable(mMergedNetworkCapabilities, flags); + dest.writeParcelable(mVcnNetworkPolicyResult, flags); } /** Implement the Parcelable interface */ public static final @NonNull Creator<VcnUnderlyingNetworkPolicy> CREATOR = new Creator<VcnUnderlyingNetworkPolicy>() { public VcnUnderlyingNetworkPolicy createFromParcel(Parcel in) { - return new VcnUnderlyingNetworkPolicy( - in.readBoolean(), in.readParcelable(null)); + return new VcnUnderlyingNetworkPolicy(in.readParcelable(null)); } public VcnUnderlyingNetworkPolicy[] newArray(int size) { diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java b/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java new file mode 100644 index 000000000000..a97563724e50 --- /dev/null +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkSpecifier.java @@ -0,0 +1,125 @@ +/* + * 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.net.vcn; + +import android.annotation.NonNull; +import android.net.NetworkSpecifier; +import android.net.TelephonyNetworkSpecifier; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.internal.util.ArrayUtils; + +import java.util.Arrays; +import java.util.Objects; + +/** + * NetworkSpecifier object for VCN underlying network requests. + * + * <p>This matches any underlying network with the appropriate subIds. + * + * @hide + */ +public final class VcnUnderlyingNetworkSpecifier extends NetworkSpecifier implements Parcelable { + @NonNull private final int[] mSubIds; + + /** + * Builds a new VcnUnderlyingNetworkSpecifier with the given list of subIds + * + * @hide + */ + public VcnUnderlyingNetworkSpecifier(@NonNull int[] subIds) { + mSubIds = Objects.requireNonNull(subIds, "subIds were null"); + } + + /** + * Retrieves the list of subIds supported by this VcnUnderlyingNetworkSpecifier + * + * @hide + */ + @NonNull + @VisibleForTesting(visibility = Visibility.PRIVATE) + public int[] getSubIds() { + return mSubIds; + } + + public static final @NonNull Creator<VcnUnderlyingNetworkSpecifier> CREATOR = + new Creator<VcnUnderlyingNetworkSpecifier>() { + @Override + public VcnUnderlyingNetworkSpecifier createFromParcel(Parcel in) { + int[] subIds = in.createIntArray(); + return new VcnUnderlyingNetworkSpecifier(subIds); + } + + @Override + public VcnUnderlyingNetworkSpecifier[] newArray(int size) { + return new VcnUnderlyingNetworkSpecifier[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeIntArray(mSubIds); + } + + @Override + public int hashCode() { + return Arrays.hashCode(mSubIds); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof VcnUnderlyingNetworkSpecifier)) { + return false; + } + + VcnUnderlyingNetworkSpecifier lhs = (VcnUnderlyingNetworkSpecifier) obj; + return Arrays.equals(mSubIds, lhs.mSubIds); + } + + @Override + public String toString() { + return new StringBuilder() + .append("VcnUnderlyingNetworkSpecifier [") + .append("mSubIds = ").append(Arrays.toString(mSubIds)) + .append("]") + .toString(); + } + + /** @hide */ + @Override + public boolean canBeSatisfiedBy(NetworkSpecifier other) { + if (other instanceof TelephonyNetworkSpecifier) { + return ArrayUtils.contains( + mSubIds, ((TelephonyNetworkSpecifier) other).getSubscriptionId()); + } + // TODO(b/180140053): Allow matching against WifiNetworkAgentSpecifier + + // MatchAllNetworkSpecifier matched in NetworkCapabilities. + return equals(other); + } +} diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtils.java new file mode 100644 index 000000000000..6bbc6b1e8218 --- /dev/null +++ b/core/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtils.java @@ -0,0 +1,74 @@ +/* + * 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.net.vcn.persistablebundleutils; + +import static com.android.internal.annotations.VisibleForTesting.Visibility; + +import android.annotation.NonNull; +import android.net.InetAddresses; +import android.net.ipsec.ike.IkeTrafficSelector; +import android.os.PersistableBundle; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Objects; + +/** + * Provides utility methods to convert IkeTrafficSelector to/from PersistableBundle. + * + * @hide + */ +@VisibleForTesting(visibility = Visibility.PRIVATE) +public final class IkeTrafficSelectorUtils { + private static final String START_PORT_KEY = "START_PORT_KEY"; + private static final String END_PORT_KEY = "END_PORT_KEY"; + private static final String START_ADDRESS_KEY = "START_ADDRESS_KEY"; + private static final String END_ADDRESS_KEY = "END_ADDRESS_KEY"; + + /** Constructs an IkeTrafficSelector by deserializing a PersistableBundle. */ + @NonNull + public static IkeTrafficSelector fromPersistableBundle(@NonNull PersistableBundle in) { + Objects.requireNonNull(in, "PersistableBundle was null"); + + final int startPort = in.getInt(START_PORT_KEY); + final int endPort = in.getInt(END_PORT_KEY); + + final String startingAddress = in.getString(START_ADDRESS_KEY); + final String endingAddress = in.getString(END_ADDRESS_KEY); + Objects.requireNonNull(startingAddress, "startAddress was null"); + Objects.requireNonNull(startingAddress, "endAddress was null"); + + return new IkeTrafficSelector( + startPort, + endPort, + InetAddresses.parseNumericAddress(startingAddress), + InetAddresses.parseNumericAddress(endingAddress)); + } + + /** Serializes an IkeTrafficSelector to a PersistableBundle. */ + @NonNull + public static PersistableBundle toPersistableBundle(@NonNull IkeTrafficSelector ts) { + final PersistableBundle result = new PersistableBundle(); + + result.putInt(START_PORT_KEY, ts.startPort); + result.putInt(END_PORT_KEY, ts.endPort); + result.putString(START_ADDRESS_KEY, ts.startingAddress.getHostAddress()); + result.putString(END_ADDRESS_KEY, ts.endingAddress.getHostAddress()); + + return result; + } +} diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index bf229e0b24df..2b6f336848c3 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -47,6 +47,7 @@ public abstract class BatteryConsumer { POWER_COMPONENT_SYSTEM_SERVICES, POWER_COMPONENT_SENSORS, POWER_COMPONENT_GNSS, + POWER_COMPONENT_WIFI, POWER_COMPONENT_WAKELOCK, POWER_COMPONENT_SCREEN, POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS, @@ -66,6 +67,7 @@ public abstract class BatteryConsumer { public static final int POWER_COMPONENT_MOBILE_RADIO = 8; public static final int POWER_COMPONENT_SENSORS = 9; public static final int POWER_COMPONENT_GNSS = 10; + public static final int POWER_COMPONENT_WIFI = 11; public static final int POWER_COMPONENT_WAKELOCK = 12; public static final int POWER_COMPONENT_SCREEN = 13; // Power that is re-attributed to other battery consumers. For example, for System Server @@ -94,6 +96,7 @@ public abstract class BatteryConsumer { TIME_COMPONENT_MOBILE_RADIO, TIME_COMPONENT_SENSORS, TIME_COMPONENT_GNSS, + TIME_COMPONENT_WIFI, TIME_COMPONENT_WAKELOCK, TIME_COMPONENT_SCREEN, }) @@ -112,6 +115,7 @@ public abstract class BatteryConsumer { public static final int TIME_COMPONENT_MOBILE_RADIO = 8; public static final int TIME_COMPONENT_SENSORS = 9; public static final int TIME_COMPONENT_GNSS = 10; + public static final int TIME_COMPONENT_WIFI = 11; public static final int TIME_COMPONENT_WAKELOCK = 12; public static final int TIME_COMPONENT_SCREEN = 13; @@ -130,7 +134,7 @@ public abstract class BatteryConsumer { * Total power consumed by this consumer, in mAh. */ public double getConsumedPower() { - return mPowerComponents.getTotalPowerConsumed(); + return mPowerComponents.getTotalConsumedPower(); } /** diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 01a89017ab6c..6901df7508ab 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1671,7 +1671,7 @@ public abstract class BatteryStats implements Parcelable { public char batteryVoltage; // The charge of the battery in micro-Ampere-hours. - public int batteryChargeUAh; + public int batteryChargeUah; public double modemRailChargeMah; public double wifiRailChargeMah; @@ -1884,7 +1884,7 @@ public abstract class BatteryStats implements Parcelable { bat = (((int)batteryTemperature)&0xffff) | ((((int)batteryVoltage)<<16)&0xffff0000); dest.writeInt(bat); - dest.writeInt(batteryChargeUAh); + dest.writeInt(batteryChargeUah); dest.writeDouble(modemRailChargeMah); dest.writeDouble(wifiRailChargeMah); dest.writeInt(states); @@ -1916,7 +1916,7 @@ public abstract class BatteryStats implements Parcelable { int bat2 = src.readInt(); batteryTemperature = (short)(bat2&0xffff); batteryVoltage = (char)((bat2>>16)&0xffff); - batteryChargeUAh = src.readInt(); + batteryChargeUah = src.readInt(); modemRailChargeMah = src.readDouble(); wifiRailChargeMah = src.readDouble(); states = src.readInt(); @@ -1959,7 +1959,7 @@ public abstract class BatteryStats implements Parcelable { batteryPlugType = 0; batteryTemperature = 0; batteryVoltage = 0; - batteryChargeUAh = 0; + batteryChargeUah = 0; modemRailChargeMah = 0; wifiRailChargeMah = 0; states = 0; @@ -1991,7 +1991,7 @@ public abstract class BatteryStats implements Parcelable { batteryPlugType = o.batteryPlugType; batteryTemperature = o.batteryTemperature; batteryVoltage = o.batteryVoltage; - batteryChargeUAh = o.batteryChargeUAh; + batteryChargeUah = o.batteryChargeUah; modemRailChargeMah = o.modemRailChargeMah; wifiRailChargeMah = o.wifiRailChargeMah; states = o.states; @@ -2025,7 +2025,7 @@ public abstract class BatteryStats implements Parcelable { && batteryPlugType == o.batteryPlugType && batteryTemperature == o.batteryTemperature && batteryVoltage == o.batteryVoltage - && batteryChargeUAh == o.batteryChargeUAh + && batteryChargeUah == o.batteryChargeUah && modemRailChargeMah == o.modemRailChargeMah && wifiRailChargeMah == o.wifiRailChargeMah && states == o.states @@ -2179,12 +2179,6 @@ public abstract class BatteryStats implements Parcelable { public abstract void finishIteratingHistoryLocked(); - public abstract boolean startIteratingOldHistoryLocked(); - - public abstract boolean getNextOldHistoryLocked(HistoryItem out); - - public abstract void finishIteratingOldHistoryLocked(); - /** * Return the base time offset for the battery history. */ @@ -2859,6 +2853,11 @@ public abstract class BatteryStats implements Parcelable { public abstract boolean getIsOnBattery(); /** + * Returns the timestamp of when battery stats collection started, in microseconds. + */ + public abstract long getStatsStartRealtime(); + + /** * Returns a SparseArray containing the statistics for each uid. */ @UnsupportedAppUsage @@ -6520,7 +6519,7 @@ public abstract class BatteryStats implements Parcelable { item.append(checkin ? ",Bv=" : " volt="); item.append(oldVolt); } - final int chargeMAh = rec.batteryChargeUAh / 1000; + final int chargeMAh = rec.batteryChargeUah / 1000; if (oldChargeMAh != chargeMAh) { oldChargeMAh = chargeMAh; item.append(checkin ? ",Bcc=" : " charge="); @@ -7056,24 +7055,6 @@ public abstract class BatteryStats implements Parcelable { finishIteratingHistoryLocked(); } } - - if (startIteratingOldHistoryLocked()) { - try { - final HistoryItem rec = new HistoryItem(); - pw.println("Old battery History:"); - HistoryPrinter hprinter = new HistoryPrinter(); - long baseTime = -1; - while (getNextOldHistoryLocked(rec)) { - if (baseTime < 0) { - baseTime = rec.time; - } - hprinter.printNextItem(pw, rec, baseTime, false, (flags&DUMP_VERBOSE) != 0); - } - pw.println(); - } finally { - finishIteratingOldHistoryLocked(); - } - } } if (filtering && (flags&(DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) { diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 305815ff9e51..de7b88575e98 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -17,9 +17,12 @@ package android.os; import android.annotation.NonNull; -import android.annotation.SuppressLint; +import android.util.Range; import android.util.SparseArray; +import com.android.internal.os.BatteryStatsHistory; +import com.android.internal.os.BatteryStatsHistoryIterator; + import java.util.ArrayList; import java.util.List; @@ -31,35 +34,63 @@ import java.util.List; public final class BatteryUsageStats implements Parcelable { private final double mConsumedPower; private final int mDischargePercentage; + private final long mStatsStartRealtimeMs; + private final double mDischargedPowerLowerBound; + private final double mDischargedPowerUpperBound; private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers; private final ArrayList<SystemBatteryConsumer> mSystemBatteryConsumers; private final ArrayList<UserBatteryConsumer> mUserBatteryConsumers; + private final Parcel mHistoryBuffer; + private final List<BatteryStats.HistoryTag> mHistoryTagPool; private BatteryUsageStats(@NonNull Builder builder) { - mConsumedPower = builder.mConsumedPower; + mStatsStartRealtimeMs = builder.mStatsStartRealtimeMs; mDischargePercentage = builder.mDischargePercentage; + mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah; + mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah; + mHistoryBuffer = builder.mHistoryBuffer; + mHistoryTagPool = builder.mHistoryTagPool; + + double totalPower = 0; - int uidBatteryConsumerCount = builder.mUidBatteryConsumerBuilders.size(); + final int uidBatteryConsumerCount = builder.mUidBatteryConsumerBuilders.size(); mUidBatteryConsumers = new ArrayList<>(uidBatteryConsumerCount); for (int i = 0; i < uidBatteryConsumerCount; i++) { - UidBatteryConsumer.Builder uidBatteryConsumerBuilder = + final UidBatteryConsumer.Builder uidBatteryConsumerBuilder = builder.mUidBatteryConsumerBuilders.valueAt(i); if (!uidBatteryConsumerBuilder.isExcludedFromBatteryUsageStats()) { - mUidBatteryConsumers.add(uidBatteryConsumerBuilder.build()); + final UidBatteryConsumer consumer = uidBatteryConsumerBuilder.build(); + totalPower += consumer.getConsumedPower(); + mUidBatteryConsumers.add(consumer); } } - int systemBatteryConsumerCount = builder.mSystemBatteryConsumerBuilders.size(); + final int systemBatteryConsumerCount = builder.mSystemBatteryConsumerBuilders.size(); mSystemBatteryConsumers = new ArrayList<>(systemBatteryConsumerCount); for (int i = 0; i < systemBatteryConsumerCount; i++) { - mSystemBatteryConsumers.add(builder.mSystemBatteryConsumerBuilders.valueAt(i).build()); + final SystemBatteryConsumer consumer = + builder.mSystemBatteryConsumerBuilders.valueAt(i).build(); + totalPower += consumer.getConsumedPower(); + mSystemBatteryConsumers.add(consumer); } - int userBatteryConsumerCount = builder.mUserBatteryConsumerBuilders.size(); + final int userBatteryConsumerCount = builder.mUserBatteryConsumerBuilders.size(); mUserBatteryConsumers = new ArrayList<>(userBatteryConsumerCount); for (int i = 0; i < userBatteryConsumerCount; i++) { - mUserBatteryConsumers.add(builder.mUserBatteryConsumerBuilders.valueAt(i).build()); + final UserBatteryConsumer consumer = + builder.mUserBatteryConsumerBuilders.valueAt(i).build(); + totalPower += consumer.getConsumedPower(); + mUserBatteryConsumers.add(consumer); } + + mConsumedPower = totalPower; + } + + /** + * Timestamp of the latest battery stats reset, in milliseconds. + */ + public long getStatsStartRealtime() { + return mStatsStartRealtimeMs; } /** @@ -71,6 +102,14 @@ public final class BatteryUsageStats implements Parcelable { } /** + * Returns the discharged power since BatteryStats were last reset, in mAh as an estimated + * range. + */ + public Range<Double> getDischargedPowerRange() { + return Range.create(mDischargedPowerLowerBound, mDischargedPowerUpperBound); + } + + /** * Total amount of battery charge drained since BatteryStats reset (e.g. due to being fully * charged), in mAh */ @@ -93,29 +132,88 @@ public final class BatteryUsageStats implements Parcelable { return mUserBatteryConsumers; } + /** + * Returns an iterator for {@link android.os.BatteryStats.HistoryItem}'s. + */ + @NonNull + public BatteryStatsHistoryIterator iterateBatteryStatsHistory() { + if (mHistoryBuffer == null) { + throw new IllegalStateException( + "Battery history was not requested in the BatteryUsageStatsQuery"); + } + return new BatteryStatsHistoryIterator(new BatteryStatsHistory(mHistoryBuffer), + mHistoryTagPool); + } + @Override public int describeContents() { return 0; } private BatteryUsageStats(@NonNull Parcel source) { + mStatsStartRealtimeMs = source.readLong(); + mConsumedPower = source.readDouble(); + mDischargePercentage = source.readInt(); + mDischargedPowerLowerBound = source.readDouble(); + mDischargedPowerUpperBound = source.readDouble(); mUidBatteryConsumers = new ArrayList<>(); source.readParcelableList(mUidBatteryConsumers, getClass().getClassLoader()); mSystemBatteryConsumers = new ArrayList<>(); source.readParcelableList(mSystemBatteryConsumers, getClass().getClassLoader()); mUserBatteryConsumers = new ArrayList<>(); source.readParcelableList(mUserBatteryConsumers, getClass().getClassLoader()); - mConsumedPower = source.readDouble(); - mDischargePercentage = source.readInt(); + if (source.readBoolean()) { + mHistoryBuffer = Parcel.obtain(); + mHistoryBuffer.setDataSize(0); + mHistoryBuffer.setDataPosition(0); + + int historyBufferSize = source.readInt(); + int curPos = source.dataPosition(); + mHistoryBuffer.appendFrom(source, curPos, historyBufferSize); + source.setDataPosition(curPos + historyBufferSize); + + int historyTagCount = source.readInt(); + mHistoryTagPool = new ArrayList<>(historyTagCount); + for (int i = 0; i < historyTagCount; i++) { + BatteryStats.HistoryTag tag = new BatteryStats.HistoryTag(); + tag.string = source.readString(); + tag.uid = source.readInt(); + tag.poolIdx = source.readInt(); + mHistoryTagPool.add(tag); + } + } else { + mHistoryBuffer = null; + mHistoryTagPool = null; + } } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mStatsStartRealtimeMs); + dest.writeDouble(mConsumedPower); + dest.writeInt(mDischargePercentage); + dest.writeDouble(mDischargedPowerLowerBound); + dest.writeDouble(mDischargedPowerUpperBound); dest.writeParcelableList(mUidBatteryConsumers, flags); dest.writeParcelableList(mSystemBatteryConsumers, flags); dest.writeParcelableList(mUserBatteryConsumers, flags); - dest.writeDouble(mConsumedPower); - dest.writeInt(mDischargePercentage); + if (mHistoryBuffer != null) { + dest.writeBoolean(true); + + final int historyBufferSize = mHistoryBuffer.dataSize(); + dest.writeInt(historyBufferSize); + dest.appendFrom(mHistoryBuffer, 0, historyBufferSize); + + dest.writeInt(mHistoryTagPool.size()); + for (int i = mHistoryTagPool.size() - 1; i >= 0; i--) { + final BatteryStats.HistoryTag tag = mHistoryTagPool.get(i); + dest.writeString(tag.string); + dest.writeInt(tag.uid); + dest.writeInt(tag.poolIdx); + } + } else { + dest.writeBoolean(false); + } } @NonNull @@ -135,14 +233,18 @@ public final class BatteryUsageStats implements Parcelable { public static final class Builder { private final int mCustomPowerComponentCount; private final int mCustomTimeComponentCount; - private double mConsumedPower; + private long mStatsStartRealtimeMs; private int mDischargePercentage; + private double mDischargedPowerLowerBoundMah; + private double mDischargedPowerUpperBoundMah; private final SparseArray<UidBatteryConsumer.Builder> mUidBatteryConsumerBuilders = new SparseArray<>(); private final SparseArray<SystemBatteryConsumer.Builder> mSystemBatteryConsumerBuilders = new SparseArray<>(); private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders = new SparseArray<>(); + private Parcel mHistoryBuffer; + private List<BatteryStats.HistoryTag> mHistoryTagPool; public Builder(int customPowerComponentCount, int customTimeComponentCount) { mCustomPowerComponentCount = customPowerComponentCount; @@ -158,10 +260,17 @@ public final class BatteryUsageStats implements Parcelable { } /** + * Sets the timestamp of the latest battery stats reset, in milliseconds. + */ + public Builder setStatsStartRealtime(long statsStartRealtimeMs) { + mStatsStartRealtimeMs = statsStartRealtimeMs; + return this; + } + + /** * Sets the battery discharge amount since BatteryStats reset as percentage of the full * charge. */ - @SuppressLint("PercentageInt") // See b/174188159 @NonNull public Builder setDischargePercentage(int dischargePercentage) { mDischargePercentage = dischargePercentage; @@ -169,11 +278,24 @@ public final class BatteryUsageStats implements Parcelable { } /** - * Sets the battery discharge amount since BatteryStats reset, in mAh. + * Sets the estimated battery discharge range. + */ + @NonNull + public Builder setDischargedPowerRange(double dischargedPowerLowerBoundMah, + double dischargedPowerUpperBoundMah) { + mDischargedPowerLowerBoundMah = dischargedPowerLowerBoundMah; + mDischargedPowerUpperBoundMah = dischargedPowerUpperBoundMah; + return this; + } + + /** + * Sets the parceled recent history. */ @NonNull - public Builder setConsumedPower(double consumedPower) { - mConsumedPower = consumedPower; + public Builder setBatteryHistory(Parcel historyBuffer, + List<BatteryStats.HistoryTag> historyTagPool) { + mHistoryBuffer = historyBuffer; + mHistoryTagPool = historyTagPool; return this; } diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java index 5b5fe1d15a82..9518bf197fa0 100644 --- a/core/java/android/os/BatteryUsageStatsQuery.java +++ b/core/java/android/os/BatteryUsageStatsQuery.java @@ -40,6 +40,7 @@ public final class BatteryUsageStatsQuery implements Parcelable { */ @IntDef(flag = true, prefix = { "FLAG_BATTERY_USAGE_STATS_" }, value = { FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL, + FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY, }) @Retention(RetentionPolicy.SOURCE) public @interface BatteryUsageStatsFlags {} @@ -53,6 +54,12 @@ public final class BatteryUsageStatsQuery implements Parcelable { */ public static final int FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL = 1; + /** + * Indicates that battery history should be included in the BatteryUsageStats. + * @hide + */ + public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY = 2; + private final int mFlags; @NonNull private final int[] mUserIds; @@ -79,6 +86,14 @@ public final class BatteryUsageStatsQuery implements Parcelable { return mUserIds; } + /** + * Returns true if the power calculations must be based on the PowerProfile constants, + * even if measured energy data is available. + */ + public boolean shouldForceUsePowerProfileModel() { + return (mFlags & FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL) != 0; + } + private BatteryUsageStatsQuery(Parcel in) { mFlags = in.readInt(); mUserIds = new int[in.readInt()]; @@ -138,10 +153,10 @@ public final class BatteryUsageStatsQuery implements Parcelable { } /** - * Sets flags to modify the behavior of {@link BatteryStatsManager#getBatteryUsageStats}. + * Requests that battery history be included in the BatteryUsageStats. */ - public Builder setFlags(@BatteryUsageStatsFlags int flags) { - mFlags = flags; + public Builder includeBatteryHistory() { + mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY; return this; } diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index 305c686f8657..73bb8d566500 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -25,6 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressAutoDoc; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.WorkerThread; import android.app.ActivityManager; import android.content.Context; import android.util.Log; @@ -41,7 +42,15 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.Executor; -/** Class that provides a privileged API to capture and consume bugreports. */ +/** + * Class that provides a privileged API to capture and consume bugreports. + * + * <p>This class may only be used by apps that currently have carrier privileges (see {@link + * android.telephony.TelephonyManager#hasCarrierPrivileges}) on an active SIM or priv-apps + * explicitly allowed by the device manufacturer. + * + * <p>Only one bugreport can be generated by the system at a time. + */ @SystemService(Context.BUGREPORT_SERVICE) public final class BugreportManager { @@ -56,7 +65,15 @@ public final class BugreportManager { mBinder = binder; } - /** An interface describing the callback for bugreport progress and status. */ + /** + * An interface describing the callback for bugreport progress and status. + * + * <p>Callers will receive {@link #onProgress} calls as the bugreport progresses, followed by a + * terminal call to either {@link #onFinished} or {@link #onError}. + * + * <p>If an issue is encountered while starting the bugreport asynchronously, callers will + * receive an {@link #onError} call without any {@link #onProgress} callbacks. + */ public abstract static class BugreportCallback { /** * Possible error codes taking a bugreport can encounter. @@ -75,15 +92,18 @@ public final class BugreportManager { }) public @interface BugreportErrorCode {} - /** The input options were invalid */ + /** + * The input options were invalid. For example, the destination file the app provided could + * not be written by the system. + */ public static final int BUGREPORT_ERROR_INVALID_INPUT = IDumpstateListener.BUGREPORT_ERROR_INVALID_INPUT; - /** A runtime error occurred */ + /** A runtime error occurred. */ public static final int BUGREPORT_ERROR_RUNTIME = IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR; - /** User denied consent to share the bugreport */ + /** User denied consent to share the bugreport. */ public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT = IDumpstateListener.BUGREPORT_ERROR_USER_DENIED_CONSENT; @@ -149,6 +169,7 @@ public final class BugreportManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.DUMP) + @WorkerThread public void startBugreport( @NonNull ParcelFileDescriptor bugreportFd, @Nullable ParcelFileDescriptor screenshotFd, @@ -222,6 +243,7 @@ public final class BugreportManager { * @param callback callback for progress and status updates. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @WorkerThread public void startConnectivityBugreport( @NonNull ParcelFileDescriptor bugreportFd, @NonNull @CallbackExecutor Executor executor, @@ -247,6 +269,7 @@ public final class BugreportManager { * @throws SecurityException if trying to cancel another app's bugreport in progress */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @WorkerThread public void cancelBugreport() { try { mBinder.cancelBugreport(-1 /* callingUid */, mContext.getOpPackageName()); diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 33deddae01b6..874add5cdbd8 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -24,7 +24,6 @@ import android.net.Network; import android.net.NetworkStats; import android.net.RouteInfo; import android.net.UidRange; -import android.os.INetworkActivityListener; /** * @hide @@ -294,25 +293,6 @@ interface INetworkManagementService @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) boolean isBandwidthControlEnabled(); - /** - * Sets idletimer for an interface. - * - * This either initializes a new idletimer or increases its - * reference-counting if an idletimer already exists for given - * {@code iface}. - * - * {@code type} is the type of the interface, such as TYPE_MOBILE. - * - * Every {@code addIdleTimer} should be paired with a - * {@link removeIdleTimer} to cleanup when the network disconnects. - */ - void addIdleTimer(String iface, int timeout, int type); - - /** - * Removes idletimer for an interface. - */ - void removeIdleTimer(String iface); - void setFirewallEnabled(boolean enabled); boolean isFirewallEnabled(); void setFirewallInterfaceRule(String iface, boolean allow); @@ -320,21 +300,6 @@ interface INetworkManagementService void setFirewallUidRules(int chain, in int[] uids, in int[] rules); void setFirewallChainEnabled(int chain, boolean enable); - /** - * Start listening for mobile activity state changes. - */ - void registerNetworkActivityListener(INetworkActivityListener listener); - - /** - * Stop listening for mobile activity state changes. - */ - void unregisterNetworkActivityListener(INetworkActivityListener listener); - - /** - * Check whether the mobile radio is currently active. - */ - boolean isNetworkActive(); - void addLegacyRouteForNetId(int netId, in RouteInfo routeInfo, int uid); /** diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 7437e037fa3e..087568defb27 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -113,7 +113,7 @@ interface IUserManager { boolean hasBadge(int userId); boolean isUserUnlocked(int userId); boolean isUserRunning(int userId); - boolean isUserForeground(); + boolean isUserForeground(int userId); boolean isUserNameSet(int userId); boolean hasRestrictedProfiles(); boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags); diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index ac2328504f74..d239b23b6ff6 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -29,38 +29,38 @@ class PowerComponents { public static final int CUSTOM_TIME_COMPONENT_OFFSET = BatteryConsumer.TIME_COMPONENT_COUNT - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID; - private final double mTotalPowerConsumed; - private final double[] mPowerComponents; - private final long[] mTimeComponents; + private final double mTotalConsumedPowerMah; + private final double[] mPowerComponentsMah; + private final long[] mTimeComponentsMs; private final int mCustomPowerComponentCount; PowerComponents(@NonNull Builder builder) { mCustomPowerComponentCount = builder.mCustomPowerComponentCount; - mPowerComponents = builder.mPowerComponents; - mTimeComponents = builder.mTimeComponents; - mTotalPowerConsumed = builder.getTotalPower(); + mPowerComponentsMah = builder.mPowerComponentsMah; + mTimeComponentsMs = builder.mTimeComponentsMs; + mTotalConsumedPowerMah = builder.getTotalPower(); } PowerComponents(@NonNull Parcel source) { - mTotalPowerConsumed = source.readDouble(); + mTotalConsumedPowerMah = source.readDouble(); mCustomPowerComponentCount = source.readInt(); - mPowerComponents = source.createDoubleArray(); - mTimeComponents = source.createLongArray(); + mPowerComponentsMah = source.createDoubleArray(); + mTimeComponentsMs = source.createLongArray(); } /** Writes contents to Parcel */ void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeDouble(mTotalPowerConsumed); + dest.writeDouble(mTotalConsumedPowerMah); dest.writeInt(mCustomPowerComponentCount); - dest.writeDoubleArray(mPowerComponents); - dest.writeLongArray(mTimeComponents); + dest.writeDoubleArray(mPowerComponentsMah); + dest.writeLongArray(mTimeComponentsMs); } /** * Total power consumed by this consumer, in mAh. */ - public double getTotalPowerConsumed() { - return mTotalPowerConsumed; + public double getTotalConsumedPower() { + return mTotalConsumedPowerMah; } /** @@ -76,7 +76,7 @@ class PowerComponents { "Unsupported power component ID: " + componentId); } try { - return mPowerComponents[componentId]; + return mPowerComponentsMah[componentId]; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException("Unsupported power component ID: " + componentId); } @@ -92,7 +92,7 @@ class PowerComponents { if (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID && componentId < BatteryConsumer.LAST_CUSTOM_POWER_COMPONENT_ID) { try { - return mPowerComponents[CUSTOM_POWER_COMPONENT_OFFSET + componentId]; + return mPowerComponentsMah[CUSTOM_POWER_COMPONENT_OFFSET + componentId]; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException( "Unsupported custom power component ID: " + componentId); @@ -116,7 +116,7 @@ class PowerComponents { "Unsupported time component ID: " + componentId); } try { - return mTimeComponents[componentId]; + return mTimeComponentsMs[componentId]; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException("Unsupported power component ID: " + componentId); } @@ -134,7 +134,7 @@ class PowerComponents { "Unsupported custom time component ID: " + componentId); } try { - return mTimeComponents[CUSTOM_TIME_COMPONENT_OFFSET + componentId]; + return mTimeComponentsMs[CUSTOM_TIME_COMPONENT_OFFSET + componentId]; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException( "Unsupported custom time component ID: " + componentId); @@ -145,16 +145,16 @@ class PowerComponents { * Builder for PowerComponents. */ static final class Builder { - private final double[] mPowerComponents; + private final double[] mPowerComponentsMah; private final int mCustomPowerComponentCount; - private final long[] mTimeComponents; + private final long[] mTimeComponentsMs; Builder(int customPowerComponentCount, int customTimeComponentCount) { mCustomPowerComponentCount = customPowerComponentCount; int powerComponentCount = BatteryConsumer.POWER_COMPONENT_COUNT + customPowerComponentCount; - mPowerComponents = new double[powerComponentCount]; - mTimeComponents = + mPowerComponentsMah = new double[powerComponentCount]; + mTimeComponentsMs = new long[BatteryConsumer.TIME_COMPONENT_COUNT + customTimeComponentCount]; } @@ -173,7 +173,7 @@ class PowerComponents { "Unsupported power component ID: " + componentId); } try { - mPowerComponents[componentId] = componentPower; + mPowerComponentsMah[componentId] = componentPower; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException( "Unsupported power component ID: " + componentId); @@ -192,7 +192,8 @@ class PowerComponents { if (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID && componentId < BatteryConsumer.LAST_CUSTOM_POWER_COMPONENT_ID) { try { - mPowerComponents[CUSTOM_POWER_COMPONENT_OFFSET + componentId] = componentPower; + mPowerComponentsMah[CUSTOM_POWER_COMPONENT_OFFSET + componentId] = + componentPower; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException( "Unsupported custom power component ID: " + componentId); @@ -219,7 +220,7 @@ class PowerComponents { "Unsupported time component ID: " + componentId); } try { - mTimeComponents[componentId] = componentUsageDurationMillis; + mTimeComponentsMs[componentId] = componentUsageDurationMillis; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException( "Unsupported time component ID: " + componentId); @@ -241,7 +242,7 @@ class PowerComponents { "Unsupported custom time component ID: " + componentId); } try { - mTimeComponents[CUSTOM_TIME_COMPONENT_OFFSET + componentId] = + mTimeComponentsMs[CUSTOM_TIME_COMPONENT_OFFSET + componentId] = componentUsageDurationMillis; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalArgumentException( @@ -251,11 +252,11 @@ class PowerComponents { } public void addPowerAndDuration(Builder other) { - for (int i = 0; i < mPowerComponents.length; i++) { - mPowerComponents[i] += other.mPowerComponents[i]; + for (int i = 0; i < mPowerComponentsMah.length; i++) { + mPowerComponentsMah[i] += other.mPowerComponentsMah[i]; } - for (int i = 0; i < mTimeComponents.length; i++) { - mTimeComponents[i] += other.mTimeComponents[i]; + for (int i = 0; i < mTimeComponentsMs.length; i++) { + mTimeComponentsMs[i] += other.mTimeComponentsMs[i]; } } @@ -264,11 +265,11 @@ class PowerComponents { * by the time the {@code build()} method is called. */ public double getTotalPower() { - double totalPower = 0; - for (int i = mPowerComponents.length - 1; i >= 0; i--) { - totalPower += mPowerComponents[i]; + double totalPowerMah = 0; + for (int i = mPowerComponentsMah.length - 1; i >= 0; i--) { + totalPowerMah += mPowerComponentsMah[i]; } - return totalPower; + return totalPowerMah; } /** diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 3774fb595680..e5163d83de69 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1629,6 +1629,12 @@ public final class PowerManager { * <p> * Requires the {@link android.Manifest.permission#REBOOT} permission. * </p> + * <p> + * If the {@code reason} string contains ",quiescent", then the screen stays off during reboot + * and is not turned on again until the user triggers the device to wake up (for example, + * by pressing the power key). + * This behavior applies to Android TV devices launched on Android 11 (API level 30) or higher. + * </p> * * @param reason code to pass to the kernel (e.g., "recovery") to * request special boot modes, or null. @@ -1875,6 +1881,10 @@ public final class PowerManager { * Returns the current battery saver control mode. Values it may return are defined in * AutoPowerSaveModeTriggers. Note that this is a global device state, not a per user setting. * + * <p>Note: Prior to Android version {@link Build.VERSION_CODES#S}, any app calling this method + * was required to hold the {@link android.Manifest.permission#POWER_SAVER} permission. Starting + * from Android version {@link Build.VERSION_CODES#S}, that permission is no longer required. + * * @return The current value power saver mode for the system. * * @see AutoPowerSaveModeTriggers @@ -1883,7 +1893,6 @@ public final class PowerManager { */ @AutoPowerSaveModeTriggers @SystemApi - @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveModeTrigger() { try { return mService.getPowerSaveModeTrigger(); diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 54d2df865c39..136dc388022f 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -207,6 +207,12 @@ public class Process { public static final int SE_UID = 1068; /** + * Defines the UID/GID for the iorapd. + * @hide + */ + public static final int IORAPD_UID = 1071; + + /** * Defines the UID/GID for the NetworkStack app. * @hide */ diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 93c1690e3813..43184ea4b9a9 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -26,6 +26,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.app.KeyguardManager; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; @@ -631,10 +632,15 @@ public class RecoverySystem { /** * Prepare to apply an unattended update by asking the user for their Lock Screen Knowledge * Factor (LSKF). If supplied, the {@code intentSender} will be called when the system is setup - * and ready to apply the OTA. This API is expected to handle requests from multiple clients - * simultaneously, e.g. from ota and mainline. + * and ready to apply the OTA. <p> * - * <p> The behavior of multi-client Resume on Reboot works as follows + * <p> If the device doesn't setup a lock screen, i.e. by checking + * {@link KeyguardManager#isKeyguardSecure()}, this API call will fail and throw an exception. + * Callers are expected to use {@link PowerManager#reboot(String)} directly without going + * through the RoR flow. <p> + * + * <p> This API is expected to handle requests from multiple clients simultaneously, e.g. + * from ota and mainline. The behavior of multi-client Resume on Reboot works as follows * <li> Each client should call this function to prepare for Resume on Reboot before calling * {@link #rebootAndApply(Context, String, boolean)} </li> * <li> One client cannot clear the Resume on Reboot preparation of another client. </li> @@ -658,6 +664,13 @@ public class RecoverySystem { if (updateToken == null) { throw new NullPointerException("updateToken == null"); } + + KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class); + if (keyguardManager == null || !keyguardManager.isDeviceSecure()) { + throw new IOException("Failed to request LSKF because the device doesn't have a" + + " lock screen. "); + } + RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); if (!rs.requestLskf(context.getPackageName(), intentSender)) { throw new IOException("preparation for update failed"); diff --git a/core/java/android/os/SystemBatteryConsumer.java b/core/java/android/os/SystemBatteryConsumer.java index fc4aa93b97c7..06cff904a215 100644 --- a/core/java/android/os/SystemBatteryConsumer.java +++ b/core/java/android/os/SystemBatteryConsumer.java @@ -45,12 +45,13 @@ public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable DRAIN_TYPE_FLASHLIGHT, DRAIN_TYPE_IDLE, DRAIN_TYPE_MEMORY, - DRAIN_TYPE_OVERCOUNTED, + // Reserved: OVERCOUNTED, DRAIN_TYPE_PHONE, DRAIN_TYPE_SCREEN, - DRAIN_TYPE_UNACCOUNTED, + // Reserved: UNACCOUNTED, // Reserved: USER, DRAIN_TYPE_WIFI, + DRAIN_TYPE_CUSTOM, }) @Retention(RetentionPolicy.SOURCE) public static @interface DrainType { @@ -63,11 +64,10 @@ public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable public static final int DRAIN_TYPE_FLASHLIGHT = 5; public static final int DRAIN_TYPE_IDLE = 6; public static final int DRAIN_TYPE_MEMORY = 7; - public static final int DRAIN_TYPE_OVERCOUNTED = 8; public static final int DRAIN_TYPE_PHONE = 9; public static final int DRAIN_TYPE_SCREEN = 10; - public static final int DRAIN_TYPE_UNACCOUNTED = 11; public static final int DRAIN_TYPE_WIFI = 13; + public static final int DRAIN_TYPE_CUSTOM = 14; @DrainType private final int mDrainType; diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java index e29d75611856..d8f63444dd1c 100644 --- a/core/java/android/os/SystemClock.java +++ b/core/java/android/os/SystemClock.java @@ -338,8 +338,7 @@ public final class SystemClock { try { time = mMgr.getGnssTimeMillis(); } catch (RemoteException e) { - e.rethrowFromSystemServer(); - return 0; + throw e.rethrowFromSystemServer(); } if (time == null) { throw new DateTimeException("Gnss based time is not available."); diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING index 1bdc82a82c6c..97e03e9d0d94 100644 --- a/core/java/android/os/TEST_MAPPING +++ b/core/java/android/os/TEST_MAPPING @@ -38,6 +38,23 @@ "include-filter": "com.android.server.pm.parsing.PackageInfoUserFieldsTest" } ] + }, + { + "file_patterns": ["BatteryStats.java"], + "name": "FrameworksCoreTests", + "options": [ + { "include-filter": "com.android.internal.os.BatteryStatsTests" }, + { "exclude-annotation": "com.android.internal.os.SkipPresubmit" } + ] + }, + { + "file_patterns": ["BatteryStats.java"], + "name": "FrameworksServicesTests", + "options": [ + { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }, + { "include-filter": "com.android.server.am.MeasuredEnergySnapshotTest" }, + { "include-filter": "com.android.server.am.BatteryExternalStatsWorkerTest" } + ] } ], "postsubmit": [ diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 9c9e4995d673..c8cbc517b226 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -168,8 +168,10 @@ public final class Trace { } /** - * Set whether application tracing is allowed for this process. This is intended to be set - * once at application start-up time based on whether the application is debuggable. + * From Android S, this is no-op. + * + * Before, set whether application tracing is allowed for this process. This is intended to be + * set once at application start-up time based on whether the application is debuggable. * * @hide */ diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index a828077ac055..dfa0c396485d 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -16,9 +16,13 @@ package android.os; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Contains power consumption data attributed to a specific UID. * @@ -26,40 +30,74 @@ import android.annotation.Nullable; */ public final class UidBatteryConsumer extends BatteryConsumer implements Parcelable { + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + STATE_FOREGROUND, + STATE_BACKGROUND + }) + public @interface State { + } + + /** + * The state of an application when it is either running a foreground (top) activity + * or a foreground service. + */ + public static final int STATE_FOREGROUND = 0; + + /** + * The state of an application when it is running in the background, including the following + * states: + * + * {@link android.app.ActivityManager#PROCESS_STATE_IMPORTANT_BACKGROUND}, + * {@link android.app.ActivityManager#PROCESS_STATE_TRANSIENT_BACKGROUND}, + * {@link android.app.ActivityManager#PROCESS_STATE_BACKUP}, + * {@link android.app.ActivityManager#PROCESS_STATE_SERVICE}, + * {@link android.app.ActivityManager#PROCESS_STATE_RECEIVER}. + */ + public static final int STATE_BACKGROUND = 1; + private final int mUid; @Nullable private final String mPackageWithHighestDrain; - private boolean mSystemComponent; + private final long mTimeInForegroundMs; + private final long mTimeInBackgroundMs; public int getUid() { return mUid; } - /** - * Returns true if this battery consumer is considered to be a part of the operating - * system itself. For example, the UidBatteryConsumer with the UID {@link Process#BLUETOOTH_UID} - * is a system component. - */ - public boolean isSystemComponent() { - return mSystemComponent; - } - @Nullable public String getPackageWithHighestDrain() { return mPackageWithHighestDrain; } + /** + * Returns the amount of time in milliseconds this UID spent in the specified state. + */ + public long getTimeInStateMs(@State int state) { + switch (state) { + case STATE_BACKGROUND: + return mTimeInBackgroundMs; + case STATE_FOREGROUND: + return mTimeInForegroundMs; + } + return 0; + } + private UidBatteryConsumer(@NonNull Builder builder) { super(builder.mPowerComponentsBuilder.build()); mUid = builder.mUid; - mSystemComponent = builder.mSystemComponent; mPackageWithHighestDrain = builder.mPackageWithHighestDrain; + mTimeInForegroundMs = builder.mTimeInForegroundMs; + mTimeInBackgroundMs = builder.mTimeInBackgroundMs; } private UidBatteryConsumer(@NonNull Parcel source) { super(new PowerComponents(source)); mUid = source.readInt(); mPackageWithHighestDrain = source.readString(); + mTimeInForegroundMs = source.readLong(); + mTimeInBackgroundMs = source.readLong(); } /** @@ -70,6 +108,8 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela super.writeToParcel(dest, flags); dest.writeInt(mUid); dest.writeString(mPackageWithHighestDrain); + dest.writeLong(mTimeInForegroundMs); + dest.writeLong(mTimeInBackgroundMs); } @NonNull @@ -95,7 +135,8 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela private final BatteryStats.Uid mBatteryStatsUid; private final int mUid; private String mPackageWithHighestDrain; - private boolean mSystemComponent; + public long mTimeInForegroundMs; + public long mTimeInBackgroundMs; private boolean mExcludeFromBatteryUsageStats; public Builder(int customPowerComponentCount, int customTimeComponentCount, @@ -125,6 +166,25 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela } /** + * Sets the duration, in milliseconds, that this UID was active in a particular state, + * such as foreground or background. + */ + @NonNull + public Builder setTimeInStateMs(@State int state, long timeInStateMs) { + switch (state) { + case STATE_FOREGROUND: + mTimeInForegroundMs = timeInStateMs; + break; + case STATE_BACKGROUND: + mTimeInBackgroundMs = timeInStateMs; + break; + default: + throw new IllegalArgumentException("Unsupported state: " + state); + } + return this; + } + + /** * Marks the UidBatteryConsumer for exclusion from the result set. */ public Builder excludeFromBatteryUsageStats() { diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 8bdfd3d3d627..5069e0319119 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1688,6 +1688,7 @@ public class UserManager { * @return Whether guest user is always ephemeral * @hide */ + @TestApi public static boolean isGuestUserEphemeral() { return Resources.getSystem() .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral); @@ -1802,6 +1803,20 @@ public class UserManager { } /** + * @return the user type of the context user. + * @hide + */ + @TestApi + @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) + @UserHandleAware + public @NonNull String getUserType() { + UserInfo userInfo = getUserInfo(mUserId); + return userInfo == null ? "" : userInfo.userType; + } + + /** * Returns the user name of the context user. This call is only available to applications on * the system image; it requires the {@code android.permission.MANAGE_USERS} or {@code * android.permission.GET_ACCOUNTS_PRIVILEGED} permissions. @@ -1809,7 +1824,8 @@ public class UserManager { * @return the user name */ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, - android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}, conditional = true) + android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, + android.Manifest.permission.CREATE_USERS}, conditional = true) @UserHandleAware public @NonNull String getUserName() { if (UserHandle.myUserId() == mUserId) { @@ -2300,13 +2316,14 @@ public class UserManager { } /** - * Checks if the calling user is running on foreground. + * Checks if the context user is running in the foreground. * - * @return whether the calling user is running on foreground. + * @return whether the context user is running in the foreground. */ + @UserHandleAware public boolean isUserForeground() { try { - return mService.isUserForeground(); + return mService.isUserForeground(mUserId); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2792,6 +2809,7 @@ public class UserManager { */ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) + @TestApi public @Nullable UserInfo createUser(@Nullable String name, @NonNull String userType, @UserInfoFlag int flags) { try { @@ -2828,6 +2846,7 @@ public class UserManager { * @throws UserOperationException if the user could not be created. * @hide */ + @TestApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) public @NonNull UserInfo preCreateUser(@NonNull String userType) @@ -2976,10 +2995,11 @@ public class UserManager { * * @hide */ + @TestApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) - public UserInfo createProfileForUser(String name, @NonNull String userType, - @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages) { + public @Nullable UserInfo createProfileForUser(@Nullable String name, @NonNull String userType, + @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) { try { return mService.createProfileForUserWithThrow(name, userType, flags, userId, disallowedPackages); @@ -3022,9 +3042,10 @@ public class UserManager { * * @hide */ + @TestApi @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) - public UserInfo createRestrictedProfile(String name) { + public @Nullable UserInfo createRestrictedProfile(@Nullable String name) { try { UserHandle parentUserHandle = Process.myUserHandle(); UserInfo user = mService.createRestrictedProfileWithThrow(name, @@ -3248,10 +3269,11 @@ public class UserManager { /** * Return the number of users currently created on the device. - * <p>This API is not for use by third-party apps. It requires the {@code MANAGE_USERS} - * permission.</p> */ - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) public int getUserCount() { List<UserInfo> users = getUsers(); return users != null ? users.size() : 1; @@ -3274,7 +3296,10 @@ public class UserManager { * @hide */ @UnsupportedAppUsage - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) public List<UserInfo> getUsers() { return getUsers(/*excludePartial= */ true, /* excludeDying= */ false, /* excludePreCreated= */ true); @@ -3292,7 +3317,10 @@ public class UserManager { * @return the list of users that were created. * @hide */ - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) public @NonNull List<UserInfo> getAliveUsers() { return getUsers(/*excludePartial= */ true, /* excludeDying= */ true, /* excludePreCreated= */ true); @@ -3306,7 +3334,10 @@ public class UserManager { */ @Deprecated @UnsupportedAppUsage - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) public @NonNull List<UserInfo> getUsers(boolean excludeDying) { return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); @@ -3317,8 +3348,12 @@ public class UserManager { * * @hide */ - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) - public List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, + @TestApi + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) + public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated) { try { return mService.getUsers(excludePartial, excludeDying, excludePreCreated); @@ -3335,7 +3370,10 @@ public class UserManager { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) public @NonNull List<UserHandle> getUserHandles(boolean excludeDying) { List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); @@ -3354,7 +3392,10 @@ public class UserManager { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS + }) public long[] getSerialNumbersOfUsers(boolean excludeDying) { List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); @@ -3678,7 +3719,10 @@ public class UserManager { * @hide */ @UnsupportedAppUsage - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS + }) public UserInfo getProfileParent(@UserIdInt int userId) { try { return mService.getProfileParent(userId); @@ -3697,7 +3741,10 @@ public class UserManager { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS + }) public @Nullable UserHandle getProfileParent(@NonNull UserHandle user) { UserInfo info = getProfileParent(user.getIdentifier()); diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index df3beb2c6ea7..0587610630a6 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -437,7 +437,11 @@ public abstract class VibrationEffect implements Parcelable { * @hide */ protected static int scale(int amplitude, float scaleFactor) { - return (int) (scale((float) amplitude / MAX_AMPLITUDE, scaleFactor) * MAX_AMPLITUDE); + if (amplitude == 0) { + return 0; + } + int scaled = (int) (scale((float) amplitude / MAX_AMPLITUDE, scaleFactor) * MAX_AMPLITUDE); + return MathUtils.constrain(scaled, 1, MAX_AMPLITUDE); } /** @@ -473,7 +477,7 @@ public abstract class VibrationEffect implements Parcelable { float a = (expMaxX + 1f) / (expMaxX - 1f); float fx = (expX - 1f) / (expX + 1f); - return a * fx; + return MathUtils.constrain(a * fx, 0f, 1f); } /** @hide */ @@ -536,9 +540,10 @@ public abstract class VibrationEffect implements Parcelable { /** @hide */ @Override public OneShot resolve(int defaultAmplitude) { - if (defaultAmplitude > MAX_AMPLITUDE || defaultAmplitude < 0) { + if (defaultAmplitude > MAX_AMPLITUDE || defaultAmplitude <= 0) { throw new IllegalArgumentException( - "Amplitude is negative or greater than MAX_AMPLITUDE"); + "amplitude must be between 1 and 255 inclusive (amplitude=" + + defaultAmplitude + ")"); } if (mAmplitude == DEFAULT_AMPLITUDE) { return new OneShot(mDuration, defaultAmplitude); diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index d6fa733927fb..b003d238c268 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -186,7 +186,8 @@ public abstract class Vibrator { /** * Return the ID of this vibrator. * - * @return The id of the vibrator controlled by this service. + * @return A non-negative integer representing the id of the vibrator controlled by this + * service, or -1 this service is not attached to any physical vibrator. */ public int getId() { return -1; diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java index 5dd38b6cbd86..5a01814508e1 100644 --- a/core/java/android/os/VibratorManager.java +++ b/core/java/android/os/VibratorManager.java @@ -91,10 +91,10 @@ public abstract class VibratorManager { * * <p> * Pass in a {@link CombinedVibrationEffect} representing a combination of {@link - * VibrationEffect} to be played on one or more vibrators. + * VibrationEffect VibrationEffects} to be played on one or more vibrators. * </p> * - * @param effect an array of longs of times for which to turn the vibrator on or off. + * @param effect a combination of effects to be performed by one or more vibrators. */ @RequiresPermission(android.Manifest.permission.VIBRATE) public final void vibrate(@NonNull CombinedVibrationEffect effect) { @@ -109,7 +109,7 @@ public abstract class VibratorManager { * VibrationEffect} to be played on one or more vibrators. * </p> * - * @param effect an array of longs of times for which to turn the vibrator on or off. + * @param effect a combination of effects to be performed by one or more vibrators. * @param attributes {@link VibrationAttributes} corresponding to the vibration. For example, * specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or * {@link VibrationAttributes#USAGE_RINGTONE} for vibrations associated with diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 06203ff15094..9ffc5aa0022c 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -426,7 +426,7 @@ public class ZygoteProcess { // avoid writing a partial response to the zygote. for (String arg : args) { // Making two indexOf calls here is faster than running a manually fused loop due - // to the fact that indexOf is a optimized intrinsic. + // to the fact that indexOf is an optimized intrinsic. if (arg.indexOf('\n') >= 0) { throw new ZygoteStartFailedEx("Embedded newlines not allowed"); } else if (arg.indexOf('\r') >= 0) { diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index f2fe71913bb1..73520e07d118 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -36,6 +36,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.DataLoaderParams; import android.content.pm.IDataLoaderStatusListener; +import android.content.pm.IPackageLoadingProgressCallback; import android.content.pm.InstallationFileParcel; import java.io.File; @@ -58,7 +59,6 @@ public final class IncrementalFileStorages { /** * Set up files and directories used in an installation session. Only used by Incremental. * All the files will be created in defaultStorage. - * TODO(b/133435829): code clean up * * @throws IllegalStateException the session is not an Incremental installation session. * @throws IOException if fails to setup files or directories. @@ -71,12 +71,11 @@ public final class IncrementalFileStorages { @Nullable StorageHealthCheckParams healthCheckParams, @Nullable IStorageHealthListener healthListener, @NonNull List<InstallationFileParcel> addedFiles, - @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException { - // TODO(b/136132412): validity check if session should not be incremental + @NonNull PerUidReadTimeouts[] perUidReadTimeouts, + @Nullable IPackageLoadingProgressCallback progressCallback) throws IOException { IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService( Context.INCREMENTAL_SERVICE); if (incrementalManager == null) { - // TODO(b/146080380): add incremental-specific error code throw new IOException("Failed to obtain incrementalManager."); } @@ -87,7 +86,6 @@ public final class IncrementalFileStorages { try { result.addApkFile(file); } catch (IOException e) { - // TODO(b/146080380): add incremental-specific error code throw new IOException( "Failed to add file to IncFS: " + file.name + ", reason: ", e); } @@ -95,6 +93,11 @@ public final class IncrementalFileStorages { throw new IOException("Unknown file location: " + file.location); } } + // Register progress loading callback after files have been added + if (progressCallback != null) { + incrementalManager.registerLoadingProgressCallback(stageDir.getAbsolutePath(), + progressCallback); + } result.startLoading(dataLoaderParams, statusListener, healthCheckParams, healthListener, perUidReadTimeouts); @@ -196,7 +199,6 @@ public final class IncrementalFileStorages { /** * Resets the states and unbinds storage instances for an installation session. - * TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept */ public void cleanUp() { if (mDefaultStorage == null) { @@ -204,6 +206,7 @@ public final class IncrementalFileStorages { } try { + mIncrementalManager.unregisterLoadingProgressCallbacks(mStageDir.getAbsolutePath()); mDefaultStorage.unBind(mStageDir.getAbsolutePath()); } catch (IOException ignored) { } diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 05899947c3df..592e98abae63 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -241,10 +241,12 @@ public final class IncrementalManager { } /** - * Checks if device supports V2 calls (e.g. PerUid). + * 0 - IncFs is disabled. + * 1 - IncFs v1, core features, no PerUid support. Optional in R. + * 2 - IncFs v2, PerUid support, fs-verity support. Required in S. */ - public static boolean isV2Available() { - return nativeIsV2Available(); + public static int getVersion() { + return nativeIsEnabled() ? nativeIsV2Available() ? 2 : 1 : 0; } /** @@ -342,7 +344,6 @@ public final class IncrementalManager { storage.unregisterLoadingProgressListener(); } - // TODO(b/165841827): handle reboot and app update public boolean registerCallback(@NonNull IncrementalStorage storage, @NonNull IPackageLoadingProgressCallback callback) { final int storageId = storage.getId(); @@ -364,30 +365,6 @@ public final class IncrementalManager { return storage.registerLoadingProgressListener(this); } - public boolean unregisterCallback(@NonNull IncrementalStorage storage, - @NonNull IPackageLoadingProgressCallback callback) { - final int storageId = storage.getId(); - final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage; - synchronized (mCallbacks) { - callbacksForStorage = mCallbacks.get(storageId); - if (callbacksForStorage == null) { - // no callback has ever been registered on this storage - return false; - } - if (!callbacksForStorage.unregister(callback)) { - // the callback was not registered - return false; - } - if (callbacksForStorage.getRegisteredCallbackCount() > 0) { - // other callbacks are still listening on this storage - return true; - } - mCallbacks.delete(storageId); - } - // stop listening for this storage - return storage.unregisterLoadingProgressListener(); - } - @Override public void onStorageLoadingProgressChanged(int storageId, float progress) { final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage; diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index 4669b208b163..0041699df9ef 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -196,4 +196,6 @@ interface IStorageManager { void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88; void fixupAppDir(in String path) = 89; void disableAppDataIsolation(in String pkgName, int pid, int userId) = 90; -} + void notifyAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 91; + void notifyAppIoResumed(in String volumeUuid, int uid, int tid, int reason) = 92; + } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index a5d3c2acc577..7c8874cc1ea7 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1536,6 +1536,7 @@ public class StorageManager { } /** {@hide} */ + @TestApi public static boolean isUserKeyUnlocked(int userId) { if (sStorageManager == null) { sStorageManager = IStorageManager.Stub @@ -2698,6 +2699,80 @@ public class StorageManager { } } + /** + * Reason to provide if app IO is blocked/resumed because of transcoding + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 0; + + /** + * Constants for use with + * {@link #notifyAppIoBlocked} and {@link notifyAppIoResumed}, to specify the reason an app's + * IO is blocked/resumed. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "APP_IO_BLOCKED_REASON_" }, value = { + APP_IO_BLOCKED_REASON_TRANSCODING + }) + public @interface AppIoBlockedReason {} + + /** + * Notify the system that an app with {@code uid} and {@code tid} is blocked on an IO request on + * {@code volumeUuid} for {@code reason}. + * + * This blocked state can be used to modify the ANR behavior for the app while it's blocked. + * For example during transcoding. + * + * This can only be called by the {@link ExternalStorageService} holding the + * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission. + * + * @param volumeUuid the UUID of the storage volume that the app IO is blocked on + * @param uid the UID of the app blocked on IO + * @param tid the tid of the app blocked on IO + * @param reason the reason the app is blocked on IO + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public void notifyAppIoBlocked(@NonNull String volumeUuid, int uid, int tid, + @AppIoBlockedReason int reason) { + try { + mStorageManager.notifyAppIoBlocked(volumeUuid, uid, tid, reason); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Notify the system that an app with {@code uid} and {@code tid} has resmued a previously + * blocked IO request on {@code volumeUuid} for {@code reason}. + * + * All app IO will be automatically marked as unblocked if {@code volumeUuid} is unmounted. + * + * This can only be called by the {@link ExternalStorageService} holding the + * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission. + * + * @param volumeUuid the UUID of the storage volume that the app IO is resumed on + * @param uid the UID of the app resuming IO + * @param tid the tid of the app resuming IO + * @param reason the reason the app is resuming IO + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public void notifyAppIoResumed(@NonNull String volumeUuid, int uid, int tid, + @AppIoBlockedReason int reason) { + try { + mStorageManager.notifyAppIoResumed(volumeUuid, uid, tid, reason); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private final Object mFuseAppLoopLock = new Object(); @GuardedBy("mFuseAppLoopLock") diff --git a/core/java/android/os/strictmode/IncorrectContextUseViolation.java b/core/java/android/os/strictmode/IncorrectContextUseViolation.java index 11d26cab14b3..d8c22fd94ce9 100644 --- a/core/java/android/os/strictmode/IncorrectContextUseViolation.java +++ b/core/java/android/os/strictmode/IncorrectContextUseViolation.java @@ -24,7 +24,7 @@ import android.content.Context; * instance. * * @see Context#getSystemService(String) - * @see Context#isUiContext(Context) + * @see Context#isUiContext * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse() */ public final class IncorrectContextUseViolation extends Violation { diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index ff01011bd19b..bae36b299247 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -485,10 +485,6 @@ public final class PermissionManager { * One for cases where the installer of the package allowlists a permission. This list * corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER} flag. Can be * accessed by pre-installed holders of a dedicated permission or the installer on record. - * <li> - * One for cases where the system exempts the permission when granting a role. This list - * corresponds to the {@link PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE} flag. Can be - * accessed by pre-installed holders of a dedicated permission. * </ol> * * @param packageName the app for which to get allowlisted permissions @@ -502,7 +498,6 @@ public final class PermissionManager { * @see PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM * @see PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE * @see PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER - * @see PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE * * @hide Pending API */ @@ -549,10 +544,6 @@ public final class PermissionManager { * One for cases where the installer of the package allowlists a permission. This list * corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER} flag. Can be * accessed by pre-installed holders of a dedicated permission or the installer on record. - * <li> - * One for cases where the system exempts the permission when granting a role. This list - * corresponds to the {@link PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE} flag. Can be - * accessed by pre-installed holders of a dedicated permission. * </ol> * <p> * You need to specify the allowlists for which to set the allowlisted permissions which will @@ -570,7 +561,6 @@ public final class PermissionManager { * @see PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM * @see PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE * @see PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER - * @see PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE * * @hide Pending API */ @@ -613,10 +603,6 @@ public final class PermissionManager { * One for cases where the installer of the package allowlists a permission. This list * corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER} flag. Can be * accessed by pre-installed holders of a dedicated permission or the installer on record. - * <li> - * One for cases where the system exempts the permission when granting a role. This list - * corresponds to the {@link PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE} flag. Can be - * accessed by pre-installed holders of a dedicated permission. * </ol> * <p> * You need to specify the allowlists for which to set the allowlisted permissions which will @@ -634,7 +620,6 @@ public final class PermissionManager { * @see PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM * @see PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE * @see PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER - * @see PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE * * @hide Pending API */ diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index 0e35ef98f1b7..4c9e77c35135 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -57,6 +57,8 @@ import android.util.ArraySet; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; +import com.android.internal.R; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -186,6 +188,15 @@ public class PermissionUsageHelper { == PackageManager.PERMISSION_GRANTED; } + private boolean isSpeechRecognizerUsage(String op, String packageName) { + if (!OPSTR_RECORD_AUDIO.equals(op)) { + return false; + } + + return packageName.equals( + mContext.getString(R.string.config_systemSpeechRecognizer)); + } + /** * @see PermissionManager.getIndicatorAppOpUsageData */ @@ -317,7 +328,8 @@ public class PermissionUsageHelper { if (packageName.equals(SYSTEM_PKG) || (!isUserSensitive(packageName, user, op) && !isLocationProvider(packageName, user) - && !isAppPredictor(packageName, user))) { + && !isAppPredictor(packageName, user)) + && !isSpeechRecognizerUsage(op, packageName)) { continue; } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index e134c29520b2..6e89faf9c2ed 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -157,6 +157,14 @@ public final class DeviceConfig { public static final String NAMESPACE_BLUETOOTH = "bluetooth"; /** + * Namespace for features relating to clipboard. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_CLIPBOARD = "clipboard"; + + /** * Namespace for all networking connectivity related features. * * @hide @@ -275,6 +283,14 @@ public final class DeviceConfig { public static final String NAMESPACE_PROFCOLLECT_NATIVE_BOOT = "profcollect_native_boot"; /** + * Namespace for features related to Reboot Readiness detection. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_REBOOT_READINESS = "reboot_readiness"; + + /** * Namespace for Rollback flags that are applied immediately. * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e979e13d89c5..617220e00f9d 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -417,6 +417,22 @@ public final class Settings { "android.settings.MANAGE_UNKNOWN_APP_SOURCES"; /** + * Activity Action: Show settings to allow configuration of + * {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission + * + * Input: Optionally, the Intent's data URI can specify the application package name to + * directly invoke the management GUI specific to the package name. For example + * "package:com.my.app". + * <p> + * Output: When a package data uri is passed as input, the activity result is set to + * {@link android.app.Activity#RESULT_OK} if the permission was granted to the app. Otherwise, + * the result is set to {@link android.app.Activity#RESULT_CANCELED}. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_SCHEDULE_EXACT_ALARM = + "android.settings.REQUEST_SCHEDULE_EXACT_ALARM"; + + /** * Activity Action: Show settings to allow configuration of cross-profile access for apps * * Input: Optionally, the Intent's data URI can specify the application package name to @@ -10496,18 +10512,6 @@ public final class Settings { "force_desktop_mode_on_external_displays"; /** - * Whether to allow non-resizable apps to be freeform. - * - * TODO(b/176061101) remove after update all usages - * @deprecated use {@link #DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW} - * @hide - */ - @Deprecated - @Readable - public static final String DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM = - "enable_sizecompat_freeform"; - - /** * Whether to allow non-resizable apps to be shown in multi-window. The app will be * letterboxed if the request orientation is not met, and will be shown in size-compat * mode if the container size has changed. @@ -10697,14 +10701,6 @@ public final class Settings { "hdmi_cec_switch_enabled"; /** - * HDMI CEC version to use. Defaults to v1.4b. - * @hide - */ - @Readable - public static final String HDMI_CEC_VERSION = - "hdmi_cec_version"; - - /** * Whether TV will automatically turn on upon reception of the CEC command * <Text View On> or <Image View On>. (0 = false, 1 = true) * @@ -14614,6 +14610,29 @@ public final class Settings { public static final String POWER_BUTTON_VERY_LONG_PRESS = "power_button_very_long_press"; + + /** + * Keyguard should be on the left hand side of the screen, for wide screen layouts. + * + * @hide + */ + public static final int ONE_HANDED_KEYGUARD_SIDE_LEFT = 0; + + /** + * Keyguard should be on the right hand side of the screen, for wide screen layouts. + * + * @hide + */ + public static final int ONE_HANDED_KEYGUARD_SIDE_RIGHT = 1; + /** + * In one handed mode, which side the keyguard should be on. Allowable values are one of + * the ONE_HANDED_KEYGUARD_SIDE_* constants. + * + * @hide + */ + @Readable + public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side"; + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. diff --git a/core/java/android/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java index 074d5f167ec3..030b86339822 100644 --- a/core/java/android/provider/SimPhonebookContract.java +++ b/core/java/android/provider/SimPhonebookContract.java @@ -44,8 +44,11 @@ import java.util.Objects; * The contract between the provider of contact records on the device's SIM cards and applications. * Contains definitions of the supported URIs and columns. * - * <p>This content provider does not support any of the QUERY_ARG_SQL* bundle arguments. An - * IllegalArgumentException will be thrown if these are included. + * <h3>Permissions</h3> + * <p> + * Querying this provider requires {@link android.Manifest.permission#READ_CONTACTS} and writing + * to this provider requires {@link android.Manifest.permission#WRITE_CONTACTS} + * </p> */ public final class SimPhonebookContract { @@ -85,7 +88,73 @@ public final class SimPhonebookContract { } } - /** Constants for the contact records on a SIM card. */ + /** + * Constants for the contact records on a SIM card. + * + * <h3 id="simrecords-data">Data</h3> + * <p> + * Data is stored in a specific elementary file on a specific SIM card and these are isolated + * from each other. SIM cards are identified by their subscription ID. SIM cards may not support + * all or even any of the elementary file types. A SIM will have constraints on + * the values of the data that can be stored in each elementary file. The available SIMs, + * their supported elementary file types and the constraints on the data can be discovered by + * querying {@link ElementaryFiles#CONTENT_URI}. Each elementary file has a fixed capacity + * for the number of records that may be stored. This can be determined from the value + * of the {@link ElementaryFiles#MAX_RECORDS} column. + * </p> + * <p> + * The {@link SimRecords#PHONE_NUMBER} column can only contain dialable characters and this + * applies regardless of the SIM that is being used. See + * {@link android.telephony.PhoneNumberUtils#isDialable(char)} for more details. Additionally + * the phone number can contain at most {@link ElementaryFiles#PHONE_NUMBER_MAX_LENGTH} + * characters. The {@link SimRecords#NAME} column can contain at most + * {@link ElementaryFiles#NAME_MAX_LENGTH} bytes when it is encoded for storage on the SIM. + * Encoding is done internally and so the name should be provided unencoded but the number of + * bytes required to encode it will vary depending on the characters it contains. This length + * can be determined by calling + * {@link SimRecords#getEncodedNameLength(ContentResolver, String)}. + * </p> + * <h3>Operations </h3> + * <dl> + * <dd><b>Insert</b></dd> + * <p> + * Only {@link ElementaryFiles#EF_ADN} supports inserts. {@link SimRecords#PHONE_NUMBER} + * is a required column. If the value provided for this column is missing, null, empty + * or violates the requirements discussed in the <a href="#simrecords-data">Data</a> + * section above an {@link IllegalArgumentException} will be thrown. The + * {@link SimRecords#NAME} column may be omitted but if provided and it violates any of + * the requirements discussed in the <a href="#simrecords-data">Data</a> section above + * an {@link IllegalArgumentException} will be thrown. + * </p> + * <p> + * If an insert is not possible because the elementary file is full then an + * {@link IllegalStateException} will be thrown. + * </p> + * <dd><b>Update</b></dd> + * <p> + * Updates can only be performed for individual records on {@link ElementaryFiles#EF_ADN}. + * A specific record is referenced via the Uri returned by + * {@link SimRecords#getItemUri(int, int, int)}. Updates have the same constraints and + * behavior for the {@link SimRecords#PHONE_NUMBER} and {@link SimRecords#NAME} as insert. + * However, in the case of update the {@link SimRecords#PHONE_NUMBER} may be omitted as + * the existing record will already have a valid value. + * </p> + * <dd><b>Delete</b></dd> + * <p> + * Delete may only be performed for individual records on {@link ElementaryFiles#EF_ADN}. + * Deleting records will free up space for use by future inserts. + * </p> + * <dd><b>Query</b></dd> + * <p> + * All the records stored on a specific elementary file can be read via a Uri returned by + * {@link SimRecords#getContentUri(int, int)}. This query always returns all records; there + * is no support for filtering via a selection. An individual record can be queried via a Uri + * returned by {@link SimRecords#getItemUri(int, int, int)}. Queries will throw an + * {@link IllegalArgumentException} when the SIM with the subscription ID or the elementary file + * type are invalid or unavailable. + * </p> + * </dl> + */ public static final class SimRecords { /** @@ -197,8 +266,8 @@ public final class SimPhonebookContract { * be discovered by querying {@link ElementaryFiles#CONTENT_URI}. * * <p>If a SIM with the provided subscription ID does not exist or the SIM with the provided - * subscription ID doesn't support the specified entity file then queries will return - * and empty cursor and inserts will throw an {@link IllegalArgumentException} + * subscription ID doesn't support the specified entity file then all operations will + * throw an {@link IllegalArgumentException}. * * @param subscriptionId the subscriptionId of the SIM card that this Uri will reference * @param efType the elementary file on the SIM that this Uri will reference @@ -233,6 +302,9 @@ public final class SimPhonebookContract { * must be greater than 0. If there is no record with this record * number in the specified entity file then it will be treated as a * non-existent record. + * @see ElementaryFiles#SUBSCRIPTION_ID + * @see ElementaryFiles#EF_TYPE + * @see #RECORD_NUMBER */ @NonNull public static Uri getItemUri( @@ -287,7 +359,28 @@ public final class SimPhonebookContract { } - /** Constants for metadata about the elementary files of the SIM cards in the phone. */ + /** + * Constants for metadata about the elementary files of the SIM cards in the phone. + * + * <h3>Operations </h3> + * <dl> + * <dd><b>Insert</b></dd> + * <p>Insert is not supported for the Uris defined in this class.</p> + * <dd><b>Update</b></dd> + * <p>Update is not supported for the Uris defined in this class.</p> + * <dd><b>Delete</b></dd> + * <p>Delete is not supported for the Uris defined in this class.</p> + * <dd><b>Query</b></dd> + * <p> + * The elementary files for all the inserted SIMs can be read via + * {@link ElementaryFiles#CONTENT_URI}. Unsupported elementary files are omitted from the + * results. This Uri always returns all supported elementary files for all available SIMs; it + * does not support filtering via a selection. A specific elementary file can be queried + * via a Uri returned by {@link ElementaryFiles#getItemUri(int, int)}. If the elementary file + * referenced by this Uri is unsupported by the SIM then the query will return an empty cursor. + * </p> + * </dl> + */ public static final class ElementaryFiles { /** {@link SubscriptionInfo#getSimSlotIndex()} of the SIM for this row. */ diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java index d859b1c33c94..6788353ba2fb 100644 --- a/core/java/android/security/keystore/recovery/RecoveryController.java +++ b/core/java/android/security/keystore/recovery/RecoveryController.java @@ -27,8 +27,11 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.security.KeyStore; -import android.security.keystore.AndroidKeyStoreProvider; +import android.security.KeyStore2; import android.security.keystore.KeyPermanentlyInvalidatedException; +import android.security.keystore2.AndroidKeyStoreProvider; +import android.system.keystore2.Domain; +import android.system.keystore2.KeyDescriptor; import com.android.internal.widget.ILockSettings; @@ -709,10 +712,34 @@ public class RecoveryController { */ @NonNull Key getKeyFromGrant(@NonNull String grantAlias) throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( - mKeyStore, - grantAlias, - KeyStore.UID_SELF); + if (grantAlias.startsWith(APPLICATION_KEY_GRANT_PREFIX)) { + return AndroidKeyStoreProvider + .loadAndroidKeyStoreSecretKeyFromKeystore( + KeyStore2.getInstance(), + getGrantDescriptor(grantAlias)); + } + // TODO(b/171305545): remove KeyStore1 logic. + return android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( + mKeyStore, + grantAlias, + KeyStore.UID_SELF); + + } + + private static final String APPLICATION_KEY_GRANT_PREFIX = "recoverable_key:"; + + private static @Nullable KeyDescriptor getGrantDescriptor(String grantAlias) { + KeyDescriptor result = new KeyDescriptor(); + result.domain = Domain.GRANT; + result.blob = null; + result.alias = null; + try { + result.nspace = Long.parseUnsignedLong( + grantAlias.substring(APPLICATION_KEY_GRANT_PREFIX.length()), 16); + } catch (NumberFormatException e) { + return null; + } + return result; } /** diff --git a/core/java/android/service/displayhash/DisplayHasherService.java b/core/java/android/service/displayhash/DisplayHasherService.java new file mode 100644 index 000000000000..331dbe91f6c7 --- /dev/null +++ b/core/java/android/service/displayhash/DisplayHasherService.java @@ -0,0 +1,168 @@ +/* + * 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.displayhash; + +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Intent; +import android.graphics.Rect; +import android.hardware.HardwareBuffer; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteCallback; +import android.view.displayhash.DisplayHash; +import android.view.displayhash.DisplayHashResultCallback; +import android.view.displayhash.VerifiedDisplayHash; + +/** + * A service that handles generating and verify {@link DisplayHash}. + * + * The service will generate a DisplayHash based on arguments passed in. Then later that + * same DisplayHash can be verified to determine that it was created by the system. + * + * @hide + */ +@SystemApi +public abstract class DisplayHasherService extends Service { + + /** @hide **/ + public static final String EXTRA_VERIFIED_DISPLAY_HASH = + "android.service.displayhash.extra.VERIFIED_DISPLAY_HASH"; + + /** + * Manifest metadata key for the resource string array containing the names of all hashing + * algorithms provided by the service. + * + * @hide + */ + public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = + "android.displayhash.available_algorithms"; + + /** + * The {@link Intent} action that must be declared as handled by a service in its manifest + * for the system to recognize it as a DisplayHash providing service. + * + * @hide + */ + @SystemApi + public static final String SERVICE_INTERFACE = + "android.service.displayhash.DisplayHasherService"; + + private DisplayHasherServiceWrapper mWrapper; + private Handler mHandler; + + public DisplayHasherService() { + } + + @Override + public void onCreate() { + super.onCreate(); + mWrapper = new DisplayHasherServiceWrapper(); + mHandler = new Handler(Looper.getMainLooper(), null, true); + } + + @NonNull + @Override + public final IBinder onBind(@NonNull Intent intent) { + return mWrapper; + } + + /** + * Generates the DisplayHash that can be used to validate that the system generated the + * token. + * + * @param salt The salt to use when generating the hmac. This should be unique to the + * caller so the token cannot be verified by any other process. + * @param buffer The buffer for the content to generate the hash for. + * @param bounds The size and position of the content in window space. + * @param hashAlgorithm The String for the hashing algorithm to use based values in + * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}. + * @param callback The callback to invoke + * {@link DisplayHashResultCallback#onDisplayHashResult(DisplayHash)} + * if successfully generated a DisplayHash or {@link + * DisplayHashResultCallback#onDisplayHashError(int)} if failed. + */ + @Nullable + public abstract void onGenerateDisplayHash(@NonNull byte[] salt, + @NonNull HardwareBuffer buffer, @NonNull Rect bounds, + @NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback); + + /** + * Call to verify that the DisplayHash passed in was generated by the system. + * + * @param salt The salt value to use when verifying the hmac. This should be the + * same value that was passed to + * {@link #onGenerateDisplayHash(byte[], + * HardwareBuffer, Rect, String, DisplayHashResultCallback)} to + * generate the token. + * @param displayHash The token to verify that it was generated by the system. + * @return a {@link VerifiedDisplayHash} if the token was generated by the system or null + * if the token cannot be verified. + */ + @Nullable + public abstract VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[] salt, + @NonNull DisplayHash displayHash); + + private void verifyDisplayHash(byte[] salt, DisplayHash displayHash, + RemoteCallback callback) { + VerifiedDisplayHash verifiedDisplayHash = onVerifyDisplayHash(salt, + displayHash); + final Bundle data = new Bundle(); + data.putParcelable(EXTRA_VERIFIED_DISPLAY_HASH, verifiedDisplayHash); + callback.sendResult(data); + } + + private final class DisplayHasherServiceWrapper extends IDisplayHasherService.Stub { + @Override + public void generateDisplayHash(byte[] salt, HardwareBuffer buffer, Rect bounds, + String hashAlgorithm, RemoteCallback callback) { + mHandler.sendMessage( + obtainMessage(DisplayHasherService::onGenerateDisplayHash, + DisplayHasherService.this, salt, buffer, bounds, + hashAlgorithm, new DisplayHashResultCallback() { + @Override + public void onDisplayHashResult( + @NonNull DisplayHash displayHash) { + Bundle result = new Bundle(); + result.putParcelable(EXTRA_DISPLAY_HASH, displayHash); + callback.sendResult(result); + } + + @Override + public void onDisplayHashError(int errorCode) { + Bundle result = new Bundle(); + result.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode); + callback.sendResult(result); + } + })); + } + + @Override + public void verifyDisplayHash(byte[] salt, DisplayHash displayHash, + RemoteCallback callback) { + mHandler.sendMessage( + obtainMessage(DisplayHasherService::verifyDisplayHash, + DisplayHasherService.this, salt, displayHash, callback)); + } + } +} diff --git a/core/java/android/service/screenshot/IScreenshotHasherService.aidl b/core/java/android/service/displayhash/IDisplayHasherService.aidl index d14d147e096c..236bc28c74c8 100644 --- a/core/java/android/service/screenshot/IScreenshotHasherService.aidl +++ b/core/java/android/service/displayhash/IDisplayHasherService.aidl @@ -14,43 +14,41 @@ * limitations under the License. */ -package android.service.screenshot; +package android.service.displayhash; import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.os.RemoteCallback; -import android.service.screenshot.ScreenshotHash; +import android.view.displayhash.DisplayHash; /** - * Service used to handle ScreenshotHash requests. + * Service used to handle DisplayHash requests. * * @hide */ -oneway interface IScreenshotHasherService { +oneway interface IDisplayHasherService { /** - * Generates the ScreenshotHash token that can be used to validate that the system generated the - * token. + * Generates the DisplayHash that can be used to validate that the system generated the token. * * @param salt The salt to use when generating the hmac. This should be unique to the caller so * the token cannot be verified by any other process. - * @param screenshot The screenshot to generate the hash and add to the token. - * @param bounds The size and position of the content being screenshot in the window. - * @param hashAlgorithm The String for the hashing algorithm to use based on values in + * @param buffer The buffer to generate the hash for. + * @param bounds The size and position of the content being hashed in window space. + * @param hashAlgorithm The String for the hash algorithm to use based on values in * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS}. - * @param Callback The callback invoked to send back the ScreenshotHash token. + * @param Callback The callback invoked to send back the DisplayHash. */ - void generateScreenshotHash(in byte[] salt, in HardwareBuffer screenshot, in Rect bounds, + void generateDisplayHash(in byte[] salt, in HardwareBuffer buffer, in Rect bounds, in String hashAlgorithm, in RemoteCallback callback); /** - * Call to verify that the ScreenshotHash passed in was generated by the system. The result - * will be sent in the callback as a boolean with the key {@link #EXTRA_VERIFICATION_STATUS}. + * Call to verify that the DisplayHash passed in was generated by the system. The result + * will be sent in the callback as a boolean with the key {@link #EXTRA_VERIFIED_DISPLAY_HASH}. * * @param salt The salt value to use when verifying the hmac. This should be the same value that - * was passed to {@link generateScreenshotHash()} to generate the token. - * @param screenshotHash The hash to verify that it was generated by the system. - * @param callback The callback invoked to send back the verification status. + * was passed to {@link generateDisplayHash()} to generate the DisplayHash. + * @param displayHash The hash to verify that it was generated by the system. + * @param callback The callback invoked to send back the VerifiedDisplayHash. */ - void verifyScreenshotHash(in byte[] salt, in ScreenshotHash screenshotHash, - in RemoteCallback callback); + void verifyDisplayHash(in byte[] salt, in DisplayHash displayHash, in RemoteCallback callback); } diff --git a/core/java/android/service/screenshot/OWNERS b/core/java/android/service/displayhash/OWNERS index 0862c05e0ee4..0862c05e0ee4 100644 --- a/core/java/android/service/screenshot/OWNERS +++ b/core/java/android/service/displayhash/OWNERS diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index 2b7cb1db174a..b384b66bf680 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -20,6 +20,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.content.pm.ParceledListSlice; +import android.os.Bundle; import android.os.UserHandle; import android.service.notification.NotificationStats; import android.service.notification.IStatusBarNotificationHolder; @@ -58,4 +59,5 @@ oneway interface INotificationListener void onActionClicked(String key, in Notification.Action action, int source); void onNotificationClicked(String key); void onAllowedAdjustmentsChanged(); + void onNotificationFeedbackReceived(String key, in NotificationRankingUpdate update, in Bundle feedback); } diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index 1d49a7206023..4fd36e590d18 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -30,6 +30,7 @@ import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -90,6 +91,11 @@ public abstract class NotificationAssistantService extends NotificationListenerS = "android.service.notification.NotificationAssistantService"; /** + * Data type: int, the feedback rating score provided by user + */ + public static final String FEEDBACK_RATING = "feedback.rating"; + + /** * @hide */ protected Handler mHandler; @@ -272,6 +278,17 @@ public abstract class NotificationAssistantService extends NotificationListenerS } /** + * Implement this to know when user provides a feedback. + * @param key the notification key + * @param rankingMap The current ranking map that can be used to retrieve ranking information + * for active notifications. + * @param feedback the feedback detail + */ + public void onNotificationFeedbackReceived(@NonNull String key, @NonNull RankingMap rankingMap, + @NonNull Bundle feedback) { + } + + /** * Updates a notification. N.B. this won’t cause * an existing notification to alert, but might allow a future update to * this notification to alert. @@ -455,6 +472,18 @@ public abstract class NotificationAssistantService extends NotificationListenerS public void onAllowedAdjustmentsChanged() { mHandler.obtainMessage(MyHandler.MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED).sendToTarget(); } + + @Override + public void onNotificationFeedbackReceived(String key, NotificationRankingUpdate update, + Bundle feedback) { + applyUpdateLocked(update); + SomeArgs args = SomeArgs.obtain(); + args.arg1 = key; + args.arg2 = getCurrentRanking(); + args.arg3 = feedback; + mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_FEEDBACK_RECEIVED, + args).sendToTarget(); + } } private void setAdjustmentIssuer(@Nullable Adjustment adjustment) { @@ -476,6 +505,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS public static final int MSG_ON_PANEL_HIDDEN = 10; public static final int MSG_ON_NOTIFICATION_VISIBILITY_CHANGED = 11; public static final int MSG_ON_NOTIFICATION_CLICKED = 12; + public static final int MSG_ON_NOTIFICATION_FEEDBACK_RECEIVED = 13; public MyHandler(Looper looper) { super(looper, null, false); @@ -589,6 +619,15 @@ public abstract class NotificationAssistantService extends NotificationListenerS onNotificationClicked(key); break; } + case MSG_ON_NOTIFICATION_FEEDBACK_RECEIVED: { + SomeArgs args = (SomeArgs) msg.obj; + String key = (String) args.arg1; + RankingMap ranking = (RankingMap) args.arg2; + Bundle feedback = (Bundle) args.arg3; + args.recycle(); + onNotificationFeedbackReceived(key, ranking, feedback); + break; + } } } } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index f66f85b9e8cc..7aa5bbc930fb 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -43,6 +43,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Build; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -84,6 +85,10 @@ import java.util.Objects; * android:name="android.service.notification.default_filter_types" * android:value="1,2"> * </meta-data> + * <meta-data + * android:name="android.service.notification.disabled_filter_types" + * android:value="2"> + * </meta-data> * </service></pre> * * <p>The service should wait for the {@link #onListenerConnected()} event @@ -122,6 +127,19 @@ public abstract class NotificationListenerService extends Service { = "android.service.notification.default_filter_types"; /** + * The name of the {@code meta-data} tag containing a comma separated list of default + * integer notification types that this listener never wants to receive. See + * {@link #FLAG_FILTER_TYPE_ONGOING}, + * {@link #FLAG_FILTER_TYPE_CONVERSATIONS}, {@link #FLAG_FILTER_TYPE_ALERTING), + * and {@link #FLAG_FILTER_TYPE_SILENT}. + * <p>Types provided in this list will appear as 'off' and 'disabled' in the user interface, + * so users don't enable a type that the listener will never bridge to their paired devices.</p> + * + */ + public static final String META_DATA_DISABLED_FILTER_TYPES + = "android.service.notification.disabled_filter_types"; + + /** * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * Normal interruption filter. */ @@ -1543,6 +1561,14 @@ public abstract class NotificationListenerService extends Service { mHandler.obtainMessage(MyHandler.MSG_ON_STATUS_BAR_ICON_BEHAVIOR_CHANGED, hideSilentStatusIcons).sendToTarget(); } + + @Override + public void onNotificationFeedbackReceived(String key, NotificationRankingUpdate update, + Bundle feedback) { + // no-op in the listener + } + + } /** @@ -1628,6 +1654,7 @@ public abstract class NotificationListenerService extends Service { private int mSuppressedVisualEffects; private @NotificationManager.Importance int mImportance; private CharSequence mImportanceExplanation; + private float mRankingScore; // System specified group key. private String mOverrideGroupKey; // Notification assistant channel override. @@ -1668,6 +1695,7 @@ public abstract class NotificationListenerService extends Service { out.writeInt(mSuppressedVisualEffects); out.writeInt(mImportance); out.writeCharSequence(mImportanceExplanation); + out.writeFloat(mRankingScore); out.writeString(mOverrideGroupKey); out.writeParcelable(mChannel, flags); out.writeStringList(mOverridePeople); @@ -1705,6 +1733,7 @@ public abstract class NotificationListenerService extends Service { mSuppressedVisualEffects = in.readInt(); mImportance = in.readInt(); mImportanceExplanation = in.readCharSequence(); // may be null + mRankingScore = in.readFloat(); mOverrideGroupKey = in.readString(); // may be null mChannel = in.readParcelable(cl); // may be null mOverridePeople = in.createStringArrayList(); @@ -1802,6 +1831,17 @@ public abstract class NotificationListenerService extends Service { } /** + * Returns the ranking score provided by the {@link NotificationAssistantService} to + * sort the notifications in the shade + * + * @return the ranking score of the notification, range from -1 to 1 + * @hide + */ + public float getRankingScore() { + return mRankingScore; + } + + /** * If the system has overridden the group key, then this will be non-null, and this * key should be used to bundle notifications. */ diff --git a/core/java/android/service/screenshot/ScreenshotHash.java b/core/java/android/service/screenshot/ScreenshotHash.java deleted file mode 100644 index 9ae4192bb676..000000000000 --- a/core/java/android/service/screenshot/ScreenshotHash.java +++ /dev/null @@ -1,256 +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.service.screenshot; - -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.graphics.Rect; -import android.os.Parcel; -import android.os.Parcelable; - -import com.android.internal.util.DataClass; - -/** - * The screenshot hash used to validate information about what was present on screen. - * @hide - * - * TODO: Remove hide and SystemAPI since this will be a public class - */ -@SystemApi -@DataClass(genToString = true, genAidl = true) -public final class ScreenshotHash implements Parcelable { - /** - * The timestamp when the screenshot was generated. - */ - private final long mScreenshotTimeMillis; - - /** - * The bounds of the requested area to take the screenshot. This is in window space passed in - * by the client. - */ - @NonNull - private final Rect mBoundsInWindow; - - /** - * The selected hashing algorithm that generated the image hash. - */ - @NonNull - private final String mHashingAlgorithm; - - /** - * The image hash generated when creating the ScreenshotHash from the screenshot taken. - */ - @NonNull - private final byte[] mImageHash; - - /** - * The hmac generated by the system and used to verify whether this token was generated by - * the system. This should only be accessed by a system process. - */ - @NonNull - private final byte[] mHmac; - - /** - * The hmac generated by the system and used to verify whether this token was generated by - * the system. This should only be accessed by a system process. - * - * @hide - */ - @SystemApi - @NonNull - public byte[] getHmac() { - return mHmac; - } - - - - // Code below generated by codegen v1.0.22. - // - // DO NOT MODIFY! - // CHECKSTYLE:OFF Generated code - // - // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/screenshot - // /ScreenshotHash.java - // - // To exclude the generated code from IntelliJ auto-formatting enable (one-time): - // Settings > Editor > Code Style > Formatter Control - //@formatter:off - - - /** - * Creates a new ScreenshotHash. - * - * @param screenshotTimeMillis - * The timestamp when the screenshot was generated. - * @param boundsInWindow - * The bounds of the requested area to take the screenshot. This is in window space passed in - * by the client. - * @param hashingAlgorithm - * The selected hashing algorithm that generated the image hash. - * @param imageHash - * The image hash generated when creating the ScreenshotHash from the screenshot taken. - * @param hmac - * The hmac generated by the system and used to verify whether this token was generated by - * the system. This should only be accessed by a system process. - */ - @DataClass.Generated.Member - public ScreenshotHash( - long screenshotTimeMillis, - @NonNull Rect boundsInWindow, - @NonNull String hashingAlgorithm, - @NonNull byte[] imageHash, - @NonNull byte[] hmac) { - this.mScreenshotTimeMillis = screenshotTimeMillis; - this.mBoundsInWindow = boundsInWindow; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mBoundsInWindow); - this.mHashingAlgorithm = hashingAlgorithm; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mHashingAlgorithm); - this.mImageHash = imageHash; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mImageHash); - this.mHmac = hmac; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mHmac); - - // onConstructed(); // You can define this method to get a callback - } - - /** - * The timestamp when the screenshot was generated. - */ - @DataClass.Generated.Member - public long getScreenshotTimeMillis() { - return mScreenshotTimeMillis; - } - - /** - * The bounds of the requested area to take the screenshot. This is in window space passed in - * by the client. - */ - @DataClass.Generated.Member - public @NonNull Rect getBoundsInWindow() { - return mBoundsInWindow; - } - - /** - * The selected hashing algorithm that generated the image hash. - */ - @DataClass.Generated.Member - public @NonNull String getHashingAlgorithm() { - return mHashingAlgorithm; - } - - /** - * The image hash generated when creating the ScreenshotHash from the screenshot taken. - */ - @DataClass.Generated.Member - public @NonNull byte[] getImageHash() { - return mImageHash; - } - - @Override - @DataClass.Generated.Member - public String toString() { - // You can override field toString logic by defining methods like: - // String fieldNameToString() { ... } - - return "ScreenshotHash { " + - "screenshotTimeMillis = " + mScreenshotTimeMillis + ", " + - "boundsInWindow = " + mBoundsInWindow + ", " + - "hashingAlgorithm = " + mHashingAlgorithm + ", " + - "imageHash = " + java.util.Arrays.toString(mImageHash) + ", " + - "hmac = " + java.util.Arrays.toString(mHmac) + - " }"; - } - - @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.writeLong(mScreenshotTimeMillis); - dest.writeTypedObject(mBoundsInWindow, flags); - dest.writeString(mHashingAlgorithm); - dest.writeByteArray(mImageHash); - dest.writeByteArray(mHmac); - } - - @Override - @DataClass.Generated.Member - public int describeContents() { return 0; } - - /** @hide */ - @SuppressWarnings({"unchecked", "RedundantCast"}) - @DataClass.Generated.Member - /* package-private */ ScreenshotHash(@NonNull Parcel in) { - // You can override field unparcelling by defining methods like: - // static FieldType unparcelFieldName(Parcel in) { ... } - - long screenshotTimeMillis = in.readLong(); - Rect boundsInWindow = (Rect) in.readTypedObject(Rect.CREATOR); - String hashingAlgorithm = in.readString(); - byte[] imageHash = in.createByteArray(); - byte[] hmac = in.createByteArray(); - - this.mScreenshotTimeMillis = screenshotTimeMillis; - this.mBoundsInWindow = boundsInWindow; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mBoundsInWindow); - this.mHashingAlgorithm = hashingAlgorithm; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mHashingAlgorithm); - this.mImageHash = imageHash; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mImageHash); - this.mHmac = hmac; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mHmac); - - // onConstructed(); // You can define this method to get a callback - } - - @DataClass.Generated.Member - public static final @NonNull Parcelable.Creator<ScreenshotHash> CREATOR - = new Parcelable.Creator<ScreenshotHash>() { - @Override - public ScreenshotHash[] newArray(int size) { - return new ScreenshotHash[size]; - } - - @Override - public ScreenshotHash createFromParcel(@NonNull Parcel in) { - return new ScreenshotHash(in); - } - }; - - @DataClass.Generated( - time = 1612383172822L, - codegenVersion = "1.0.22", - sourceFile = "frameworks/base/core/java/android/service/screenshot/ScreenshotHash.java", - inputSignatures = "private final long mScreenshotTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashingAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate final @android.annotation.NonNull byte[] mHmac\npublic @android.annotation.SystemApi @android.annotation.NonNull byte[] getHmac()\nclass ScreenshotHash extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)") - @Deprecated - private void __metadata() {} - - - //@formatter:on - // End of generated code - -} diff --git a/core/java/android/service/screenshot/ScreenshotHasherService.java b/core/java/android/service/screenshot/ScreenshotHasherService.java deleted file mode 100644 index d96cc7eaba7a..000000000000 --- a/core/java/android/service/screenshot/ScreenshotHasherService.java +++ /dev/null @@ -1,160 +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.service.screenshot; - -import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.app.Service; -import android.content.Intent; -import android.graphics.Rect; -import android.hardware.HardwareBuffer; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.RemoteCallback; - -/** - * A service that handles generating and verify {@link ScreenshotHash}. - * - * The service will generate a ScreenshotHash based on arguments passed in. Then later that - * same ScreenshotHash can be verified to determine that it was created by the system. - * - * @hide - */ -@SystemApi -public abstract class ScreenshotHasherService extends Service { - /** @hide **/ - public static final String EXTRA_SCREENSHOT_HASH = - "android.service.screenshot.extra.SCREENSHOT_HASH"; - - /** @hide **/ - public static final String EXTRA_VERIFICATION_STATUS = - "android.service.screenshot.extra.VERIFICATION_STATUS"; - - /** - * Manifest metadata key for the resource string array containing the names of all hashing - * algorithms provided by the service. - * - * @hide - */ - public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = - "android.screenshot.available_algorithms"; - - /** - * The {@link Intent} action that must be declared as handled by a service in its manifest - * for the system to recognize it as a ScreenshotHash providing service. - * - * @hide - */ - @SystemApi - public static final String SERVICE_INTERFACE = - "android.service.screenshot.ScreenshotHasherService"; - - private ScreenshotHasherServiceWrapper mWrapper; - private Handler mHandler; - - public ScreenshotHasherService() { - } - - @Override - public void onCreate() { - super.onCreate(); - mWrapper = new ScreenshotHasherServiceWrapper(); - mHandler = new Handler(Looper.getMainLooper(), null, true); - } - - @NonNull - @Override - public final IBinder onBind(@NonNull Intent intent) { - return mWrapper; - } - - /** - * Generates the ScreenshotHash that can be used to validate that the system generated the - * token. - * - * @param salt The salt to use when generating the hmac. This should be unique to the - * caller so the token cannot be verified by any other process. - * @param screenshot The screenshot buffer for the content. - * @param bounds The size and position of the content being screenshot in the window. - * @param hashAlgorithm The String for the hashing algorithm to use based values in - * {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}. - * @return A ScreenshotHash that can be used to validate information about the content. - * Returns null when the arguments sent are invalid. - */ - @Nullable - public abstract ScreenshotHash onGenerateScreenshotHash(@NonNull byte[] salt, - @NonNull HardwareBuffer screenshot, @NonNull Rect bounds, - @NonNull String hashAlgorithm); - - /** - * Call to verify that the ScreenshotHash passed in was generated by the system. - * - * @param salt The salt value to use when verifying the hmac. This should be the - * same value that was passed to - * {@link #onGenerateScreenshotHash(byte[], - * HardwareBuffer, Rect, String)} to - * generate the token. - * @param screenshotHash The token to verify that it was generated by the system. - * @return true if the token can be verified that it was generated by the system. - */ - public abstract boolean onVerifyScreenshotHash(@NonNull byte[] salt, - @NonNull ScreenshotHash screenshotHash); - - private void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds, - String hashAlgorithm, RemoteCallback callback) { - ScreenshotHash screenshotHash = onGenerateScreenshotHash(salt, screenshot, - bounds, - hashAlgorithm); - final Bundle data = new Bundle(); - data.putParcelable(EXTRA_SCREENSHOT_HASH, screenshotHash); - callback.sendResult(data); - } - - private void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash, - RemoteCallback callback) { - boolean verificationStatus = onVerifyScreenshotHash(salt, screenshotHash); - final Bundle data = new Bundle(); - data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus); - callback.sendResult(data); - } - - private final class ScreenshotHasherServiceWrapper extends - IScreenshotHasherService.Stub { - @Override - public void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds, - String hashAlgorithm, RemoteCallback callback) { - mHandler.sendMessage( - obtainMessage(ScreenshotHasherService::generateScreenshotHash, - ScreenshotHasherService.this, salt, screenshot, bounds, - hashAlgorithm, callback)); - } - - @Override - public void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash, - RemoteCallback callback) { - mHandler.sendMessage( - obtainMessage(ScreenshotHasherService::verifyScreenshotHash, - ScreenshotHasherService.this, salt, screenshotHash, - callback)); - } - } -} diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java index 87add57f383d..1e07a8748af9 100644 --- a/core/java/android/service/storage/ExternalStorageService.java +++ b/core/java/android/service/storage/ExternalStorageService.java @@ -102,14 +102,6 @@ public abstract class ExternalStorageService extends Service { */ public static final String EXTRA_PACKAGE_NAME = "android.service.storage.extra.package_name"; - /** - * {@link Bundle} key for a {@link Long} value. - * - * {@hide} - */ - public static final String EXTRA_ANR_TIMEOUT_MS = - "android.service.storage.extra.anr_timeout_ms"; - /** @hide */ @IntDef(flag = true, prefix = {"FLAG_SESSION_"}, value = {FLAG_SESSION_TYPE_FUSE, FLAG_SESSION_ATTRIBUTE_INDEXABLE}) @@ -178,12 +170,12 @@ public abstract class ExternalStorageService extends Service { } /** - * Called when {@code packageName} is about to ANR + * Called when {@code packageName} is about to ANR. The {@link ExternalStorageService} can + * show a progress dialog for the {@code reason}. * - * @return ANR dialog delay in milliseconds */ - public long onGetAnrDelayMillis(@NonNull String packageName, int uid) { - throw new UnsupportedOperationException("onGetAnrDelayMillis not implemented"); + public void onAnrDelayStarted(@NonNull String packageName, int uid, int tid, int reason) { + throw new UnsupportedOperationException("onAnrDelayStarted not implemented"); } @Override @@ -247,14 +239,14 @@ public abstract class ExternalStorageService extends Service { } @Override - public void getAnrDelayMillis(String packageName, int uid, RemoteCallback callback) - throws RemoteException { + public void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason, + RemoteCallback callback) throws RemoteException { mHandler.post(() -> { try { - long timeoutMs = onGetAnrDelayMillis(packageName, uid); - sendTimeoutResult(packageName, timeoutMs, null /* throwable */, callback); + onAnrDelayStarted(packageName, uid, tid, reason); + sendResult(packageName, null /* throwable */, callback); } catch (Throwable t) { - sendTimeoutResult(packageName, 0 /* timeoutMs */, t, callback); + sendResult(packageName, t, callback); } }); } @@ -267,16 +259,5 @@ public abstract class ExternalStorageService extends Service { } callback.sendResult(bundle); } - - private void sendTimeoutResult(String packageName, long timeoutMs, Throwable throwable, - RemoteCallback callback) { - Bundle bundle = new Bundle(); - bundle.putString(EXTRA_PACKAGE_NAME, packageName); - bundle.putLong(EXTRA_ANR_TIMEOUT_MS, timeoutMs); - if (throwable != null) { - bundle.putParcelable(EXTRA_ERROR, new ParcelableException(throwable)); - } - callback.sendResult(bundle); - } } } diff --git a/core/java/android/service/storage/IExternalStorageService.aidl b/core/java/android/service/storage/IExternalStorageService.aidl index 2e0bd86c3d7d..ba98efa58f7c 100644 --- a/core/java/android/service/storage/IExternalStorageService.aidl +++ b/core/java/android/service/storage/IExternalStorageService.aidl @@ -32,5 +32,6 @@ oneway interface IExternalStorageService in RemoteCallback callback); void freeCache(@utf8InCpp String sessionId, in String volumeUuid, long bytes, in RemoteCallback callback); - void getAnrDelayMillis(String packageName, int uid, in RemoteCallback callback); + void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason, + in RemoteCallback callback); }
\ No newline at end of file diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java index 3c35d2ca78eb..7f1c5ff96636 100644 --- a/core/java/android/service/voice/HotwordDetectionService.java +++ b/core/java/android/service/voice/HotwordDetectionService.java @@ -19,15 +19,18 @@ package android.service.voice; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.CallSuper; +import android.annotation.DurationMillisLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; +import android.media.AudioFormat; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; @@ -57,14 +60,21 @@ public abstract class HotwordDetectionService extends Service { private final IHotwordDetectionService mInterface = new IHotwordDetectionService.Stub() { @Override - public void detectFromDspSource(int sessionId, IDspHotwordDetectionCallback callback) + public void detectFromDspSource( + ParcelFileDescriptor audioStream, + AudioFormat audioFormat, + long timeoutMillis, + IDspHotwordDetectionCallback callback) throws RemoteException { if (DBG) { Log.d(TAG, "#detectFromDspSource"); } mHandler.sendMessage(obtainMessage(HotwordDetectionService::onDetectFromDspSource, HotwordDetectionService.this, - sessionId, new DspHotwordDetectionCallback(callback))); + audioStream, + audioFormat, + timeoutMillis, + new DspHotwordDetectionCallback(callback))); } }; @@ -89,15 +99,24 @@ public abstract class HotwordDetectionService extends Service { /** * Detect the audio data generated from Dsp. * - * @param sessionId The session to use when attempting to capture more audio from the DSP - * hardware. + * <p>Note: the clients are supposed to call {@code close} on the input stream when they are + * done with the operation in order to free up resources. + * + * @param audioStream Stream containing audio bytes returned from DSP + * @param audioFormat Format of the supplied audio + * @param timeoutMillis Timeout in milliseconds for the operation to invoke the callback. If + * the application fails to abide by the timeout, system will close the + * microphone and cancel the operation. * @param callback Use {@link HotwordDetectionService#DspHotwordDetectionCallback} to return * the detected result. * * @hide */ @SystemApi - public void onDetectFromDspSource(int sessionId, + public void onDetectFromDspSource( + @NonNull ParcelFileDescriptor audioStream, + @NonNull AudioFormat audioFormat, + @DurationMillisLong long timeoutMillis, @NonNull DspHotwordDetectionCallback callback) { } diff --git a/core/java/android/service/voice/IHotwordDetectionService.aidl b/core/java/android/service/voice/IHotwordDetectionService.aidl index 990ad4574788..cbe76e4bf69f 100644 --- a/core/java/android/service/voice/IHotwordDetectionService.aidl +++ b/core/java/android/service/voice/IHotwordDetectionService.aidl @@ -16,6 +16,8 @@ package android.service.voice; +import android.media.AudioFormat; +import android.os.ParcelFileDescriptor; import android.service.voice.IDspHotwordDetectionCallback; /** @@ -24,5 +26,9 @@ import android.service.voice.IDspHotwordDetectionCallback; * @hide */ oneway interface IHotwordDetectionService { - void detectFromDspSource(int sessionId, in IDspHotwordDetectionCallback callback); + void detectFromDspSource( + in ParcelFileDescriptor audioStream, + in AudioFormat audioFormat, + long timeoutMillis, + in IDspHotwordDetectionCallback callback); } diff --git a/core/java/android/service/wallpaper/Android.bp b/core/java/android/service/wallpaper/Android.bp index ffbdb035c36c..a527d3d10fdf 100644 --- a/core/java/android/service/wallpaper/Android.bp +++ b/core/java/android/service/wallpaper/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "WallpaperSharedLib", diff --git a/core/java/android/service/wallpaper/EngineWindowPage.java b/core/java/android/service/wallpaper/EngineWindowPage.java new file mode 100644 index 000000000000..5ed0ad6f2aeb --- /dev/null +++ b/core/java/android/service/wallpaper/EngineWindowPage.java @@ -0,0 +1,96 @@ +/* + * 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.service.wallpaper; + +import android.app.WallpaperColors; +import android.graphics.Bitmap; +import android.graphics.RectF; +import android.util.ArrayMap; +import android.util.ArraySet; + +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; + +/** + * This class represents a page of a launcher page used by the wallpaper + * @hide + */ +public class EngineWindowPage { + private Bitmap mScreenShot; + private volatile long mLastUpdateTime = 0; + private Set<RectF> mCallbackAreas = new ArraySet<>(); + private Map<RectF, WallpaperColors> mRectFColors = new ArrayMap<>(); + + /** should be locked extrnally */ + public void addArea(RectF area) { + mCallbackAreas.add(area); + } + + /** should be locked extrnally */ + public void addWallpaperColors(RectF area, WallpaperColors colors) { + mCallbackAreas.add(area); + mRectFColors.put(area, colors); + } + + /** get screenshot bitmap */ + public Bitmap getBitmap() { + if (mScreenShot == null || mScreenShot.isRecycled()) return null; + return mScreenShot; + } + + /** remove callbacks for an area */ + public void removeArea(RectF area) { + mCallbackAreas.remove(area); + mRectFColors.remove(area); + } + + /** set the last time the screenshot was updated */ + public void setLastUpdateTime(long lastUpdateTime) { + mLastUpdateTime = lastUpdateTime; + } + + /** get last screenshot time */ + public long getLastUpdateTime() { + return mLastUpdateTime; + } + + /** get colors for an area */ + public WallpaperColors getColors(RectF rect) { + return mRectFColors.get(rect); + } + + /** set the new bitmap version */ + public void setBitmap(Bitmap screenShot) { + mScreenShot = screenShot; + } + + /** get areas of interest */ + public Set<RectF> getAreas() { + return mCallbackAreas; + } + + /** run operations on this page */ + public synchronized void execSync(Consumer<EngineWindowPage> run) { + run.accept(this); + } + + /** nullify the area color */ + public void removeColor(RectF colorArea) { + mRectFColors.remove(colorArea); + } +} diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl index f334d9d3e874..f81ed3410d58 100644 --- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl +++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl @@ -16,6 +16,7 @@ package android.service.wallpaper; +import android.graphics.RectF; import android.os.ParcelFileDescriptor; import android.service.wallpaper.IWallpaperEngine; import android.app.WallpaperColors; @@ -28,4 +29,5 @@ interface IWallpaperConnection { void engineShown(IWallpaperEngine engine); ParcelFileDescriptor setWallpaper(String name); void onWallpaperColorsChanged(in WallpaperColors colors, int displayId); + void onLocalWallpaperColorsChanged(in RectF area, in WallpaperColors colors, int displayId); } diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl index 90392e65794a..fbb449d3875e 100644 --- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl +++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl @@ -16,7 +16,10 @@ package android.service.wallpaper; +import android.app.ILocalWallpaperColorConsumer; +import android.app.WallpaperColors; import android.graphics.Rect; +import android.graphics.RectF; import android.view.MotionEvent; import android.os.Bundle; @@ -39,4 +42,6 @@ oneway interface IWallpaperEngine { void destroy(); void setZoomOut(float scale); void scalePreview(in Rect positionInWindow); + void removeLocalColorsAreas(in List<RectF> regions); + void addLocalColorsAreas(in List<RectF> regions); } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 82e0b4a1aecc..920c6a727e9a 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -23,6 +23,7 @@ import static android.graphics.Matrix.MSKEW_Y; import static android.view.View.SYSTEM_UI_FLAG_VISIBLE; import android.annotation.FloatRange; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -42,6 +43,7 @@ import android.graphics.Matrix; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; @@ -53,6 +55,8 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.SystemClock; +import android.os.Trace; +import android.util.ArraySet; import android.util.Log; import android.util.MergedConfiguration; import android.view.Display; @@ -66,6 +70,8 @@ import android.view.InputEventReceiver; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.MotionEvent; +import android.view.PixelCopy; +import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.View; @@ -83,6 +89,9 @@ import com.android.internal.view.BaseSurfaceHolder; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; @@ -116,6 +125,11 @@ public abstract class WallpaperService extends Service { static final String TAG = "WallpaperService"; static final boolean DEBUG = false; + static final float MIN_PAGE_ALLOWED_MARGIN = .05f; + private static final int MIN_BITMAP_SCREENSHOT_WIDTH = 64; + private static final long DEFAULT_UPDATE_SCREENSHOT_DURATION = 60 * 1000; //Once per minute + private static final @NonNull RectF LOCAL_COLOR_BOUNDS = + new RectF(0, 0, 1, 1); private static final int DO_ATTACH = 10; private static final int DO_DETACH = 20; @@ -134,6 +148,8 @@ public abstract class WallpaperService extends Service { private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050; private static final int MSG_ZOOM = 10100; private static final int MSG_SCALE_PREVIEW = 10110; + private static final List<Float> PROHIBITED_STEPS = Arrays.asList(0f, Float.POSITIVE_INFINITY, + Float.NEGATIVE_INFINITY); private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; @@ -158,6 +174,14 @@ public abstract class WallpaperService extends Service { */ public class Engine { IWallpaperEngineWrapper mIWallpaperEngine; + final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4); + final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4); + + // 2D matrix [x][y] to represent a page of a portion of a window + EngineWindowPage[] mWindowPages = new EngineWindowPage[1]; + Bitmap mLastScreenshot; + int mLastWindowPage = -1; + float mLastPageOffset = 0; // Copies from mIWallpaperEngine. HandlerCaller mCaller; @@ -409,8 +433,8 @@ public abstract class WallpaperService extends Service { */ @VisibleForTesting public Engine(Supplier<Long> clockFunction, Handler handler) { - mClockFunction = clockFunction; - mHandler = handler; + mClockFunction = clockFunction; + mHandler = handler; } /** @@ -448,6 +472,19 @@ public abstract class WallpaperService extends Service { } /** + * Return whether the wallpaper is capable of extracting local colors in a rectangle area, + * Must implement without calling super: + * {@link #addLocalColorsAreas(List)} + * {@link #removeLocalColorsAreas(List)} + * When local colors change, call {@link #notifyLocalColorsChanged(List, List)} + * See {@link com.android.systemui.ImageWallpaper} for an example + * @hide + */ + public boolean supportsLocalColorExtraction() { + return false; + } + + /** * Returns true if this engine is running in preview mode -- that is, * it is being shown to the user before they select it as the actual * wallpaper. @@ -691,6 +728,8 @@ public abstract class WallpaperService extends Service { Log.w(TAG, "Can't notify system because wallpaper connection " + "was not established."); } + resetWindowPages(); + processLocalColors(mPendingXOffset, mPendingXOffsetStep); } catch (RemoteException e) { Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e); } @@ -714,6 +753,28 @@ public abstract class WallpaperService extends Service { } /** + * Send the changed local color areas for the connection + * @param regions + * @param colors + * @hide + */ + public void notifyLocalColorsChanged(@NonNull List<RectF> regions, + @NonNull List<WallpaperColors> colors) + throws RuntimeException { + for (int i = 0; i < regions.size() && i < colors.size() && colors.get(i) != null; i++) { + try { + mConnection.onLocalWallpaperColorsChanged( + regions.get(i), + colors.get(i), + mDisplayContext.getDisplayId() + ); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + } + + /** * Sets internal engine state. Only for testing. * @param created {@code true} or {@code false}. * @hide @@ -1068,7 +1129,9 @@ public abstract class WallpaperService extends Service { mIsCreating = false; mSurfaceCreated = true; if (redrawNeeded) { + resetWindowPages(); mSession.finishDrawing(mWindow, null /* postDrawTransaction */); + processLocalColors(mPendingXOffset, mPendingXOffsetStep); } reposition(); mIWallpaperEngine.reportShown(); @@ -1209,6 +1272,7 @@ public abstract class WallpaperService extends Service { if (!mDestroyed) { mVisible = visible; reportVisibility(); + if (visible) processLocalColors(mPendingXOffset, mPendingXOffsetStep); } } @@ -1278,6 +1342,403 @@ public abstract class WallpaperService extends Service { } catch (RemoteException e) { } } + + // setup local color extraction data + processLocalColors(xOffset, xOffsetStep); + } + + private void processLocalColors(float xOffset, float xOffsetStep) { + // implemented by the wallpaper + if (supportsLocalColorExtraction()) return; + if (DEBUG) { + Log.d(TAG, "processLocalColors " + xOffset + " of step " + + xOffsetStep); + } + //below is the default implementation + if (xOffset % xOffsetStep > MIN_PAGE_ALLOWED_MARGIN) return; + int xPage; + int xPages; + if (!validStep(xOffsetStep)) { + if (DEBUG) { + Log.w(TAG, "invalid offset step " + xOffsetStep); + } + xOffset = 0; + xOffsetStep = 1; + xPage = 0; + xPages = 1; + } else { + xPages = Math.round(1 / xOffsetStep) + 1; + xOffsetStep = (float) 1 / (float) xPages; + float shrink = (float) (xPages - 1) / (float) xPages; + xOffset *= shrink; + xPage = Math.round(xOffset / xOffsetStep); + } + if (DEBUG) { + Log.d(TAG, "xPages " + xPages + " xPage " + xPage); + Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset); + } + EngineWindowPage current; + synchronized (mLock) { + if (mWindowPages.length == 0 || (mWindowPages.length != xPages)) { + mWindowPages = new EngineWindowPage[xPages]; + initWindowPages(mWindowPages, xOffsetStep); + } + if (mLocalColorsToAdd.size() != 0) { + for (RectF colorArea : mLocalColorsToAdd) { + if (!isValid(colorArea)) continue; + mLocalColorAreas.add(colorArea); + int colorPage = getRectFPage(colorArea, xOffsetStep); + EngineWindowPage currentPage = mWindowPages[colorPage]; + if (currentPage == null) { + currentPage = new EngineWindowPage(); + currentPage.addArea(colorArea); + mWindowPages[colorPage] = currentPage; + } else { + currentPage.setLastUpdateTime(0); + currentPage.removeColor(colorArea); + } + } + mLocalColorsToAdd.clear(); + } + if (xPage >= mWindowPages.length) { + if (DEBUG) { + Log.e(TAG, "error xPage >= mWindowPages.length page: " + xPage); + Log.e(TAG, "error on page " + xPage + " out of " + xPages); + Log.e(TAG, + "error on xOffsetStep " + xOffsetStep + " xOffset " + xOffset); + } + xPage = mWindowPages.length - 1; + } + current = mWindowPages[xPage]; + if (current == null) { + if (DEBUG) Log.d(TAG, "making page " + xPage + " out of " + xPages); + if (DEBUG) { + Log.d(TAG, "xOffsetStep " + xOffsetStep + " xOffset " + xOffset); + } + current = new EngineWindowPage(); + mWindowPages[xPage] = current; + } + } + updatePage(current, xPage, xPages, xOffsetStep); + } + + private void initWindowPages(EngineWindowPage[] windowPages, float step) { + for (int i = 0; i < windowPages.length; i++) { + windowPages[i] = new EngineWindowPage(); + } + mLocalColorAreas.addAll(mLocalColorsToAdd); + mLocalColorsToAdd.clear(); + for (RectF area: mLocalColorAreas) { + if (!isValid(area)) { + mLocalColorAreas.remove(area); + continue; + } + int pageNum = getRectFPage(area, step); + windowPages[pageNum].addArea(area); + } + } + + void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages, + float xOffsetStep) { + // to save creating a runnable, check twice + long current = System.nanoTime() / 1_000_000; + long lapsed = current - currentPage.getLastUpdateTime(); + if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) { + return; + } + Surface surface = mSurfaceHolder.getSurface(); + boolean widthIsLarger = + mSurfaceControl.getWidth() > mSurfaceControl.getHeight(); + int smaller = widthIsLarger ? mSurfaceControl.getWidth() + : mSurfaceControl.getHeight(); + float ratio = (float) MIN_BITMAP_SCREENSHOT_WIDTH / (float) smaller; + int width = (int) (ratio * mSurfaceControl.getWidth()); + int height = (int) (ratio * mSurfaceControl.getHeight()); + if (width <= 0 || height <= 0) { + Log.e(TAG, "wrong width and height values of bitmap " + width + " " + height); + return; + } + Bitmap screenShot = Bitmap.createBitmap(width, height, + Bitmap.Config.ARGB_8888); + final Bitmap finalScreenShot = screenShot; + Trace.beginSection("WallpaperService#pixelCopy"); + PixelCopy.request(surface, screenShot, (res) -> { + Trace.endSection(); + if (DEBUG) Log.d(TAG, "result of pixel copy is " + res); + if (res != PixelCopy.SUCCESS) { + Bitmap lastBitmap = currentPage.getBitmap(); + currentPage.execSync((p) -> { + // assign the last bitmap taken for now + p.setBitmap(mLastScreenshot); + }); + Bitmap lastScreenshot = mLastScreenshot; + if (lastScreenshot != null && !lastScreenshot.isRecycled() + && !Objects.equals(lastBitmap, lastScreenshot)) { + updatePageColors(currentPage, pageIndx, numPages, xOffsetStep); + } + } else { + mLastScreenshot = finalScreenShot; + // going to hold this lock for a while + currentPage.execSync((p) -> { + p.setBitmap(finalScreenShot); + p.setLastUpdateTime(current); + }); + updatePageColors(currentPage, pageIndx, numPages, xOffsetStep); + } + }, mHandler); + + } + // locked by the passed page + private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages, + float xOffsetStep) { + if (page.getBitmap() == null) return; + if (DEBUG) { + Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas " + + page.getAreas().size() + " and bitmap size of " + + page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight()); + } + for (RectF area: page.getAreas()) { + RectF subArea = generateSubRect(area, pageIndx, numPages); + Bitmap b = page.getBitmap(); + int x = Math.round(b.getWidth() * subArea.left); + int y = Math.round(b.getHeight() * subArea.top); + int width = Math.round(b.getWidth() * subArea.width()); + int height = Math.round(b.getHeight() * subArea.height()); + Bitmap target; + try { + target = Bitmap.createBitmap(page.getBitmap(), x, y, width, height); + } catch (Exception e) { + Log.e(TAG, "Error creating page local color bitmap", e); + continue; + } + WallpaperColors color = WallpaperColors.fromBitmap(target); + target.recycle(); + WallpaperColors currentColor = page.getColors(area); + + if (DEBUG) { + Log.d(TAG, "getting local bitmap area x " + x + " y " + y + + " width " + width + " height " + height + " for sub area " + subArea + + " and with page " + pageIndx + " of " + numPages); + + } + if (currentColor == null || !color.equals(currentColor)) { + page.addWallpaperColors(area, color); + if (DEBUG) { + Log.d(TAG, "onLocalWallpaperColorsChanged" + + " local color callback for area" + area + " for page " + pageIndx + + " of " + numPages); + } + try { + mConnection.onLocalWallpaperColorsChanged(area, color, + mDisplayContext.getDisplayId()); + } catch (RemoteException e) { + Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e); + } + } + } + } + + private RectF generateSubRect(RectF in, int pageInx, int numPages) { + float minLeft = (float) (pageInx) / (float) (numPages); + float maxRight = (float) (pageInx + 1) / (float) (numPages); + float left = in.left; + float right = in.right; + + // bound rect + if (left < minLeft) left = minLeft; + if (right > maxRight) right = maxRight; + + // scale up the sub area then trim + left = (left * (float) numPages) % 1f; + right = (right * (float) numPages) % 1f; + if (right == 0f) { + right = 1f; + } + + return new RectF(left, in.top, right, in.bottom); + } + + private void resetWindowPages() { + if (supportsLocalColorExtraction()) return; + mLastWindowPage = -1; + synchronized (mLock) { + for (int i = 0; i < mWindowPages.length; i++) { + EngineWindowPage page = mWindowPages[i]; + if (page != null) { + page.execSync((p) -> { + p.setLastUpdateTime(0L); + }); + } + } + } + } + + private int getRectFPage(RectF area, float step) { + if (!isValid(area)) return 0; + if (!validStep(step)) return 0; + int pages = Math.round(1 / step); + int page = Math.round(area.centerX() * pages); + if (page == pages) return pages - 1; + if (page == mWindowPages.length) page = mWindowPages.length - 1; + return page; + } + + /** + * Add local colors areas of interest + * @param regions list of areas + * @hide + */ + public void addLocalColorsAreas(@NonNull List<RectF> regions) { + if (supportsLocalColorExtraction()) return; + if (DEBUG) { + Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions); + } + float step = mPendingXOffsetStep; + + List<WallpaperColors> colors = getLocalWallpaperColors(regions); + synchronized (mLock) { + if (!validStep(step)) { + step = 0; + } + for (int i = 0; i < regions.size(); i++) { + RectF area = regions.get(i); + if (!isValid(area)) continue; + int pageInx = getRectFPage(area, step); + // no page should be null + EngineWindowPage page = mWindowPages[pageInx]; + + if (page != null) { + mLocalColorAreas.add(area); + page.addArea(area); + WallpaperColors color = colors.get(i); + if (color != null && !color.equals(page.getColors(area))) { + page.execSync(p -> { + p.addWallpaperColors(area, color); + }); + } + } else { + mLocalColorsToAdd.add(area); + } + } + } + + for (int i = 0; i < colors.size() && colors.get(i) != null; i++) { + try { + mConnection.onLocalWallpaperColorsChanged(regions.get(i), colors.get(i), + mDisplayContext.getDisplayId()); + } catch (RemoteException e) { + Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e); + return; + } + } + } + + /** + * Remove local colors areas of interest if they exist + * @param regions list of areas + * @hide + */ + public void removeLocalColorsAreas(@NonNull List<RectF> regions) { + if (supportsLocalColorExtraction()) return; + synchronized (mLock) { + float step = mPendingXOffsetStep; + mLocalColorsToAdd.removeAll(regions); + mLocalColorAreas.removeAll(regions); + if (!validStep(step)) { + return; + } + for (int i = 0; i < regions.size(); i++) { + RectF area = regions.get(i); + if (!isValid(area)) continue; + int pageInx = getRectFPage(area, step); + // no page should be null + EngineWindowPage page = mWindowPages[pageInx]; + if (page != null) { + page.execSync(p -> { + p.removeArea(area); + }); + } + } + } + } + + private @NonNull List<WallpaperColors> getLocalWallpaperColors(@NonNull List<RectF> areas) { + ArrayList<WallpaperColors> colors = new ArrayList<>(areas.size()); + float step = mPendingXOffsetStep; + if (!validStep(step)) { + if (DEBUG) Log.d(TAG, "invalid step size " + step); + step = 1.0f; + } + for (int i = 0; i < areas.size(); i++) { + RectF currentArea = areas.get(i); + EngineWindowPage page; + RectF area; + int pageIndx; + synchronized (mLock) { + pageIndx = getRectFPage(currentArea, step); + if (mWindowPages.length == 0 || pageIndx < 0 + || pageIndx > mWindowPages.length || !isValid(currentArea)) { + colors.add(null); + continue; + } + area = generateSubRect(currentArea, pageIndx, mWindowPages.length); + page = mWindowPages[pageIndx]; + } + if (page == null) { + colors.add(null); + continue; + } + float finalStep = step; + int finalPageIndx = pageIndx; + Bitmap screenShot = page.getBitmap(); + if (screenShot == null || screenShot.isRecycled()) { + if (DEBUG) { + Log.d(TAG, "invalid bitmap " + screenShot + + " for page " + finalPageIndx); + } + page.setLastUpdateTime(0); + colors.add(null); + continue; + } + Bitmap b = screenShot; + Rect subImage = new Rect( + Math.round(area.left * b.getWidth() / finalStep), + Math.round(area.top * b.getHeight()), + Math.round(area.right * b.getWidth() / finalStep), + Math.round(area.bottom * b.getHeight()) + ); + subImage = fixRect(b, subImage); + if (DEBUG) { + Log.d(TAG, "getting subbitmap of " + subImage.toString() + + " for RectF " + area.toString() + + " screenshot width " + screenShot.getWidth() + " height " + + screenShot.getHeight()); + } + Bitmap colorImg = Bitmap.createBitmap(screenShot, + subImage.left, subImage.top, subImage.width(), subImage.height()); + if (DEBUG) { + Log.d(TAG, "created bitmap " + colorImg.getWidth() + ", " + + colorImg.getHeight()); + } + WallpaperColors color = WallpaperColors.fromBitmap(colorImg); + colors.add(color); + } + return colors; + } + + // fix the rect to be included within the bounds of the bitmap + private Rect fixRect(Bitmap b, Rect r) { + r.left = r.left >= r.right || r.left >= b.getWidth() || r.left > 0 + ? 0 + : r.left; + r.right = r.left >= r.right || r.right > b.getWidth() + ? b.getWidth() + : r.right; + return r; + } + + private boolean validStep(float step) { + return !PROHIBITED_STEPS.contains(step) && step > 0. && step <= 1.; } void doCommand(WallpaperCommand cmd) { @@ -1371,6 +1832,16 @@ public abstract class WallpaperService extends Service { }; } + private boolean isValid(RectF area) { + boolean valid = area.bottom > area.top && area.left < area.right + && LOCAL_COLOR_BOUNDS.contains(area); + return valid; + } + + private boolean inRectFRange(float number) { + return number >= 0f && number <= 1f; + } + class IWallpaperEngineWrapper extends IWallpaperEngine.Stub implements HandlerCaller.Callback { private final HandlerCaller mCaller; @@ -1477,6 +1948,14 @@ public abstract class WallpaperService extends Service { mCaller.sendMessage(msg); } + public void addLocalColorsAreas(List<RectF> regions) { + mEngine.addLocalColorsAreas(regions); + } + + public void removeLocalColorsAreas(List<RectF> regions) { + mEngine.removeLocalColorsAreas(regions); + } + public void destroy() { Message msg = mCaller.obtainMessage(DO_DETACH); mCaller.sendMessage(msg); @@ -1516,14 +1995,15 @@ public abstract class WallpaperService extends Service { } switch (message.what) { case DO_ATTACH: { + Engine engine = onCreateEngine(); + mEngine = engine; try { mConnection.attachEngine(this, mDisplayId); } catch (RemoteException e) { + engine.detach(); Log.w(TAG, "Wallpaper host disappeared", e); return; } - Engine engine = onCreateEngine(); - mEngine = engine; mActiveEngines.add(engine); engine.attach(this); return; diff --git a/core/java/android/speech/IRecognitionService.aidl b/core/java/android/speech/IRecognitionService.aidl index f91e122ea9cb..cc1cdedd0f96 100644 --- a/core/java/android/speech/IRecognitionService.aidl +++ b/core/java/android/speech/IRecognitionService.aidl @@ -43,7 +43,7 @@ oneway interface IRecognitionService { * @param featureId The feature in the package */ void startListening(in Intent recognizerIntent, in IRecognitionListener listener, - String packageName, String featureId); + String packageName, String featureId, int callingUid); /** * Stops listening for speech. Speech captured so far will be recognized as @@ -62,6 +62,7 @@ oneway interface IRecognitionService { * @param listener to receive callbacks, note that this must be non-null * @param packageName the package name calling this API * @param featureId The feature in the package + * @param isShutdown Whether the cancellation is caused by a client calling #shutdown */ - void cancel(in IRecognitionListener listener, String packageName, String featureId); + void cancel(in IRecognitionListener listener, String packageName, String featureId, boolean isShutdown); } diff --git a/core/java/android/speech/IRecognitionServiceManager.aidl b/core/java/android/speech/IRecognitionServiceManager.aidl index 7158ba2f9f63..8e5292d1ddf1 100644 --- a/core/java/android/speech/IRecognitionServiceManager.aidl +++ b/core/java/android/speech/IRecognitionServiceManager.aidl @@ -16,6 +16,8 @@ package android.speech; +import android.content.ComponentName; + import android.speech.IRecognitionServiceManagerCallback; /** @@ -23,6 +25,10 @@ import android.speech.IRecognitionServiceManagerCallback; * * {@hide} */ -interface IRecognitionServiceManager { - void createSession(in IRecognitionServiceManagerCallback callback); +oneway interface IRecognitionServiceManager { + void createSession( + in ComponentName componentName, + in IBinder clientToken, + boolean onDevice, + in IRecognitionServiceManagerCallback callback); } diff --git a/core/java/android/speech/IRecognitionServiceManagerCallback.aidl b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl index d760810deda8..26afdaafd919 100644 --- a/core/java/android/speech/IRecognitionServiceManagerCallback.aidl +++ b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl @@ -25,5 +25,5 @@ import android.speech.IRecognitionService; */ oneway interface IRecognitionServiceManagerCallback { void onSuccess(in IRecognitionService service); - void onError(); + void onError(int errorCode); } diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java index c97dbfe38ead..fd584f191743 100644 --- a/core/java/android/speech/RecognitionService.java +++ b/core/java/android/speech/RecognitionService.java @@ -105,17 +105,6 @@ public abstract class RecognitionService extends Service { int callingUid) { if (mCurrentCallback == null) { if (DBG) Log.d(TAG, "created new mCurrentCallback, listener = " + listener.asBinder()); - try { - listener.asBinder().linkToDeath(new IBinder.DeathRecipient() { - @Override - public void binderDied() { - mHandler.sendMessage(mHandler.obtainMessage(MSG_CANCEL, listener)); - } - }, 0); - } catch (RemoteException re) { - Log.e(TAG, "dead listener on startListening"); - return; - } mCurrentCallback = new Callback(listener, callingUid); RecognitionService.this.onStartListening(intent, mCurrentCallback); } else { @@ -352,7 +341,6 @@ public abstract class RecognitionService extends Service { * Return the Linux uid assigned to the process that sent you the current transaction that * is being processed. This is obtained from {@link Binder#getCallingUid()}. */ - // TODO(b/176578753): need to make sure this is fixed when proxied through system. public int getCallingUid() { return mCallingUid; } @@ -368,7 +356,7 @@ public abstract class RecognitionService extends Service { @Override public void startListening(Intent recognizerIntent, IRecognitionListener listener, - String packageName, String featureId) { + String packageName, String featureId, int callingUid) { Preconditions.checkNotNull(packageName); if (DBG) Log.d(TAG, "startListening called by:" + listener.asBinder()); @@ -377,7 +365,7 @@ public abstract class RecognitionService extends Service { packageName, featureId)) { service.mHandler.sendMessage(Message.obtain(service.mHandler, MSG_START_LISTENING, service.new StartListeningArgs( - recognizerIntent, listener, Binder.getCallingUid()))); + recognizerIntent, listener, callingUid))); } } @@ -397,7 +385,7 @@ public abstract class RecognitionService extends Service { @Override public void cancel(IRecognitionListener listener, String packageName, - String featureId) { + String featureId, boolean isShutdown) { Preconditions.checkNotNull(packageName); if (DBG) Log.d(TAG, "cancel called by:" + listener.asBinder()); diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java index de879c63a1a6..850f997a2d2f 100644 --- a/core/java/android/speech/SpeechRecognizer.java +++ b/core/java/android/speech/SpeechRecognizer.java @@ -20,8 +20,8 @@ import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; import android.content.pm.ResolveInfo; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -32,10 +32,9 @@ import android.os.ServiceManager; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; +import android.util.Slog; -import java.util.LinkedList; import java.util.List; -import java.util.Queue; /** * This class provides access to the speech recognition service. This service allows access to the @@ -107,6 +106,12 @@ public class SpeechRecognizer { /** Insufficient permissions */ public static final int ERROR_INSUFFICIENT_PERMISSIONS = 9; + /** Too many requests from the same client. */ + public static final int ERROR_TOO_MANY_REQUESTS = 10; + + /** Server has been disconnected, e.g. because the app has crashed. */ + public static final int ERROR_SERVER_DISCONNECTED = 11; + /** action codes */ private final static int MSG_START = 1; private final static int MSG_STOP = 2; @@ -116,9 +121,6 @@ public class SpeechRecognizer { /** The actual RecognitionService endpoint */ private IRecognitionService mService; - /** The connection to the actual service */ - private Connection mConnection; - /** Context with which the manager was created */ private final Context mContext; @@ -151,15 +153,11 @@ public class SpeechRecognizer { } }; - /** - * Temporary queue, saving the messages until the connection will be established, afterwards, - * only mHandler will receive the messages - */ - private final Queue<Message> mPendingTasks = new LinkedList<Message>(); - /** The Listener that will receive all the callbacks */ private final InternalListener mListener = new InternalListener(); + private final IBinder mClientToken = new Binder(); + /** * The right way to create a {@code SpeechRecognizer} is by using * {@link #createSpeechRecognizer} static factory method @@ -181,30 +179,6 @@ public class SpeechRecognizer { } /** - * Basic ServiceConnection that records the mService variable. Additionally, on creation it - * invokes the {@link IRecognitionService#startListening(Intent, IRecognitionListener)}. - */ - private class Connection implements ServiceConnection { - - public void onServiceConnected(final ComponentName name, final IBinder service) { - // always done on the application main thread, so no need to send message to mHandler - mService = IRecognitionService.Stub.asInterface(service); - if (DBG) Log.d(TAG, "onServiceConnected - Success"); - while (!mPendingTasks.isEmpty()) { - mHandler.sendMessage(mPendingTasks.poll()); - } - } - - public void onServiceDisconnected(final ComponentName name) { - // always done on the application main thread, so no need to send message to mHandler - mService = null; - mConnection = null; - mPendingTasks.clear(); - if (DBG) Log.d(TAG, "onServiceDisconnected - Success"); - } - } - - /** * Checks whether a speech recognition service is available on the system. If this method * returns {@code false}, {@link SpeechRecognizer#createSpeechRecognizer(Context)} will * fail. @@ -303,87 +277,52 @@ public class SpeechRecognizer { throw new IllegalArgumentException("intent must not be null"); } checkIsCalledFromMainThread(); - if (mConnection == null) { // first time connection - // TODO(b/176578753): both flows should go through system service. - if (mOnDevice) { - connectToSystemService(); - } else { - connectToService(); - } - } - putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent)); - } - private void connectToSystemService() { - mManagerService = IRecognitionServiceManager.Stub.asInterface( - ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE)); - - if (mManagerService == null) { - mListener.onError(ERROR_CLIENT); - return; - } - - try { - // TODO(b/176578753): this has to supply information on whether to use on-device impl. - mManagerService.createSession(new IRecognitionServiceManagerCallback.Stub(){ - @Override - public void onSuccess(IRecognitionService service) throws RemoteException { - mService = service; - } - - @Override - public void onError() throws RemoteException { - Log.e(TAG, "Bind to system recognition service failed"); - mListener.onError(ERROR_CLIENT); - } - }); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - } - - private void connectToService() { - mConnection = new Connection(); - - Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE); - - if (mServiceComponent == null) { - String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.VOICE_RECOGNITION_SERVICE); - - if (TextUtils.isEmpty(serviceComponent)) { - Log.e(TAG, "no selected voice recognition service"); - mListener.onError(ERROR_CLIENT); - return; + if (DBG) { + Slog.i(TAG, "#startListening called"); + if (mService == null) { + Slog.i(TAG, "Connection is not established yet"); } + } - serviceIntent.setComponent( - ComponentName.unflattenFromString(serviceComponent)); + if (mService == null) { + // First time connection: first establish a connection, then dispatch #startListening. + connectToSystemService( + () -> putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent))); } else { - serviceIntent.setComponent(mServiceComponent); - } - if (!mContext.bindService(serviceIntent, mConnection, - Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) { - Log.e(TAG, "bind to recognition service failed"); - mConnection = null; - mService = null; - mListener.onError(ERROR_CLIENT); - return; + putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent)); } } /** * Stops listening for speech. Speech captured so far will be recognized as if the user had - * stopped speaking at this point. Note that in the default case, this does not need to be - * called, as the speech endpointer will automatically stop the recognizer listening when it - * determines speech has completed. However, you can manipulate endpointer parameters directly - * using the intent extras defined in {@link RecognizerIntent}, in which case you may sometimes - * want to manually call this method to stop listening sooner. Please note that + * stopped speaking at this point. + * + * <p>Note that in the default case, this does not need to be called, as the speech endpointer + * will automatically stop the recognizer listening when it determines speech has completed. + * However, you can manipulate endpointer parameters directly using the intent extras defined in + * {@link RecognizerIntent}, in which case you may sometimes want to manually call this method + * to stop listening sooner. + * + * <p>Upon invocation clients must wait until {@link RecognitionListener#onResults} or + * {@link RecognitionListener#onError} are invoked before calling + * {@link SpeechRecognizer#startListening} again. Otherwise such an attempt would be rejected by + * recognition service. + * + * <p>Please note that * {@link #setRecognitionListener(RecognitionListener)} should be called beforehand, otherwise * no notifications will be received. */ public void stopListening() { checkIsCalledFromMainThread(); + + if (DBG) { + Slog.i(TAG, "#stopListening called"); + if (mService == null) { + Slog.i(TAG, "Connection is not established yet"); + } + } + putMessage(Message.obtain(mHandler, MSG_STOP)); } @@ -405,11 +344,7 @@ public class SpeechRecognizer { } private void putMessage(Message msg) { - if (mService == null) { - mPendingTasks.offer(msg); - } else { - mHandler.sendMessage(msg); - } + mHandler.sendMessage(msg); } /** sends the actual message to the service */ @@ -419,7 +354,7 @@ public class SpeechRecognizer { } try { mService.startListening(recognizerIntent, mListener, mContext.getOpPackageName(), - mContext.getAttributionTag()); + mContext.getAttributionTag(), android.os.Process.myUid()); if (DBG) Log.d(TAG, "service start listening command succeded"); } catch (final RemoteException e) { Log.e(TAG, "startListening() failed", e); @@ -448,7 +383,11 @@ public class SpeechRecognizer { return; } try { - mService.cancel(mListener, mContext.getOpPackageName(), mContext.getAttributionTag()); + mService.cancel( + mListener, + mContext.getOpPackageName(), + mContext.getAttributionTag(), + false /* isShutdown */); if (DBG) Log.d(TAG, "service cancel command succeded"); } catch (final RemoteException e) { Log.e(TAG, "cancel() failed", e); @@ -471,28 +410,94 @@ public class SpeechRecognizer { mListener.mInternalListener = listener; } - /** - * Destroys the {@code SpeechRecognizer} object. - */ + /** Destroys the {@code SpeechRecognizer} object. */ public void destroy() { if (mService != null) { try { mService.cancel(mListener, mContext.getOpPackageName(), - mContext.getAttributionTag()); + mContext.getAttributionTag(), true /* isShutdown */); } catch (final RemoteException e) { // Not important } } - if (mConnection != null) { - mContext.unbindService(mConnection); - } - mPendingTasks.clear(); mService = null; - mConnection = null; mListener.mInternalListener = null; } + /** Establishes a connection to system server proxy and initializes the session. */ + private void connectToSystemService(Runnable onSuccess) { + mManagerService = IRecognitionServiceManager.Stub.asInterface( + ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE)); + + if (mManagerService == null) { + mListener.onError(ERROR_CLIENT); + return; + } + + ComponentName componentName = getSpeechRecognizerComponentName(); + + if (!mOnDevice && componentName == null) { + mListener.onError(ERROR_CLIENT); + return; + } + + try { + mManagerService.createSession( + componentName, + mClientToken, + mOnDevice, + new IRecognitionServiceManagerCallback.Stub(){ + @Override + public void onSuccess(IRecognitionService service) throws RemoteException { + mService = service; + onSuccess.run(); + } + + @Override + public void onError(int errorCode) throws RemoteException { + Log.e(TAG, "Bind to system recognition service failed"); + mListener.onError(errorCode); + } + }); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Returns the component name to be used for establishing a connection, based on the parameters + * used during initialization. + * + * <p>Note the 3 different scenarios: + * <ol> + * <li>On-device speech recognizer which is determined by the manufacturer and not + * changeable by the user + * <li>Default user-selected speech recognizer as specified by + * {@code Settings.Secure.VOICE_RECOGNITION_SERVICE} + * <li>Custom speech recognizer supplied by the client. + */ + private ComponentName getSpeechRecognizerComponentName() { + if (mOnDevice) { + return null; + } + + if (mServiceComponent != null) { + return mServiceComponent; + } + + String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.VOICE_RECOGNITION_SERVICE); + + if (TextUtils.isEmpty(serviceComponent)) { + Log.e(TAG, "no selected voice recognition service"); + mListener.onError(ERROR_CLIENT); + return null; + } + + return ComponentName.unflattenFromString(serviceComponent); + } + /** * Internal wrapper of IRecognitionListener which will propagate the results to * RecognitionListener diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 5d66dc7f29eb..78e5eabb69a8 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -913,7 +913,7 @@ public class TextToSpeech { // Special case, we are asked to shutdown connection that did finalize its connection. synchronized (mStartLock) { if (mConnectingServiceConnection != null) { - mContext.unbindService(mConnectingServiceConnection); + mConnectingServiceConnection.disconnect(); mConnectingServiceConnection = null; return; } diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index ffb4a6eeb1ab..3c355d4b6f45 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -270,13 +270,13 @@ public class TelephonyRegistryManager { /** * Notify call state changed on certain subscription. * - * @param subId for which call state changed. * @param slotIndex for which call state changed. Can be derived from subId except when subId is * invalid. + * @param subId for which call state changed. * @param state latest call state. e.g, offhook, ringing * @param incomingNumber incoming phone number. */ - public void notifyCallStateChanged(int subId, int slotIndex, @CallState int state, + public void notifyCallStateChanged(int slotIndex, int subId, @CallState int state, @Nullable String incomingNumber) { try { sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber); @@ -329,12 +329,12 @@ public class TelephonyRegistryManager { /** * Notify {@link ServiceState} update on certain subscription. * - * @param subId for which the service state changed. * @param slotIndex for which the service state changed. Can be derived from subId except * subId is invalid. + * @param subId for which the service state changed. * @param state service state e.g, in service, out of service or roaming status. */ - public void notifyServiceStateChanged(int subId, int slotIndex, @NonNull ServiceState state) { + public void notifyServiceStateChanged(int slotIndex, int subId, @NonNull ServiceState state) { try { sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state); } catch (RemoteException ex) { @@ -345,12 +345,12 @@ public class TelephonyRegistryManager { /** * Notify {@link SignalStrength} update on certain subscription. * - * @param subId for which the signalstrength changed. * @param slotIndex for which the signalstrength changed. Can be derived from subId except when * subId is invalid. + * @param subId for which the signalstrength changed. * @param signalStrength e.g, signalstrength level {@see SignalStrength#getLevel()} */ - public void notifySignalStrengthChanged(int subId, int slotIndex, + public void notifySignalStrengthChanged(int slotIndex, int subId, @NonNull SignalStrength signalStrength) { try { sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength); @@ -363,13 +363,13 @@ public class TelephonyRegistryManager { * Notify changes to the message-waiting indicator on certain subscription. e.g, The status bar * uses message waiting indicator to determine when to display the voicemail icon. * - * @param subId for which message waiting indicator changed. * @param slotIndex for which message waiting indicator changed. Can be derived from subId * except when subId is invalid. + * @param subId for which message waiting indicator changed. * @param msgWaitingInd {@code true} indicates there is message-waiting indicator, {@code false} * otherwise. */ - public void notifyMessageWaitingChanged(int subId, int slotIndex, boolean msgWaitingInd) { + public void notifyMessageWaitingChanged(int slotIndex, int subId, boolean msgWaitingInd) { try { sRegistry.notifyMessageWaitingChangedForPhoneId(slotIndex, subId, msgWaitingInd); } catch (RemoteException ex) { @@ -410,9 +410,9 @@ public class TelephonyRegistryManager { /** * Notify changes to default (Internet) data connection state on certain subscription. * - * @param subId for which data connection state changed. * @param slotIndex for which data connections state changed. Can be derived from subId except * when subId is invalid. + * @param subId for which data connection state changed. * @param preciseState the PreciseDataConnectionState * * @see PreciseDataConnectionState @@ -431,13 +431,13 @@ public class TelephonyRegistryManager { /** * Notify {@link CallQuality} change on certain subscription. * - * @param subId for which call quality state changed. * @param slotIndex for which call quality state changed. Can be derived from subId except when * subId is invalid. + * @param subId for which call quality state changed. * @param callQuality Information about call quality e.g, call quality level * @param networkType associated with this data connection. e.g, LTE */ - public void notifyCallQualityChanged(int subId, int slotIndex, @NonNull CallQuality callQuality, + public void notifyCallQualityChanged(int slotIndex, int subId, @NonNull CallQuality callQuality, @NetworkType int networkType) { try { sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType); @@ -449,11 +449,11 @@ public class TelephonyRegistryManager { /** * Notify emergency number list changed on certain subscription. * - * @param subId for which emergency number list changed. * @param slotIndex for which emergency number list changed. Can be derived from subId except * when subId is invalid. + * @param subId for which emergency number list changed. */ - public void notifyEmergencyNumberList(int subId, int slotIndex) { + public void notifyEmergencyNumberList( int slotIndex, int subId) { try { sRegistry.notifyEmergencyNumberList(slotIndex, subId); } catch (RemoteException ex) { @@ -494,13 +494,13 @@ public class TelephonyRegistryManager { /** * Notify radio power state changed on certain subscription. * - * @param subId for which radio power state changed. * @param slotIndex for which radio power state changed. Can be derived from subId except when * subId is invalid. + * @param subId for which radio power state changed. * @param radioPowerState the current modem radio state. */ - public void notifyRadioPowerStateChanged(int subId, int slotIndex, - @RadioPowerState int radioPowerState) { + public void notifyRadioPowerStateChanged(int slotIndex, int subId, + @RadioPowerState int radioPowerState) { try { sRegistry.notifyRadioPowerStateChanged(slotIndex, subId, radioPowerState); } catch (RemoteException ex) { @@ -538,13 +538,13 @@ public class TelephonyRegistryManager { * Notify data activation state changed on certain subscription. * @see TelephonyManager#getDataActivationState() * - * @param subId for which data activation state changed. * @param slotIndex for which data activation state changed. Can be derived from subId except * when subId is invalid. + * @param subId for which data activation state changed. * @param activationState sim activation state e.g, activated. */ - public void notifyDataActivationStateChanged(int subId, int slotIndex, - @SimActivationState int activationState) { + public void notifyDataActivationStateChanged(int slotIndex, int subId, + @SimActivationState int activationState) { try { sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId, SIM_ACTIVATION_TYPE_DATA, activationState); @@ -557,13 +557,13 @@ public class TelephonyRegistryManager { * Notify voice activation state changed on certain subscription. * @see TelephonyManager#getVoiceActivationState() * - * @param subId for which voice activation state changed. * @param slotIndex for which voice activation state changed. Can be derived from subId except * subId is invalid. + * @param subId for which voice activation state changed. * @param activationState sim activation state e.g, activated. */ - public void notifyVoiceActivationStateChanged(int subId, int slotIndex, - @SimActivationState int activationState) { + public void notifyVoiceActivationStateChanged(int slotIndex, int subId, + @SimActivationState int activationState) { try { sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId, SIM_ACTIVATION_TYPE_VOICE, activationState); @@ -576,9 +576,9 @@ public class TelephonyRegistryManager { * Notify User mobile data state changed on certain subscription. e.g, mobile data is enabled * or disabled. * - * @param subId for which mobile data state has changed. * @param slotIndex for which mobile data state has changed. Can be derived from subId except * when subId is invalid. + * @param subId for which mobile data state has changed. * @param state {@code true} indicates mobile data is enabled/on. {@code false} otherwise. */ public void notifyUserMobileDataStateChanged(int slotIndex, int subId, boolean state) { @@ -599,7 +599,7 @@ public class TelephonyRegistryManager { * @param telephonyDisplayInfo The display info. */ public void notifyDisplayInfoChanged(int slotIndex, int subscriptionId, - @NonNull TelephonyDisplayInfo telephonyDisplayInfo) { + @NonNull TelephonyDisplayInfo telephonyDisplayInfo) { try { sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, telephonyDisplayInfo); } catch (RemoteException ex) { @@ -640,14 +640,14 @@ public class TelephonyRegistryManager { * Notify precise call state changed on certain subscription, including foreground, background * and ringcall states. * - * @param subId for which precise call state changed. * @param slotIndex for which precise call state changed. Can be derived from subId except when * subId is invalid. + * @param subId for which precise call state changed. * @param ringCallPreciseState ringCall state. * @param foregroundCallPreciseState foreground call state. * @param backgroundCallPreciseState background call state. */ - public void notifyPreciseCallState(int subId, int slotIndex, + public void notifyPreciseCallState(int slotIndex, int subId, @PreciseCallStates int ringCallPreciseState, @PreciseCallStates int foregroundCallPreciseState, @PreciseCallStates int backgroundCallPreciseState) { @@ -790,9 +790,10 @@ public class TelephonyRegistryManager { * @param reason Reason for data enabled/disabled. See {@code REASON_*} in * {@link TelephonyManager}. */ - public void notifyDataEnabled(boolean enabled, @TelephonyManager.DataEnabledReason int reason) { + public void notifyDataEnabled(int slotIndex, int subId, boolean enabled, + @TelephonyManager.DataEnabledReason int reason) { try { - sRegistry.notifyDataEnabled(enabled, reason); + sRegistry.notifyDataEnabled(slotIndex, subId, enabled, reason); } catch (RemoteException ex) { // system server crash } @@ -801,11 +802,11 @@ public class TelephonyRegistryManager { /** * Notify emergency number list changed on certain subscription. * - * @param subId for which emergency number list changed. * @param slotIndex for which emergency number list changed. Can be derived from subId except * when subId is invalid. + * @param subId for which emergency number list changed. */ - public void notifyAllowedNetworkTypesChanged(int subId, int slotIndex, + public void notifyAllowedNetworkTypesChanged(int slotIndex, int subId, Map<Integer, Long> allowedNetworkTypeList) { try { sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, allowedNetworkTypeList); diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java index aef185c77633..33b7c757f1e8 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -18,6 +18,7 @@ package android.text; import static java.lang.annotation.RetentionPolicy.SOURCE; +import android.annotation.CurrentTimeMillisLong; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -101,7 +102,7 @@ public final class FontConfig implements Parcelable { * * If there is no update, this return 0. */ - public long getLastModifiedTimeMillis() { + public @CurrentTimeMillisLong long getLastModifiedTimeMillis() { return mLastModifiedTimeMillis; } diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index b22921233f05..2b577d04b18d 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -46,6 +46,9 @@ public class FeatureFlagUtils { "settings_do_not_restore_preserved"; /** @hide */ public static final String SETTINGS_PROVIDER_MODEL = "settings_provider_model"; + /** @hide */ + public static final String SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES + = "settings_use_new_backup_eligibility_rules"; private static final Map<String, String> DEFAULT_FLAGS; @@ -68,6 +71,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_silky_home", "false"); DEFAULT_FLAGS.put("settings_contextual_home", "false"); DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false"); + DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "false"); } private static final Set<String> PERSISTENT_FLAGS; diff --git a/core/java/android/util/MergedConfiguration.java b/core/java/android/util/MergedConfiguration.java index 3ac44aacc2c7..4328e0826bd0 100644 --- a/core/java/android/util/MergedConfiguration.java +++ b/core/java/android/util/MergedConfiguration.java @@ -33,9 +33,9 @@ import java.io.PrintWriter; */ public class MergedConfiguration implements Parcelable { - private Configuration mGlobalConfig = new Configuration(); - private Configuration mOverrideConfig = new Configuration(); - private Configuration mMergedConfig = new Configuration(); + private final Configuration mGlobalConfig = new Configuration(); + private final Configuration mOverrideConfig = new Configuration(); + private final Configuration mMergedConfig = new Configuration(); public MergedConfiguration() { } @@ -59,15 +59,15 @@ public class MergedConfiguration implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(mGlobalConfig, flags); - dest.writeParcelable(mOverrideConfig, flags); - dest.writeParcelable(mMergedConfig, flags); + mGlobalConfig.writeToParcel(dest, flags); + mOverrideConfig.writeToParcel(dest, flags); + mMergedConfig.writeToParcel(dest, flags); } public void readFromParcel(Parcel source) { - mGlobalConfig = source.readParcelable(Configuration.class.getClassLoader()); - mOverrideConfig = source.readParcelable(Configuration.class.getClassLoader()); - mMergedConfig = source.readParcelable(Configuration.class.getClassLoader()); + mGlobalConfig.readFromParcel(source); + mOverrideConfig.readFromParcel(source); + mMergedConfig.readFromParcel(source); } @Override diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java index 698cb77947cf..0ac2c9c25ad1 100644 --- a/core/java/android/util/RotationUtils.java +++ b/core/java/android/util/RotationUtils.java @@ -24,6 +24,7 @@ import static android.view.Surface.ROTATION_90; import android.annotation.Dimension; import android.graphics.Insets; import android.graphics.Matrix; +import android.graphics.Rect; import android.view.Surface.Rotation; /** @@ -73,6 +74,60 @@ public class RotationUtils { } /** + * Rotates bounds as if parentBounds and bounds are a group. The group is rotated from + * oldRotation to newRotation. This assumes that parentBounds is at 0,0 and remains at 0,0 after + * rotation. The bounds will be at the same physical position in parentBounds. + * + * Only 'inOutBounds' is mutated. + */ + public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int oldRotation, + @Rotation int newRotation) { + rotateBounds(inOutBounds, parentBounds, deltaRotation(oldRotation, newRotation)); + } + + /** + * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta` + * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and + * remains at 0,0 after rotation. The bounds will be at the same physical position in + * parentBounds. + * + * Only 'inOutBounds' is mutated. + */ + public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int rotation) { + final int origLeft = inOutBounds.left; + final int origTop = inOutBounds.top; + switch (rotation) { + case ROTATION_0: + return; + case ROTATION_90: + inOutBounds.left = inOutBounds.top; + inOutBounds.top = parentBounds.right - inOutBounds.right; + inOutBounds.right = inOutBounds.bottom; + inOutBounds.bottom = parentBounds.right - origLeft; + return; + case ROTATION_180: + inOutBounds.left = parentBounds.right - inOutBounds.right; + inOutBounds.right = parentBounds.right - origLeft; + inOutBounds.top = parentBounds.bottom - inOutBounds.bottom; + inOutBounds.bottom = parentBounds.bottom - origTop; + return; + case ROTATION_270: + inOutBounds.left = parentBounds.bottom - inOutBounds.bottom; + inOutBounds.bottom = inOutBounds.right; + inOutBounds.right = parentBounds.bottom - inOutBounds.top; + inOutBounds.top = origLeft; + } + } + + /** @return the rotation needed to rotate from oldRotation to newRotation. */ + @Rotation + public static int deltaRotation(int oldRotation, int newRotation) { + int delta = newRotation - oldRotation; + if (delta < 0) delta += 4; + return delta; + } + + /** * Sets a matrix such that given a rotation, it transforms physical display * coordinates to that rotation's logical coordinates. * diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl index b9c55081a103..468a69c7bddb 100644 --- a/core/java/android/uwb/IUwbAdapter.aidl +++ b/core/java/android/uwb/IUwbAdapter.aidl @@ -17,7 +17,6 @@ package android.uwb; import android.os.PersistableBundle; -import android.uwb.AngleOfArrivalSupport; import android.uwb.IUwbAdapterStateCallbacks; import android.uwb.IUwbRangingCallbacks; import android.uwb.SessionHandle; @@ -47,43 +46,6 @@ interface IUwbAdapter { void unregisterAdapterStateCallbacks(in IUwbAdapterStateCallbacks callbacks); /** - * Returns true if ranging is supported, false otherwise - */ - boolean isRangingSupported(); - - /** - * Get the angle of arrival supported by this device - * - * @return the angle of arrival type supported - */ - AngleOfArrivalSupport getAngleOfArrivalSupport(); - - /** - * Generates a list of the supported 802.15.4z channels - * - * The list must be prioritized in the order of preferred channel usage. - * - * The list must only contain channels that are permitted to be used in the - * device's current location. - * - * @return an array of support channels on the device for the current location. - */ - int[] getSupportedChannels(); - - /** - * Generates a list of the supported 802.15.4z preamble codes - * - * The list must be prioritized in the order of preferred preamble usage. - * - * The list must only contain preambles that are permitted to be used in the - * device's current location. - * - * @return an array of supported preambles on the device for the current - * location. - */ - int[] getSupportedPreambleCodes(); - - /** * Get the accuracy of the ranging timestamps * * @return accuracy of the ranging timestamps in nanoseconds @@ -91,27 +53,6 @@ interface IUwbAdapter { long getTimestampResolutionNanos(); /** - * Get the supported number of simultaneous ranging sessions - * - * @return the supported number of simultaneous ranging sessions - */ - int getMaxSimultaneousSessions(); - - /** - * Get the maximum number of remote devices per session when local device is initiator - * - * @return the maximum number of remote devices supported in a single session - */ - int getMaxRemoteDevicesPerInitiatorSession(); - - /** - * Get the maximum number of remote devices per session when local device is responder - * - * @return the maximum number of remote devices supported in a single session - */ - int getMaxRemoteDevicesPerResponderSession(); - - /** * Provides the capabilities and features of the device * * @return specification specific capabilities and features of the device diff --git a/core/java/android/uwb/OWNERS b/core/java/android/uwb/OWNERS index ea41c3984dfd..17936ae7bb73 100644 --- a/core/java/android/uwb/OWNERS +++ b/core/java/android/uwb/OWNERS @@ -1,5 +1,6 @@ bstack@google.com eliptus@google.com jsolnit@google.com +matbev@google.com siyuanh@google.com zachoverflow@google.com diff --git a/core/java/android/uwb/TEST_MAPPING b/core/java/android/uwb/TEST_MAPPING index 9e50bd64d089..08ed2c7b71d9 100644 --- a/core/java/android/uwb/TEST_MAPPING +++ b/core/java/android/uwb/TEST_MAPPING @@ -2,6 +2,9 @@ "presubmit": [ { "name": "UwbManagerTests" + }, + { + "name": "CtsUwbTestCases" } ] -}
\ No newline at end of file +} diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java index 2dc0ba0b9b80..63a6d058f358 100644 --- a/core/java/android/uwb/UwbManager.java +++ b/core/java/android/uwb/UwbManager.java @@ -32,10 +32,6 @@ import android.os.ServiceManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; import java.util.concurrent.Executor; /** @@ -195,133 +191,6 @@ public final class UwbManager { } /** - * Check if ranging is supported, regardless of ranging method - * - * @return true if ranging is supported - */ - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public boolean isRangingSupported() { - try { - return mUwbAdapter.isRangingSupported(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { - ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE, - ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D, - ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL, - ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL}) - public @interface AngleOfArrivalSupportType {} - - /** - * Indicate absence of support for angle of arrival measurement - */ - public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1; - - /** - * Indicate support for planar angle of arrival measurement, due to antenna - * limitation. Typically requires at least two antennas. - */ - public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2; - - /** - * Indicate support for three dimensional angle of arrival measurement. - * Typically requires at least three antennas. However, due to antenna - * arrangement, a platform may only support hemi-spherical azimuth angles - * ranging from -pi/2 to pi/2 - */ - public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3; - - /** - * Indicate support for three dimensional angle of arrival measurement. - * Typically requires at least three antennas. This mode supports full - * azimuth angles ranging from -pi to pi. - */ - public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4; - - /** - * Gets the {@link AngleOfArrivalSupportType} supported on this platform - * <p>Possible return values are - * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE}, - * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D}, - * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL}, - * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL}. - * - * @return angle of arrival type supported - */ - @AngleOfArrivalSupportType - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public int getAngleOfArrivalSupport() { - try { - switch (mUwbAdapter.getAngleOfArrivalSupport()) { - case AngleOfArrivalSupport.TWO_DIMENSIONAL: - return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D; - - case AngleOfArrivalSupport.THREE_DIMENSIONAL_HEMISPHERICAL: - return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL; - - case AngleOfArrivalSupport.THREE_DIMENSIONAL_SPHERICAL: - return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL; - - case AngleOfArrivalSupport.NONE: - default: - return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE; - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Get a {@link List} of supported channel numbers based on the device's current location - * <p>The returned values are ordered by the system's desired ordered of use, with the first - * entry being the most preferred. - * - * <p>Channel numbers are defined based on the IEEE 802.15.4z standard for UWB. - * - * @return {@link List} of supported channel numbers ordered by preference - */ - @NonNull - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public List<Integer> getSupportedChannelNumbers() { - List<Integer> channels = new ArrayList<>(); - try { - for (int channel : mUwbAdapter.getSupportedChannels()) { - channels.add(channel); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - return channels; - } - - /** - * Get a {@link List} of supported preamble code indices - * <p> Preamble code indices are defined based on the IEEE 802.15.4z standard for UWB. - * - * @return {@link List} of supported preamble code indices - */ - @NonNull - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public Set<Integer> getSupportedPreambleCodeIndices() { - Set<Integer> preambles = new HashSet<>(); - try { - for (int preamble : mUwbAdapter.getSupportedPreambleCodes()) { - preambles.add(preamble); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - return preambles; - } - - /** * Get the timestamp resolution for events in nanoseconds * <p>This value defines the maximum error of all timestamps for events reported to * {@link RangingSession.Callback}. @@ -339,50 +208,6 @@ public final class UwbManager { } /** - * Get the number of simultaneous sessions allowed in the system - * - * @return the maximum allowed number of simultaneously open {@link RangingSession} instances. - */ - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public int getMaxSimultaneousSessions() { - try { - return mUwbAdapter.getMaxSimultaneousSessions(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Get the maximum number of remote devices in a {@link RangingSession} when the local device - * is the initiator. - * - * @return the maximum number of remote devices per {@link RangingSession} - */ - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public int getMaxRemoteDevicesPerInitiatorSession() { - try { - return mUwbAdapter.getMaxRemoteDevicesPerInitiatorSession(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Get the maximum number of remote devices in a {@link RangingSession} when the local device - * is a responder. - * - * @return the maximum number of remote devices per {@link RangingSession} - */ - @RequiresPermission(Manifest.permission.UWB_PRIVILEGED) - public int getMaxRemoteDevicesPerResponderSession() { - try { - return mUwbAdapter.getMaxRemoteDevicesPerResponderSession(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Open a {@link RangingSession} with the given parameters * <p>The {@link RangingSession.Callback#onOpened(RangingSession)} function is called with a * {@link RangingSession} object used to control ranging when the session is successfully diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java index 6543de15f969..eb49e52d5050 100644 --- a/core/java/android/view/FrameMetrics.java +++ b/core/java/android/view/FrameMetrics.java @@ -213,8 +213,7 @@ public final class FrameMetrics { Index.FRAME_TIMELINE_VSYNC_ID, Index.INTENDED_VSYNC, Index.VSYNC, - Index.OLDEST_INPUT_EVENT, - Index.NEWEST_INPUT_EVENT, + Index.INPUT_EVENT_ID, Index.HANDLE_INPUT_START, Index.ANIMATION_START, Index.PERFORM_TRAVERSALS_START, @@ -225,8 +224,11 @@ public final class FrameMetrics { Index.ISSUE_DRAW_COMMANDS_START, Index.SWAP_BUFFERS, Index.FRAME_COMPLETED, + Index.DEQUEUE_BUFFER_DURATION, + Index.QUEUE_BUFFER_DURATION, Index.GPU_COMPLETED, - Index.SWAP_BUFFERS_COMPLETED + Index.SWAP_BUFFERS_COMPLETED, + Index.DISPLAY_PRESENT_TIME, }) @Retention(RetentionPolicy.SOURCE) public @interface Index { @@ -234,20 +236,22 @@ public final class FrameMetrics { int FRAME_TIMELINE_VSYNC_ID = 1; int INTENDED_VSYNC = 2; int VSYNC = 3; - int OLDEST_INPUT_EVENT = 4; - int NEWEST_INPUT_EVENT = 5; - int HANDLE_INPUT_START = 6; - int ANIMATION_START = 7; - int PERFORM_TRAVERSALS_START = 8; - int DRAW_START = 9; - int FRAME_DEADLINE = 10; - int SYNC_QUEUED = 11; - int SYNC_START = 12; - int ISSUE_DRAW_COMMANDS_START = 13; - int SWAP_BUFFERS = 14; - int FRAME_COMPLETED = 15; - int GPU_COMPLETED = 18; - int SWAP_BUFFERS_COMPLETED = 19; + int INPUT_EVENT_ID = 4; + int HANDLE_INPUT_START = 5; + int ANIMATION_START = 6; + int PERFORM_TRAVERSALS_START = 7; + int DRAW_START = 8; + int FRAME_DEADLINE = 9; + int SYNC_QUEUED = 10; + int SYNC_START = 11; + int ISSUE_DRAW_COMMANDS_START = 12; + int SWAP_BUFFERS = 13; + int FRAME_COMPLETED = 14; + int DEQUEUE_BUFFER_DURATION = 15; + int QUEUE_BUFFER_DURATION = 16; + int GPU_COMPLETED = 17; + int SWAP_BUFFERS_COMPLETED = 18; + int DISPLAY_PRESENT_TIME = 19; int FRAME_STATS_COUNT = 20; // must always be last and in sync with // FrameInfoIndex::NumIndexes in libs/hwui/FrameInfo.h diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedTaskListener.aidl index 29c9c155e978..c31e67e59a99 100644 --- a/core/java/android/view/IPinnedStackListener.aidl +++ b/core/java/android/view/IPinnedTaskListener.aidl @@ -23,11 +23,11 @@ import android.graphics.Rect; import android.view.DisplayInfo; /** - * Listener for changes to the pinned stack made by the WindowManager. + * Listener for changes to the pinned task made by the WindowManager. * * @hide */ -oneway interface IPinnedStackListener { +oneway interface IPinnedTaskListener { /** * Called when the window manager has detected a change that would cause the movement bounds diff --git a/core/java/android/view/IScrollCaptureCallbacks.aidl b/core/java/android/view/IScrollCaptureCallbacks.aidl index d97e3c66cc5d..26eaac0a2bf5 100644 --- a/core/java/android/view/IScrollCaptureCallbacks.aidl +++ b/core/java/android/view/IScrollCaptureCallbacks.aidl @@ -16,12 +16,10 @@ package android.view; -import android.graphics.Point; import android.graphics.Rect; +import android.view.ScrollCaptureResponse; import android.view.Surface; -import android.view.IScrollCaptureConnection; - /** * Asynchronous callback channel for responses to scroll capture requests. * @@ -29,34 +27,30 @@ import android.view.IScrollCaptureConnection; */ interface IScrollCaptureCallbacks { /** - * Scroll capture is available, and a connection has been provided. + * Provides the result of WindowManagerService#requestScrollCapture * - * @param connection a connection to a window process and scrollable content - * @param scrollAreaInWindow the location of scrolling in global (window) coordinate space + * @param response the response which describes the result */ - oneway void onConnected(in IScrollCaptureConnection connection, in Rect scrollBounds, - in Point positionInWindow); + oneway void onScrollCaptureResponse(in ScrollCaptureResponse response); /** - * The window does not support scroll capture. - */ - oneway void onUnavailable(); - - /** - * Called when the remote end has confirmed the request and is ready to begin providing image - * requests. + * Called in reply to IScrollCaptureConnection#startCapture, when the remote end has confirmed + * the request and is ready to begin capturing images. */ oneway void onCaptureStarted(); /** - * Received a response from a capture request. + * Received a response from a capture request. The provided rectangle indicates the portion + * of the requested rectangle which was captured. An empty rectangle indicates that the request + * could not be satisfied (most commonly due to the available scrolling range). + * + * @param flags flags describing additional status of the result + * @param capturedArea the actual area of the image captured */ - oneway void onCaptureBufferSent(long frameNumber, in Rect capturedArea); + oneway void onImageRequestCompleted(int flags, in Rect capturedArea); /** - * Signals that the capture session has completed and the target window may be returned to - * normal interactive use. This may be due to normal shutdown, or after a timeout or other - * unrecoverable state change such as activity lifecycle, window visibility or focus. + * Signals that the capture session has completed and the target window is ready for normal use. */ - oneway void onConnectionClosed(); + oneway void onCaptureEnded(); } diff --git a/core/java/android/view/IScrollCaptureConnection.aidl b/core/java/android/view/IScrollCaptureConnection.aidl index 63a4f48aeb20..c55e88800393 100644 --- a/core/java/android/view/IScrollCaptureConnection.aidl +++ b/core/java/android/view/IScrollCaptureConnection.aidl @@ -17,33 +17,45 @@ package android.view; import android.graphics.Rect; +import android.os.ICancellationSignal; import android.view.Surface; /** - * Interface implemented by a client of the Scroll Capture framework to receive requests - * to start, capture images and end the session. + * A remote connection to a scroll capture target. * * {@hide} */ interface IScrollCaptureConnection { /** - * Informs the client that it has been selected for scroll capture and should prepare to - * to begin handling capture requests. + * Informs the target that it has been selected for scroll capture. + * + * @param surface a return channel for image buffers + * + * @return a cancallation signal which is used cancel the request + */ + ICancellationSignal startCapture(in Surface surface); + + /** + * Request the target capture an image within the provided rectangle. + * + * @param surface a return channel for image buffers + * @param signal a cancallation signal which can interrupt the request + * + * @return a cancallation signal which is used cancel the request */ - oneway void startCapture(in Surface surface); + ICancellationSignal requestImage(in Rect captureArea); /** - * Request the client capture an image within the provided rectangle. + * Inform the target that capture has ended. * - * @see android.view.ScrollCaptureCallback#onScrollCaptureRequest + * @return a cancallation signal which is used cancel the request */ - oneway void requestImage(in Rect captureArea); + ICancellationSignal endCapture(); /** - * Inform the client that capture has ended. The client should shut down and release all - * local resources in use and prepare for return to normal interactive usage. + * Closes the connection. */ - oneway void endCapture(); + oneway void close(); } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 62f4b864c7a8..afdf798d03ce 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -32,17 +32,15 @@ import android.graphics.Region; import android.os.Bundle; import android.os.IRemoteCallback; import android.os.ParcelFileDescriptor; -import android.service.screenshot.ScreenshotHash; import android.view.DisplayCutout; import android.view.IApplicationToken; import android.view.IAppTransitionAnimationSpecsFuture; -import android.view.IDockedStackListener; import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowListener; import android.view.IDisplayFoldListener; import android.view.IDisplayWindowRotationController; import android.view.IOnKeyguardExitResult; -import android.view.IPinnedStackListener; +import android.view.IPinnedTaskListener; import android.view.IScrollCaptureCallbacks; import android.view.RemoteAnimationAdapter; import android.view.IRotationWatcher; @@ -63,6 +61,8 @@ import android.view.AppTransitionAnimationSpec; import android.view.WindowContentFrameStats; import android.view.WindowManager; import android.view.SurfaceControl; +import android.view.displayhash.DisplayHash; +import android.view.displayhash.VerifiedDisplayHash; /** * System private interface to the window manager. @@ -445,12 +445,12 @@ interface IWindowManager * Sets the region the user can touch the divider. This region will be excluded from the region * which is used to cause a focus switch when dispatching touch. */ - void setDockedStackDividerTouchRegion(in Rect touchableRegion); + void setDockedTaskDividerTouchRegion(in Rect touchableRegion); /** - * Registers a listener that will be called when the pinned stack state changes. + * Registers a listener that will be called when the pinned task state changes. */ - void registerPinnedStackListener(int displayId, IPinnedStackListener listener); + void registerPinnedTaskListener(int displayId, IPinnedTaskListener listener); /** * Requests Keyboard Shortcuts from the displayed window. @@ -757,23 +757,23 @@ interface IWindowManager void holdLock(in IBinder token, in int durationMs); /** - * Gets an array of support hashing algorithms that can be used to generate the hash of the - * screenshot. The String value of one algorithm should be used when requesting to generate - * the ScreenshotHash. + * Gets an array of support hash algorithms that can be used to generate a DisplayHash. The + * String value of one algorithm should be used when requesting to generate + * the DisplayHash. * - * @return a String array of supported hashing algorithms. + * @return a String array of supported hash algorithms. */ - String[] getSupportedScreenshotHashingAlgorithms(); + String[] getSupportedDisplayHashAlgorithms(); /** - * Validate the ScreenshotHash was generated by the system. The ScreenshotHash passed in - * should be the token generated when calling {@link IWindowSession#generateScreenshotHash} + * Validate the DisplayHash was generated by the system. The DisplayHash passed in should be + * the object generated when calling {@link IWindowSession#generateDisplayHash} * - * @param ScreenshotHash The token to verify that it was generated by the system. - * @return true if the token was generated by the system or false if the token cannot be - * verified. + * @param DisplayHash The hash to verify that it was generated by the system. + * @return a {@link VerifiedDisplayHash} if the hash was generated by the system or null + * if the token cannot be verified. */ - boolean verifyScreenshotHash(in ScreenshotHash screenshotHash); + VerifiedDisplayHash verifyDisplayHash(in DisplayHash displayHash); /** * Registers a listener for a {@link android.app.WindowContext} to handle configuration changes diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index dad932c2ee19..ce649cc7c2b2 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -22,7 +22,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.Bundle; -import android.service.screenshot.ScreenshotHash; +import android.os.RemoteCallback; import android.util.MergedConfiguration; import android.view.DisplayCutout; import android.view.InputChannel; @@ -337,14 +337,15 @@ interface IWindowSession { void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus); /** - * Generates an ScreenshotHash that can be used to validate whether specific content was on + * Generates an DisplayHash that can be used to validate whether specific content was on * screen. * - * @param window The token for the window where the view to screenshot is shown. - * @param boundsInWindow The size and position of the ads view in the window - * @param hashAlgorithm The String for the hashing algorithm to use based on values returned - * from {@link IWindowManager#getSupportedHashingAlgorithms()} + * @param window The token for the window to generate the hash of. + * @param boundsInWindow The size and position in the window of where to generate the hash. + * @param hashAlgorithm The String for the hash algorithm to use based on values returned + * from {@link IWindowManager#getSupportedDisplayHashAlgorithms()} + * @param callback The callback invoked to get the results of generateDisplayHash */ - ScreenshotHash generateScreenshotHash(IWindow window, in Rect boundsInWindow, - in String hashAlgorithm); + oneway void generateDisplayHash(IWindow window, in Rect boundsInWindow, + in String hashAlgorithm, in RemoteCallback callback); } diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 59e493191711..fafb885c58e0 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -26,6 +26,7 @@ import android.hardware.Battery; import android.hardware.SensorManager; import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputManager; +import android.hardware.lights.LightsManager; import android.os.Build; import android.os.NullVibrator; import android.os.Parcel; @@ -89,6 +90,9 @@ public final class InputDevice implements Parcelable { @GuardedBy("mMotionRanges") private Battery mBattery; + @GuardedBy("mMotionRanges") + private LightsManager mLightsManager; + /** * A mask for input source classes. * @@ -859,6 +863,21 @@ public final class InputDevice implements Parcelable { } /** + * Gets the lights manager associated with the device, if there is one. + * Even if the device does not have lights, the result is never null. + * Use {@link LightsManager#getLights} to determine whether any lights is + * present. + * + * @return The lights manager associated with the device, never null. + */ + public @NonNull LightsManager getLightsManager() { + if (mLightsManager == null) { + mLightsManager = InputManager.getInstance().getInputDeviceLightsManager(mId); + } + return mLightsManager; + } + + /** * Gets the sensor manager service associated with the input device. * Even if the device does not have a sensor, the result is never null. * Use {@link SensorManager#getSensorList} to get a full list of all supported sensors. diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 2f40bdbff05f..e6cf68367ae6 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -140,7 +140,11 @@ public class InsetsSource implements Parcelable { if (getType() == ITYPE_CAPTION_BAR) { return Insets.of(0, frame.height(), 0, 0); } - if (!getIntersection(frame, relativeFrame, mTmpFrame)) { + // Checks for whether there is shared edge with insets for 0-width/height window. + final boolean hasIntersection = relativeFrame.isEmpty() + ? getIntersection(frame, relativeFrame, mTmpFrame) + : mTmpFrame.setIntersect(frame, relativeFrame); + if (!hasIntersection) { return Insets.NONE; } @@ -257,11 +261,7 @@ public class InsetsSource implements Parcelable { public InsetsSource(Parcel in) { mType = in.readInt(); - if (in.readInt() != 0) { - mFrame = Rect.CREATOR.createFromParcel(in); - } else { - mFrame = null; - } + mFrame = Rect.CREATOR.createFromParcel(in); if (in.readInt() != 0) { mVisibleFrame = Rect.CREATOR.createFromParcel(in); } else { @@ -278,12 +278,7 @@ public class InsetsSource implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); - if (mFrame != null) { - dest.writeInt(1); - mFrame.writeToParcel(dest, 0); - } else { - dest.writeInt(0); - } + mFrame.writeToParcel(dest, 0); if (mVisibleFrame != null) { dest.writeInt(1); mVisibleFrame.writeToParcel(dest, 0); diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java index c717c2adc32d..1d4b4111f884 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -77,8 +77,8 @@ public class InsetsSourceControl implements Parcelable { public InsetsSourceControl(Parcel in) { mType = in.readInt(); - mLeash = in.readParcelable(null /* loader */); - mSurfacePosition = in.readParcelable(null /* loader */); + mLeash = in.readTypedObject(SurfaceControl.CREATOR); + mSurfacePosition = in.readTypedObject(Point.CREATOR); mSkipAnimationOnce = in.readBoolean(); } @@ -119,8 +119,8 @@ public class InsetsSourceControl implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); - dest.writeParcelable(mLeash, 0 /* flags*/); - dest.writeParcelable(mSurfacePosition, 0 /* flags*/); + dest.writeTypedObject(mLeash, 0 /* parcelableFlags */); + dest.writeTypedObject(mSurfacePosition, 0 /* parcelableFlags */); dest.writeBoolean(mSkipAnimationOnce); } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 219190f554ea..fce1952de44b 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -20,8 +20,6 @@ import static android.view.InsetsStateProto.DISPLAY_CUTOUT; import static android.view.InsetsStateProto.DISPLAY_FRAME; import static android.view.InsetsStateProto.SOURCES; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE; -import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES; -import static android.view.WindowInsets.Type.SYSTEM_GESTURES; import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.indexOf; @@ -106,14 +104,11 @@ public class InsetsState implements Parcelable { public static final int ITYPE_NAVIGATION_BAR = 1; public static final int ITYPE_CAPTION_BAR = 2; - // The always visible types are visible to all windows regardless of the z-order. - public static final int FIRST_ALWAYS_VISIBLE_TYPE = 3; - public static final int ITYPE_TOP_GESTURES = FIRST_ALWAYS_VISIBLE_TYPE; + public static final int ITYPE_TOP_GESTURES = 3; public static final int ITYPE_BOTTOM_GESTURES = 4; public static final int ITYPE_LEFT_GESTURES = 5; public static final int ITYPE_RIGHT_GESTURES = 6; - /** Additional gesture inset types that map into {@link Type.MANDATORY_SYSTEM_GESTURES}. */ public static final int ITYPE_TOP_MANDATORY_GESTURES = 7; public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8; public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9; @@ -123,7 +118,6 @@ public class InsetsState implements Parcelable { public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12; public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13; public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14; - public static final int LAST_ALWAYS_VISIBLE_TYPE = ITYPE_BOTTOM_DISPLAY_CUTOUT; public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15; public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16; @@ -162,7 +156,7 @@ public class InsetsState implements Parcelable { static final int ISIDE_FLOATING = 4; static final int ISIDE_UNKNOWN = 5; - private InsetsSource[] mSources = new InsetsSource[SIZE]; + private final InsetsSource[] mSources = new InsetsSource[SIZE]; /** * The frame of the display these sources are relative to. @@ -188,18 +182,6 @@ public class InsetsState implements Parcelable { } /** - * Mirror the always visible sources from the other state. They will share the same object for - * the always visible types. - * - * @param other the state to mirror the mirrored sources from. - */ - public void mirrorAlwaysVisibleInsetsSources(InsetsState other) { - for (int type = FIRST_ALWAYS_VISIBLE_TYPE; type <= LAST_ALWAYS_VISIBLE_TYPE; type++) { - mSources[type] = other.mSources[type]; - } - } - - /** * Calculates {@link WindowInsets} based on the current source configuration. * * @param frame The frame to calculate the insets relative to. @@ -380,14 +362,14 @@ public class InsetsState implements Parcelable { processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, insets, type); - if (type == MANDATORY_SYSTEM_GESTURES) { + if (type == Type.MANDATORY_SYSTEM_GESTURES) { // Mandatory system gestures are also system gestures. // TODO: find a way to express this more generally. One option would be to define // Type.systemGestureInsets() as NORMAL | MANDATORY, but then we lose the // ability to set systemGestureInsets() independently from // mandatorySystemGestureInsets() in the Builder. processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, - insets, SYSTEM_GESTURES); + insets, Type.SYSTEM_GESTURES); } } @@ -493,9 +475,14 @@ public class InsetsState implements Parcelable { * to the client. * * @param type The {@link InternalInsetsType} of the source to remove + * @return {@code true} if this InsetsState was modified; {@code false} otherwise. */ - public void removeSource(@InternalInsetsType int type) { + public boolean removeSource(@InternalInsetsType int type) { + if (mSources[type] == null) { + return false; + } mSources[type] = null; + return true; } /** @@ -552,6 +539,24 @@ public class InsetsState implements Parcelable { } } + /** + * Sets the values from the other InsetsState. But for sources, only specific types of source + * would be set. + * + * @param other the other InsetsState. + * @param types the only types of sources would be set. + */ + public void set(InsetsState other, @InsetsType int types) { + mDisplayFrame.set(other.mDisplayFrame); + mDisplayCutout.set(other.mDisplayCutout); + mRoundedCorners = other.getRoundedCorners(); + final ArraySet<Integer> t = toInternalType(types); + for (int i = t.size() - 1; i >= 0; i--) { + final int type = t.valueAt(i); + mSources[type] = other.mSources[type]; + } + } + public void addSource(InsetsSource source) { mSources[source.getType()] = source; } @@ -575,6 +580,18 @@ public class InsetsState implements Parcelable { if ((types & Type.CAPTION_BAR) != 0) { result.add(ITYPE_CAPTION_BAR); } + if ((types & Type.SYSTEM_GESTURES) != 0) { + result.add(ITYPE_LEFT_GESTURES); + result.add(ITYPE_TOP_GESTURES); + result.add(ITYPE_RIGHT_GESTURES); + result.add(ITYPE_BOTTOM_GESTURES); + } + if ((types & Type.MANDATORY_SYSTEM_GESTURES) != 0) { + result.add(ITYPE_LEFT_MANDATORY_GESTURES); + result.add(ITYPE_TOP_MANDATORY_GESTURES); + result.add(ITYPE_RIGHT_MANDATORY_GESTURES); + result.add(ITYPE_BOTTOM_MANDATORY_GESTURES); + } if ((types & Type.DISPLAY_CUTOUT) != 0) { result.add(ITYPE_LEFT_DISPLAY_CUTOUT); result.add(ITYPE_TOP_DISPLAY_CUTOUT); @@ -787,7 +804,7 @@ public class InsetsState implements Parcelable { public void writeToParcel(Parcel dest, int flags) { mDisplayFrame.writeToParcel(dest, flags); mDisplayCutout.writeToParcel(dest, flags); - dest.writeParcelableArray(mSources, 0); + dest.writeTypedArray(mSources, 0 /* parcelableFlags */); dest.writeTypedObject(mRoundedCorners, flags); } @@ -803,9 +820,9 @@ public class InsetsState implements Parcelable { }; public void readFromParcel(Parcel in) { - mDisplayFrame.set(Rect.CREATOR.createFromParcel(in)); - mDisplayCutout.set(DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in)); - mSources = in.readParcelableArray(null, InsetsSource.class); + mDisplayFrame.readFromParcel(in); + mDisplayCutout.readFromParcel(in); + in.readTypedArray(mSources, InsetsSource.CREATOR); mRoundedCorners = in.readTypedObject(RoundedCorners.CREATOR); } diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 52b7cffbc340..d67439cc9de2 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -3768,6 +3768,41 @@ public final class MotionEvent extends InputEvent implements Parcelable { return (getButtonState() & button) == button; } + /** + * Gets a rotation matrix that (when applied to a motionevent) will rotate that motion event + * such that the result coordinates end up in the same physical location on a display whose + * coordinates are rotated by `rotation`. + * + * For example, rotating 0,0 by 90 degrees will move a point from the physical top-left to + * the bottom-left of the 90-degree-rotated display. + * + * @hide + */ + public static Matrix createRotateMatrix( + @Surface.Rotation int rotation, int displayW, int displayH) { + if (rotation == Surface.ROTATION_0) { + return new Matrix(Matrix.IDENTITY_MATRIX); + } + // values is row-major + float[] values = null; + if (rotation == Surface.ROTATION_90) { + values = new float[]{0, 1, 0, + -1, 0, displayH, + 0, 0, 1}; + } else if (rotation == Surface.ROTATION_180) { + values = new float[]{-1, 0, displayW, + 0, -1, displayH, + 0, 0, 1}; + } else if (rotation == Surface.ROTATION_270) { + values = new float[]{0, -1, displayW, + 1, 0, 0, + 0, 0, 1}; + } + Matrix toOrient = new Matrix(); + toOrient.setValues(values); + return toOrient; + } + public static final @android.annotation.NonNull Parcelable.Creator<MotionEvent> CREATOR = new Parcelable.Creator<MotionEvent>() { public MotionEvent createFromParcel(Parcel in) { diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java index 015e804da2f6..569c287901c7 100644 --- a/core/java/android/view/RoundedCorners.java +++ b/core/java/android/view/RoundedCorners.java @@ -335,7 +335,7 @@ public class RoundedCorners implements Parcelable { } if (o instanceof RoundedCorners) { RoundedCorners r = (RoundedCorners) o; - return Arrays.deepEquals(mRoundedCorners, ((RoundedCorners) o).mRoundedCorners); + return Arrays.deepEquals(mRoundedCorners, r.mRoundedCorners); } return false; } diff --git a/core/java/android/view/ScrollCaptureCallback.java b/core/java/android/view/ScrollCaptureCallback.java index d3aad2c72d27..16886160baca 100644 --- a/core/java/android/view/ScrollCaptureCallback.java +++ b/core/java/android/view/ScrollCaptureCallback.java @@ -19,6 +19,7 @@ package android.view; import android.annotation.NonNull; import android.annotation.UiThread; import android.graphics.Rect; +import android.os.CancellationSignal; import java.util.function.Consumer; @@ -58,8 +59,6 @@ import java.util.function.Consumer; * @see View#setScrollCaptureHint(int) * @see View#setScrollCaptureCallback(ScrollCaptureCallback) * @see Window#registerScrollCaptureCallback(ScrollCaptureCallback) - * - * @hide */ @UiThread public interface ScrollCaptureCallback { @@ -68,80 +67,84 @@ public interface ScrollCaptureCallback { * The system is searching for the appropriate scrolling container to capture and would like to * know the size and position of scrolling content handled by this callback. * <p> - * Implementations should inset {@code containingViewBounds} to cover only the area within the - * containing view where scrolling content may be positioned. This should cover only the content - * which tracks with scrolling movement. - * <p> - * Return the updated rectangle to {@code resultConsumer}. If for any reason the scrolling - * content is not available to capture, a {@code null} rectangle may be returned, and this view - * will be excluded as the target for this request. + * To determine scroll bounds, an implementation should inset the visible bounds of the + * containing view to cover only the area where scrolling content may be positioned. This + * should cover only the content which tracks with scrolling movement. * <p> - * Responses received after XXXms will be discarded. + * Return the updated rectangle to {@link Consumer#accept onReady.accept}. If for any reason the + * scrolling content is not available to capture, a empty rectangle may be returned which will + * exclude this view from consideration. * <p> - * TODO: finalize timeout + * This request may be cancelled via the provided {@link CancellationSignal}. When this happens, + * any future call to {@link Consumer#accept onReady.accept} will have no effect and this + * content will be omitted from the search results. * - * @param onReady consumer for the updated rectangle + * @param signal signal to cancel the operation in progress + * @param onReady consumer for the updated rectangle */ - void onScrollCaptureSearch(@NonNull Consumer<Rect> onReady); + void onScrollCaptureSearch(@NonNull CancellationSignal signal, @NonNull Consumer<Rect> onReady); /** * Scroll Capture has selected this callback to provide the scrolling image content. * <p> - * The onReady signal should be called when ready to begin handling image requests. + * {@link Runnable#run onReady.run} should be called when ready to begin handling image + * requests. + * <p> + * This request may be cancelled via the provided {@link CancellationSignal}. When this happens, + * any future call to {@link Runnable#run onReady.run} will have no effect and provided session + * will not be activated. + * + * @param session the current session, resources provided by it are valid for use until the + * {@link #onScrollCaptureEnd(Runnable) session ends} + * @param signal signal to cancel the operation in progress + * @param onReady signal used to report completion of the request */ - void onScrollCaptureStart(@NonNull ScrollCaptureSession session, @NonNull Runnable onReady); + void onScrollCaptureStart(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Runnable onReady); /** * An image capture has been requested from the scrolling content. * <p> - * <code>captureArea</code> contains the bounds of the image requested, relative to the - * rectangle provided by {@link ScrollCaptureCallback#onScrollCaptureSearch}, referred to as - * {@code scrollBounds}. - * here. - * <p> - * A series of requests will step by a constant vertical amount relative to {@code - * scrollBounds}, moving through the scrolling range of content, above and below the current - * visible area. The rectangle's vertical position will not account for any scrolling movement - * since capture started. Implementations therefore must track any scroll position changes and - * subtract this distance from requests. + * The requested rectangle describes an area inside the target view, relative to + * <code>scrollBounds</code>. The content may be offscreen, above or below the current visible + * portion of the target view. To handle the request, render the available portion of this + * rectangle to a buffer and return it via the Surface available from {@link + * ScrollCaptureSession#getSurface()}. * <p> - * To handle a request, the content should be scrolled to maximize the visible area of the - * requested rectangle. Offset {@code captureArea} again to account for any further scrolling. + * Note: Implementations are only required to render the requested content, and may do so into + * off-screen buffers without scrolling if they are able. * <p> - * Finally, clip this rectangle against scrollBounds to determine what portion, if any is - * visible content to capture. If the rectangle is completely clipped, set it to {@link - * Rect#setEmpty() empty} and skip the next step. - * <p> - * Make a copy of {@code captureArea}, transform to window coordinates and draw the window, - * clipped to this rectangle, into the {@link ScrollCaptureSession#getSurface() surface} at - * offset (0,0). - * <p> - * Finally, return the resulting {@code captureArea} using - * {@link ScrollCaptureSession#notifyBufferSent}. - * <p> - * If the response is not supplied within XXXms, the session will end with a call to {@link - * #onScrollCaptureEnd}, after which {@code session} is invalid and should be discarded. - * <p> - * TODO: finalize timeout + * The resulting available portion of the request must be computed as a portion of {@code + * captureArea}, and sent to signal the operation is complete, using {@link Consumer#accept + * onComplete.accept}. If the requested rectangle is partially or fully out of bounds the + * resulting portion should be returned. If no portion is available (outside of available + * content), then skip sending any buffer and report an empty Rect as result. * <p> + * This request may be cancelled via the provided {@link CancellationSignal}. When this happens, + * any future call to {@link Consumer#accept onComplete.accept} will be ignored until the next + * request. * + * @param session the current session, resources provided by it are valid for use until the + * {@link #onScrollCaptureEnd(Runnable) session ends} + * @param signal signal to cancel the operation in progress * @param captureArea the area to capture, a rectangle within {@code scrollBounds} + * @param onComplete a consumer for the captured area */ - void onScrollCaptureImageRequest( - @NonNull ScrollCaptureSession session, @NonNull Rect captureArea); + void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Rect captureArea, + @NonNull Consumer<Rect> onComplete); /** * Signals that capture has ended. Implementations should release any temporary resources or * references to objects in use during the capture. Any resources obtained from the session are * now invalid and attempts to use them after this point may throw an exception. * <p> - * The window should be returned as much as possible to its original state when capture started. - * At a minimum, the content should be scrolled to its original position. - * <p> - * <code>onReady</code> should be called when the window should be made visible and - * interactive. The system will wait up to XXXms for this call before proceeding. + * The window should be returned to its original state when capture started. At a minimum, the + * content should be scrolled to its original position. * <p> - * TODO: finalize timeout + * {@link Runnable#run onReady.run} should be called as soon as possible after the window is + * ready for normal interactive use. After the callback (or after a timeout, if not called) the + * screenshot tool will be dismissed and the window may become visible to the user at any time. * * @param onReady a callback to inform the system that the application has completed any * cleanup and is ready to become visible diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java index 0e6cdd1dbec5..3456e016c42c 100644 --- a/core/java/android/view/ScrollCaptureConnection.java +++ b/core/java/android/view/ScrollCaptureConnection.java @@ -18,18 +18,23 @@ package android.view; import static java.util.Objects.requireNonNull; +import android.annotation.BinderThread; import android.annotation.NonNull; import android.annotation.UiThread; -import android.annotation.WorkerThread; import android.graphics.Point; import android.graphics.Rect; -import android.os.Handler; +import android.os.CancellationSignal; +import android.os.ICancellationSignal; import android.os.RemoteException; +import android.os.Trace; import android.util.CloseGuard; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import java.util.concurrent.atomic.AtomicBoolean; +import java.lang.ref.WeakReference; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Mediator between a selected scroll capture target view and a remote process. @@ -41,270 +46,276 @@ import java.util.concurrent.atomic.AtomicBoolean; public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { private static final String TAG = "ScrollCaptureConnection"; - private static final int DEFAULT_TIMEOUT = 1000; - - private final Handler mHandler; - private ScrollCaptureTarget mSelectedTarget; - private int mTimeoutMillis = DEFAULT_TIMEOUT; - - protected Surface mSurface; - private IScrollCaptureCallbacks mCallbacks; + private final Object mLock = new Object(); private final Rect mScrollBounds; private final Point mPositionInWindow; private final CloseGuard mCloseGuard; + private final Executor mUiThread; + + private ScrollCaptureCallback mLocal; + private IScrollCaptureCallbacks mRemote; - // The current session instance in use by the callback. private ScrollCaptureSession mSession; - // Helps manage timeout callbacks registered to handler and aids testing. - private DelayedAction mTimeoutAction; + private CancellationSignal mCancellation; + + private volatile boolean mStarted; + private volatile boolean mConnected; /** * Constructs a ScrollCaptureConnection. * * @param selectedTarget the target the client is controlling - * @param callbacks the callbacks to reply to system requests + * @param remote the callbacks to reply to system requests * * @hide */ public ScrollCaptureConnection( + @NonNull Executor uiThread, @NonNull ScrollCaptureTarget selectedTarget, - @NonNull IScrollCaptureCallbacks callbacks) { + @NonNull IScrollCaptureCallbacks remote) { + mUiThread = requireNonNull(uiThread, "<uiThread> must non-null"); requireNonNull(selectedTarget, "<selectedTarget> must non-null"); - requireNonNull(callbacks, "<callbacks> must non-null"); - final Rect scrollBounds = requireNonNull(selectedTarget.getScrollBounds(), + mRemote = requireNonNull(remote, "<callbacks> must non-null"); + mScrollBounds = requireNonNull(Rect.copyOrNull(selectedTarget.getScrollBounds()), "target.getScrollBounds() must be non-null to construct a client"); - mSelectedTarget = selectedTarget; - mHandler = selectedTarget.getContainingView().getHandler(); - mScrollBounds = new Rect(scrollBounds); + mLocal = selectedTarget.getCallback(); mPositionInWindow = new Point(selectedTarget.getPositionInWindow()); - mCallbacks = callbacks; mCloseGuard = new CloseGuard(); mCloseGuard.open("close"); - - selectedTarget.getContainingView().addOnAttachStateChangeListener( - new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - - } - - @Override - public void onViewDetachedFromWindow(View v) { - selectedTarget.getContainingView().removeOnAttachStateChangeListener(this); - endCapture(); - } - }); - } - - @VisibleForTesting - public void setTimeoutMillis(int timeoutMillis) { - mTimeoutMillis = timeoutMillis; + mConnected = true; } - @VisibleForTesting - public DelayedAction getTimeoutAction() { - return mTimeoutAction; - } - - private void checkConnected() { - if (mSelectedTarget == null || mCallbacks == null) { - throw new IllegalStateException("This client has been disconnected."); + @BinderThread + @Override + public ICancellationSignal startCapture(Surface surface) throws RemoteException { + checkConnected(); + if (!surface.isValid()) { + throw new RemoteException(new IllegalArgumentException("surface must be valid")); } - } - private void checkStarted() { - if (mSession == null) { - throw new IllegalStateException("Capture session has not been started!"); - } - } + ICancellationSignal cancellation = CancellationSignal.createTransport(); + mCancellation = CancellationSignal.fromTransport(cancellation); + mSession = new ScrollCaptureSession(surface, mScrollBounds, mPositionInWindow); - @WorkerThread // IScrollCaptureConnection - @Override - public void startCapture(Surface surface) throws RemoteException { - checkConnected(); - mSurface = surface; - scheduleTimeout(mTimeoutMillis, this::onStartCaptureTimeout); - mSession = new ScrollCaptureSession(mSurface, mScrollBounds, mPositionInWindow, this); - mHandler.post(() -> mSelectedTarget.getCallback().onScrollCaptureStart(mSession, - this::onStartCaptureCompleted)); + Runnable listener = + SafeCallback.create(mCancellation, mUiThread, this::onStartCaptureCompleted); + // -> UiThread + mUiThread.execute(() -> mLocal.onScrollCaptureStart(mSession, mCancellation, listener)); + return cancellation; } @UiThread private void onStartCaptureCompleted() { - if (cancelTimeout()) { - mHandler.post(() -> { - try { - mCallbacks.onCaptureStarted(); - } catch (RemoteException e) { - doShutdown(); - } - }); + mStarted = true; + try { + mRemote.onCaptureStarted(); + } catch (RemoteException e) { + Log.w(TAG, "Shutting down due to error: ", e); + close(); } } - @UiThread - private void onStartCaptureTimeout() { - endCapture(); - } - @WorkerThread // IScrollCaptureConnection + @BinderThread @Override - public void requestImage(Rect requestRect) { + public ICancellationSignal requestImage(Rect requestRect) throws RemoteException { + Trace.beginSection("requestImage"); checkConnected(); checkStarted(); - scheduleTimeout(mTimeoutMillis, this::onRequestImageTimeout); - // Response is dispatched via ScrollCaptureSession, to onRequestImageCompleted - mHandler.post(() -> mSelectedTarget.getCallback().onScrollCaptureImageRequest( - mSession, new Rect(requestRect))); - } - @UiThread - void onRequestImageCompleted(long frameNumber, Rect capturedArea) { - final Rect finalCapturedArea = new Rect(capturedArea); - if (cancelTimeout()) { - mHandler.post(() -> { - try { - mCallbacks.onCaptureBufferSent(frameNumber, finalCapturedArea); - } catch (RemoteException e) { - doShutdown(); - } - }); - } + ICancellationSignal cancellation = CancellationSignal.createTransport(); + mCancellation = CancellationSignal.fromTransport(cancellation); + + Consumer<Rect> listener = + SafeCallback.create(mCancellation, mUiThread, this::onImageRequestCompleted); + // -> UiThread + mUiThread.execute(() -> mLocal.onScrollCaptureImageRequest( + mSession, mCancellation, new Rect(requestRect), listener)); + Trace.endSection(); + return cancellation; } @UiThread - private void onRequestImageTimeout() { - endCapture(); + void onImageRequestCompleted(Rect capturedArea) { + try { + mRemote.onImageRequestCompleted(0, capturedArea); + } catch (RemoteException e) { + Log.w(TAG, "Shutting down due to error: ", e); + close(); + } } - @WorkerThread // IScrollCaptureConnection + @BinderThread @Override - public void endCapture() { - if (isStarted()) { - scheduleTimeout(mTimeoutMillis, this::onEndCaptureTimeout); - mHandler.post(() -> - mSelectedTarget.getCallback().onScrollCaptureEnd(this::onEndCaptureCompleted)); - } else { - disconnect(); - } - } + public ICancellationSignal endCapture() throws RemoteException { + checkConnected(); + checkStarted(); - private boolean isStarted() { - return mCallbacks != null && mSelectedTarget != null; - } + ICancellationSignal cancellation = CancellationSignal.createTransport(); + mCancellation = CancellationSignal.fromTransport(cancellation); - @UiThread - private void onEndCaptureCompleted() { // onEndCaptureCompleted - if (cancelTimeout()) { - doShutdown(); - } + Runnable listener = + SafeCallback.create(mCancellation, mUiThread, this::onEndCaptureCompleted); + // -> UiThread + mUiThread.execute(() -> mLocal.onScrollCaptureEnd(listener)); + return cancellation; } @UiThread - private void onEndCaptureTimeout() { - doShutdown(); + private void onEndCaptureCompleted() { + synchronized (mLock) { + mStarted = false; + try { + mRemote.onCaptureEnded(); + } catch (RemoteException e) { + Log.w(TAG, "Shutting down due to error: ", e); + close(); + } + } } + @BinderThread + @Override + public void close() { + if (mStarted) { + Log.w(TAG, "close(): capture is still started?! Ending now."); - private void doShutdown() { - try { - if (mCallbacks != null) { - mCallbacks.onConnectionClosed(); - } - } catch (RemoteException e) { - // Ignore - } finally { - disconnect(); + // -> UiThread + mUiThread.execute(() -> mLocal.onScrollCaptureEnd(() -> { /* ignore */ })); + mStarted = false; } + disconnect(); } /** * Shuts down this client and releases references to dependent objects. No attempt is made * to notify the controller, use with caution! */ - public void disconnect() { - if (mSession != null) { - mSession.disconnect(); + private void disconnect() { + synchronized (mLock) { mSession = null; + mConnected = false; + mStarted = false; + mRemote = null; + mLocal = null; + mCloseGuard.close(); } + } + + public boolean isConnected() { + return mConnected; + } - mSelectedTarget = null; - mCallbacks = null; + public boolean isStarted() { + return mStarted; + } + + private synchronized void checkConnected() throws RemoteException { + synchronized (mLock) { + if (!mConnected) { + throw new RemoteException(new IllegalStateException("Not connected")); + } + } + } + + private void checkStarted() throws RemoteException { + synchronized (mLock) { + if (!mStarted) { + throw new RemoteException(new IllegalStateException("Not started!")); + } + } } /** @return a string representation of the state of this client */ public String toString() { return "ScrollCaptureConnection{" + + "connected=" + mConnected + + ", started=" + mStarted + ", session=" + mSession - + ", selectedTarget=" + mSelectedTarget - + ", clientCallbacks=" + mCallbacks + + ", remote=" + mRemote + + ", local=" + mLocal + "}"; } - private boolean cancelTimeout() { - if (mTimeoutAction != null) { - return mTimeoutAction.cancel(); - } - return false; + @VisibleForTesting + public CancellationSignal getCancellation() { + return mCancellation; } - private void scheduleTimeout(long timeoutMillis, Runnable action) { - if (mTimeoutAction != null) { - mTimeoutAction.cancel(); + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } finally { + super.finalize(); } - mTimeoutAction = new DelayedAction(mHandler, timeoutMillis, action); } - /** @hide */ - @VisibleForTesting - public static class DelayedAction { - private final AtomicBoolean mCompleted = new AtomicBoolean(); - private final Object mToken = new Object(); - private final Handler mHandler; - private final Runnable mAction; - - @VisibleForTesting - public DelayedAction(Handler handler, long timeoutMillis, Runnable action) { - mHandler = handler; - mAction = action; - mHandler.postDelayed(this::onTimeout, mToken, timeoutMillis); + private static class SafeCallback<T> { + private final CancellationSignal mSignal; + private final WeakReference<T> mTargetRef; + private final Executor mExecutor; + private boolean mExecuted; + + protected SafeCallback(CancellationSignal signal, Executor executor, T target) { + mSignal = signal; + mTargetRef = new WeakReference<>(target); + mExecutor = executor; } - private boolean onTimeout() { - if (mCompleted.compareAndSet(false, true)) { - mAction.run(); - return true; + // 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; + if (mSignal.isCanceled()) { + return; + } + T target = mTargetRef.get(); + if (target == null) { + return; } - return false; + mExecutor.execute(() -> targetConsumer.accept(target)); } - /** - * Cause the timeout action to run immediately and mark as timed out. - * - * @return true if the timeout was run, false if the timeout had already been canceled - */ - @VisibleForTesting - public boolean timeoutNow() { - return onTimeout(); + static Runnable create(CancellationSignal signal, Executor executor, Runnable target) { + return new RunnableCallback(signal, executor, target); } - /** - * Attempt to cancel the timeout action (such as after a callback is made) - * - * @return true if the timeout was canceled and will not run, false if time has expired and - * the timeout action has or will run momentarily - */ - public boolean cancel() { - if (!mCompleted.compareAndSet(false, true)) { - // Whoops, too late! - return false; - } - mHandler.removeCallbacksAndMessages(mToken); - return true; + static <T> Consumer<T> create(CancellationSignal signal, Executor executor, + Consumer<T> target) { + return new ConsumerCallback<T>(signal, executor, target); + } + } + + private static final class RunnableCallback extends SafeCallback<Runnable> implements Runnable { + RunnableCallback(CancellationSignal signal, Executor executor, Runnable target) { + super(signal, executor, target); + } + + @Override + public void run() { + maybeAccept(Runnable::run); + } + } + + private static final class ConsumerCallback<T> extends SafeCallback<Consumer<T>> + implements Consumer<T> { + ConsumerCallback(CancellationSignal signal, Executor executor, Consumer<T> target) { + super(signal, executor, target); + } + + @Override + public void accept(T value) { + maybeAccept((target) -> target.accept(value)); } } } diff --git a/core/java/android/service/screenshot/ScreenshotHash.aidl b/core/java/android/view/ScrollCaptureResponse.aidl index a7c50db171a2..3de2b80ef16e 100644 --- a/core/java/android/service/screenshot/ScreenshotHash.aidl +++ b/core/java/android/view/ScrollCaptureResponse.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 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,6 +14,6 @@ * limitations under the License. */ -package android.service.screenshot; +package android.view; -parcelable ScreenshotHash; +parcelable ScrollCaptureResponse; diff --git a/core/java/android/view/ScrollCaptureResponse.java b/core/java/android/view/ScrollCaptureResponse.java new file mode 100644 index 000000000000..564113edb3c7 --- /dev/null +++ b/core/java/android/view/ScrollCaptureResponse.java @@ -0,0 +1,381 @@ +/* + * 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.view; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Rect; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.util.ArrayList; + +/** @hide */ +@DataClass(genToString = true, genGetters = true) +public class ScrollCaptureResponse implements Parcelable { + + /** Developer-facing human readable description of the result. */ + @NonNull + private String mDescription = ""; + + // Remaining fields are non-null when isConnected() == true + + /** The active connection for a successful result. */ + @Nullable + @DataClass.MaySetToNull + private IScrollCaptureConnection mConnection = null; + + /** The bounds of the window within the display */ + @Nullable + private Rect mWindowBounds = null; + + /** The bounds of the scrolling content, in window space. */ + @Nullable + private Rect mBoundsInWindow = null; + + /** The current window title. */ + @Nullable + private String mWindowTitle = null; + + /** Carries additional logging and debugging information when enabled. */ + @NonNull + @DataClass.PluralOf("message") + private ArrayList<String> mMessages = new ArrayList<>(); + + /** Whether a connection has been returned. */ + public boolean isConnected() { + return mConnection != null; + } + + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/ScrollCaptureResponse.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ ScrollCaptureResponse( + @NonNull String description, + @Nullable IScrollCaptureConnection connection, + @Nullable Rect windowBounds, + @Nullable Rect boundsInWindow, + @Nullable String windowTitle, + @NonNull ArrayList<String> messages) { + this.mDescription = description; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mDescription); + this.mConnection = connection; + this.mWindowBounds = windowBounds; + this.mBoundsInWindow = boundsInWindow; + this.mWindowTitle = windowTitle; + this.mMessages = messages; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mMessages); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * Developer-facing human readable description of the result. + */ + @DataClass.Generated.Member + public @NonNull String getDescription() { + return mDescription; + } + + /** + * The active connection for a successful result. + */ + @DataClass.Generated.Member + public @Nullable IScrollCaptureConnection getConnection() { + return mConnection; + } + + /** + * The bounds of the window within the display + */ + @DataClass.Generated.Member + public @Nullable Rect getWindowBounds() { + return mWindowBounds; + } + + /** + * The bounds of the scrolling content, in window space. + */ + @DataClass.Generated.Member + public @Nullable Rect getBoundsInWindow() { + return mBoundsInWindow; + } + + /** + * The current window title. + */ + @DataClass.Generated.Member + public @Nullable String getWindowTitle() { + return mWindowTitle; + } + + /** + * Carries additional logging and debugging information when enabled. + */ + @DataClass.Generated.Member + public @NonNull ArrayList<String> getMessages() { + return mMessages; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "ScrollCaptureResponse { " + + "description = " + mDescription + ", " + + "connection = " + mConnection + ", " + + "windowBounds = " + mWindowBounds + ", " + + "boundsInWindow = " + mBoundsInWindow + ", " + + "windowTitle = " + mWindowTitle + ", " + + "messages = " + mMessages + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mConnection != null) flg |= 0x2; + if (mWindowBounds != null) flg |= 0x4; + if (mBoundsInWindow != null) flg |= 0x8; + if (mWindowTitle != null) flg |= 0x10; + dest.writeByte(flg); + dest.writeString(mDescription); + if (mConnection != null) dest.writeStrongInterface(mConnection); + if (mWindowBounds != null) dest.writeTypedObject(mWindowBounds, flags); + if (mBoundsInWindow != null) dest.writeTypedObject(mBoundsInWindow, flags); + if (mWindowTitle != null) dest.writeString(mWindowTitle); + dest.writeStringList(mMessages); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected ScrollCaptureResponse(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + String description = in.readString(); + IScrollCaptureConnection connection = (flg & 0x2) == 0 ? null : IScrollCaptureConnection.Stub.asInterface(in.readStrongBinder()); + Rect windowBounds = (flg & 0x4) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR); + Rect boundsInWindow = (flg & 0x8) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR); + String windowTitle = (flg & 0x10) == 0 ? null : in.readString(); + ArrayList<String> messages = new ArrayList<>(); + in.readStringList(messages); + + this.mDescription = description; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mDescription); + this.mConnection = connection; + this.mWindowBounds = windowBounds; + this.mBoundsInWindow = boundsInWindow; + this.mWindowTitle = windowTitle; + this.mMessages = messages; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mMessages); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<ScrollCaptureResponse> CREATOR + = new Parcelable.Creator<ScrollCaptureResponse>() { + @Override + public ScrollCaptureResponse[] newArray(int size) { + return new ScrollCaptureResponse[size]; + } + + @Override + public ScrollCaptureResponse createFromParcel(@NonNull android.os.Parcel in) { + return new ScrollCaptureResponse(in); + } + }; + + /** + * A builder for {@link ScrollCaptureResponse} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static class Builder { + + private @NonNull String mDescription; + private @Nullable IScrollCaptureConnection mConnection; + private @Nullable Rect mWindowBounds; + private @Nullable Rect mBoundsInWindow; + private @Nullable String mWindowTitle; + private @NonNull ArrayList<String> mMessages; + + private long mBuilderFieldsSet = 0L; + + public Builder() { + } + + /** + * Developer-facing human readable description of the result. + */ + @DataClass.Generated.Member + public @NonNull Builder setDescription(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mDescription = value; + return this; + } + + /** + * The active connection for a successful result. + */ + @DataClass.Generated.Member + public @NonNull Builder setConnection(@Nullable IScrollCaptureConnection value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mConnection = value; + return this; + } + + /** + * The bounds of the window within the display + */ + @DataClass.Generated.Member + public @NonNull Builder setWindowBounds(@NonNull Rect value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mWindowBounds = value; + return this; + } + + /** + * The bounds of the scrolling content, in window space. + */ + @DataClass.Generated.Member + public @NonNull Builder setBoundsInWindow(@NonNull Rect value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; + mBoundsInWindow = value; + return this; + } + + /** + * The current window title. + */ + @DataClass.Generated.Member + public @NonNull Builder setWindowTitle(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; + mWindowTitle = value; + return this; + } + + /** + * Carries additional logging and debugging information when enabled. + */ + @DataClass.Generated.Member + public @NonNull Builder setMessages(@NonNull ArrayList<String> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x20; + mMessages = value; + return this; + } + + /** @see #setMessages */ + @DataClass.Generated.Member + public @NonNull Builder addMessage(@NonNull String value) { + if (mMessages == null) setMessages(new ArrayList<>()); + mMessages.add(value); + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull ScrollCaptureResponse build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x40; // Mark builder used + + if ((mBuilderFieldsSet & 0x1) == 0) { + mDescription = ""; + } + if ((mBuilderFieldsSet & 0x2) == 0) { + mConnection = null; + } + if ((mBuilderFieldsSet & 0x4) == 0) { + mWindowBounds = null; + } + if ((mBuilderFieldsSet & 0x8) == 0) { + mBoundsInWindow = null; + } + if ((mBuilderFieldsSet & 0x10) == 0) { + mWindowTitle = null; + } + if ((mBuilderFieldsSet & 0x20) == 0) { + mMessages = new ArrayList<>(); + } + ScrollCaptureResponse o = new ScrollCaptureResponse( + mDescription, + mConnection, + mWindowBounds, + mBoundsInWindow, + mWindowTitle, + mMessages); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x40) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1612282689462L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/ScrollCaptureResponse.java", + inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/view/ScrollCaptureSearchResults.java b/core/java/android/view/ScrollCaptureSearchResults.java new file mode 100644 index 000000000000..3469b9dc7103 --- /dev/null +++ b/core/java/android/view/ScrollCaptureSearchResults.java @@ -0,0 +1,271 @@ +/* + * 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.view; + +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; +import android.annotation.UiThread; +import android.graphics.Rect; +import android.os.CancellationSignal; +import android.util.IndentingPrintWriter; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * Collects nodes in the view hierarchy which have been identified as scrollable content. + * + * @hide + */ +@UiThread +public final class ScrollCaptureSearchResults { + private final Executor mExecutor; + private final List<ScrollCaptureTarget> mTargets; + private final CancellationSignal mCancel; + + private Runnable mOnCompleteListener; + private int mCompleted; + private boolean mComplete = true; + + public ScrollCaptureSearchResults(Executor executor) { + mExecutor = executor; + mTargets = new ArrayList<>(); + mCancel = new CancellationSignal(); + } + + // Public + + /** + * Add the given target to the results. + * + * @param target the target to consider + */ + public void addTarget(@NonNull ScrollCaptureTarget target) { + requireNonNull(target); + + mTargets.add(target); + mComplete = false; + final ScrollCaptureCallback callback = target.getCallback(); + final Consumer<Rect> consumer = new SearchRequest(target); + + // Defer so the view hierarchy scan completes first + mExecutor.execute( + () -> callback.onScrollCaptureSearch(mCancel, consumer)); + } + + public boolean isComplete() { + return mComplete; + } + + /** + * Provides a callback to be invoked as soon as all responses have been received from all + * targets to this point. + * + * @param onComplete listener to add + */ + public void setOnCompleteListener(Runnable onComplete) { + if (mComplete) { + onComplete.run(); + } else { + mOnCompleteListener = onComplete; + } + } + + /** + * Indicates whether the search results are empty. + * + * @return true if no targets have been added + */ + public boolean isEmpty() { + return mTargets.isEmpty(); + } + + /** + * Force the results to complete now, cancelling any pending requests and calling a complete + * listener if provided. + */ + public void finish() { + if (!mComplete) { + mCancel.cancel(); + signalComplete(); + } + } + + private void signalComplete() { + mComplete = true; + mTargets.sort(PRIORITY_ORDER); + if (mOnCompleteListener != null) { + mOnCompleteListener.run(); + mOnCompleteListener = null; + } + } + + @VisibleForTesting + public List<ScrollCaptureTarget> getTargets() { + return new ArrayList<>(mTargets); + } + + /** + * Get the top ranked result out of all completed requests. + * + * @return the top ranked result + */ + public ScrollCaptureTarget getTopResult() { + ScrollCaptureTarget target = mTargets.isEmpty() ? null : mTargets.get(0); + return target != null && target.getScrollBounds() != null ? target : null; + } + + private class SearchRequest implements Consumer<Rect> { + private ScrollCaptureTarget mTarget; + + SearchRequest(ScrollCaptureTarget target) { + mTarget = target; + } + + @Override + public void accept(Rect scrollBounds) { + if (mTarget == null || mCancel.isCanceled()) { + return; + } + mExecutor.execute(() -> consume(scrollBounds)); + } + + private void consume(Rect scrollBounds) { + if (mTarget == null || mCancel.isCanceled()) { + return; + } + if (!nullOrEmpty(scrollBounds)) { + mTarget.setScrollBounds(scrollBounds); + mTarget.updatePositionInWindow(); + } + mCompleted++; + mTarget = null; + + // All done? + if (mCompleted == mTargets.size()) { + signalComplete(); + } + } + } + + private static final int AFTER = 1; + private static final int BEFORE = -1; + private static final int EQUAL = 0; + + static final Comparator<ScrollCaptureTarget> PRIORITY_ORDER = (a, b) -> { + if (a == null && b == null) { + return 0; + } else if (a == null || b == null) { + return (a == null) ? 1 : -1; + } + + boolean emptyScrollBoundsA = nullOrEmpty(a.getScrollBounds()); + boolean emptyScrollBoundsB = nullOrEmpty(b.getScrollBounds()); + if (emptyScrollBoundsA || emptyScrollBoundsB) { + if (emptyScrollBoundsA && emptyScrollBoundsB) { + return EQUAL; + } + // Prefer the one with a non-empty scroll bounds + if (emptyScrollBoundsA) { + return AFTER; + } + return BEFORE; + } + + final View viewA = a.getContainingView(); + final View viewB = b.getContainingView(); + + // Prefer any view with scrollCaptureHint="INCLUDE", over one without + // This is an escape hatch for the next rule (descendants first) + boolean hintIncludeA = hasIncludeHint(viewA); + boolean hintIncludeB = hasIncludeHint(viewB); + if (hintIncludeA != hintIncludeB) { + return (hintIncludeA) ? BEFORE : AFTER; + } + // If the views are relatives, prefer the descendant. This allows implementations to + // leverage nested scrolling APIs by interacting with the innermost scrollable view (as + // would happen with touch input). + if (isDescendant(viewA, viewB)) { + return BEFORE; + } + if (isDescendant(viewB, viewA)) { + return AFTER; + } + + // finally, prefer one with larger scroll bounds + int scrollAreaA = area(a.getScrollBounds()); + int scrollAreaB = area(b.getScrollBounds()); + return (scrollAreaA >= scrollAreaB) ? BEFORE : AFTER; + }; + + private static int area(Rect r) { + return r.width() * r.height(); + } + + private static boolean nullOrEmpty(Rect r) { + return r == null || r.isEmpty(); + } + + private static boolean hasIncludeHint(View view) { + return (view.getScrollCaptureHint() & View.SCROLL_CAPTURE_HINT_INCLUDE) != 0; + } + + /** + * Determines if {@code otherView} is a descendant of {@code view}. + * + * @param view a view + * @param otherView another view + * @return true if {@code view} is an ancestor of {@code otherView} + */ + private static boolean isDescendant(@NonNull View view, @NonNull View otherView) { + if (view == otherView) { + return false; + } + ViewParent otherParent = otherView.getParent(); + while (otherParent != view && otherParent != null) { + otherParent = otherParent.getParent(); + } + return otherParent == view; + } + + void dump(IndentingPrintWriter writer) { + writer.println("results:"); + writer.increaseIndent(); + writer.println("complete: " + isComplete()); + writer.println("cancelled: " + mCancel.isCanceled()); + writer.println("targets:"); + writer.increaseIndent(); + if (isEmpty()) { + writer.println("None"); + } else { + for (int i = 0; i < mTargets.size(); i++) { + writer.println("[" + i + "]"); + writer.increaseIndent(); + mTargets.get(i).dump(writer); + writer.decreaseIndent(); + } + writer.decreaseIndent(); + } + writer.decreaseIndent(); + } +} diff --git a/core/java/android/view/ScrollCaptureSession.java b/core/java/android/view/ScrollCaptureSession.java index 92617a325265..748e7ea56f41 100644 --- a/core/java/android/view/ScrollCaptureSession.java +++ b/core/java/android/view/ScrollCaptureSession.java @@ -16,18 +16,15 @@ package android.view; +import static java.util.Objects.requireNonNull; + import android.annotation.NonNull; -import android.annotation.Nullable; import android.graphics.Point; import android.graphics.Rect; /** * A session represents the scope of interaction between a {@link ScrollCaptureCallback} and the - * system during an active scroll capture operation. During the scope of a session, a callback - * will receive a series of requests for image data. Resources provided here are valid for use - * until {@link ScrollCaptureCallback#onScrollCaptureEnd(Runnable)}. - * - * @hide + * system during an active scroll capture operation. */ public class ScrollCaptureSession { @@ -35,22 +32,27 @@ public class ScrollCaptureSession { private final Rect mScrollBounds; private final Point mPositionInWindow; - @Nullable - private ScrollCaptureConnection mConnection; - - /** @hide */ - public ScrollCaptureSession(Surface surface, Rect scrollBounds, Point positionInWindow, - ScrollCaptureConnection connection) { - mSurface = surface; - mScrollBounds = scrollBounds; - mPositionInWindow = positionInWindow; - mConnection = connection; + /** + * Constructs a new session instance. + * + * @param surface the surface to consume generated images + * @param scrollBounds the bounds of the capture area within the containing view + * @param positionInWindow the offset of scrollBounds within the window + */ + public ScrollCaptureSession(@NonNull Surface surface, @NonNull Rect scrollBounds, + @NonNull Point positionInWindow) { + mSurface = requireNonNull(surface); + mScrollBounds = requireNonNull(scrollBounds); + mPositionInWindow = requireNonNull(positionInWindow); } /** * Returns a * <a href="https://source.android.com/devices/graphics/arch-bq-gralloc">BufferQueue</a> in the * form of a {@link Surface} for transfer of image buffers. + * <p> + * The surface is guaranteed to remain {@link Surface#isValid() valid} until the session + * {@link ScrollCaptureCallback#onScrollCaptureEnd(Runnable) ends}. * * @return the surface for transferring image buffers * @throws IllegalStateException if the session has been closed @@ -80,26 +82,4 @@ public class ScrollCaptureSession { public Point getPositionInWindow() { return mPositionInWindow; } - - /** - * Notify the system that an a buffer has been posted via the getSurface() channel. - * - * @param frameNumber the frame number of the queued buffer - * @param capturedArea the area captured, relative to scroll bounds - */ - public void notifyBufferSent(long frameNumber, @NonNull Rect capturedArea) { - if (mConnection != null) { - mConnection.onRequestImageCompleted(frameNumber, capturedArea); - } - } - - /** - * @hide - */ - public void disconnect() { - mConnection = null; - if (mSurface.isValid()) { - mSurface.release(); - } - } } diff --git a/core/java/android/view/ScrollCaptureTarget.java b/core/java/android/view/ScrollCaptureTarget.java index f3fcabb26b31..4fd48892da70 100644 --- a/core/java/android/view/ScrollCaptureTarget.java +++ b/core/java/android/view/ScrollCaptureTarget.java @@ -22,14 +22,16 @@ import android.annotation.UiThread; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; +import android.os.CancellationSignal; import com.android.internal.util.FastMath; +import java.io.PrintWriter; +import java.util.function.Consumer; + /** * A target collects the set of contextual information for a ScrollCaptureHandler discovered during * a {@link View#dispatchScrollCaptureSearch scroll capture search}. - * - * @hide */ public final class ScrollCaptureTarget { private final View mContainingView; @@ -41,7 +43,6 @@ public final class ScrollCaptureTarget { private final float[] mTmpFloatArr = new float[2]; private final Matrix mMatrixViewLocalToWindow = new Matrix(); - private final Rect mTmpRect = new Rect(); public ScrollCaptureTarget(@NonNull View scrollTarget, @NonNull Rect localVisibleRect, @NonNull Point positionInWindow, @NonNull ScrollCaptureCallback callback) { @@ -52,7 +53,10 @@ public final class ScrollCaptureTarget { mPositionInWindow = positionInWindow; } - /** @return the hint that the {@code containing view} had during the scroll capture search */ + /** + * @return the hint that the {@code containing view} had during the scroll capture search + * @see View#getScrollCaptureHint() + */ @View.ScrollCaptureHint public int getHint() { return mHint; @@ -71,8 +75,7 @@ public final class ScrollCaptureTarget { } /** - * Returns the un-clipped, visible bounds of the containing view during the scroll capture - * search. This is used to determine on-screen area to assist in selecting the primary target. + * Returns the visible bounds of the containing view. * * @return the visible bounds of the {@code containing view} in view-local coordinates */ @@ -81,13 +84,17 @@ public final class ScrollCaptureTarget { return mLocalVisibleRect; } - /** @return the position of the {@code containing view} within the window */ + /** @return the position of the visible bounds of the containing view within the window */ @NonNull public Point getPositionInWindow() { return mPositionInWindow; } - /** @return the {@code scroll bounds} for this {@link ScrollCaptureCallback callback} */ + /** + * @return the {@code scroll bounds} for this {@link ScrollCaptureCallback callback} + * + * @see ScrollCaptureCallback#onScrollCaptureSearch(CancellationSignal, Consumer) + */ @Nullable public Rect getScrollBounds() { return mScrollBounds; @@ -119,8 +126,8 @@ public final class ScrollCaptureTarget { } /** - * Refresh the value of {@link #mLocalVisibleRect} and {@link #mPositionInWindow} based on the - * current state of the {@code containing view}. + * Refresh the local visible bounds and it's offset within the window, based on the current + * state of the {@code containing view}. */ @UiThread public void updatePositionInWindow() { @@ -132,4 +139,27 @@ public final class ScrollCaptureTarget { roundIntoPoint(mPositionInWindow, mTmpFloatArr); } + public String toString() { + return "ScrollCaptureTarget{" + "view=" + mContainingView + + ", callback=" + mCallback + + ", scrollBounds=" + mScrollBounds + + ", localVisibleRect=" + mLocalVisibleRect + + ", positionInWindow=" + mPositionInWindow + + "}"; + } + + void dump(@NonNull PrintWriter writer) { + View view = getContainingView(); + writer.println("view: " + view); + writer.println("hint: " + mHint); + writer.println("callback: " + mCallback); + writer.println("scrollBounds: " + + (mScrollBounds == null ? "null" : mScrollBounds.toShortString())); + Point inWindow = getPositionInWindow(); + writer.println("positionInWindow: " + + ((inWindow == null) ? "null" : "[" + inWindow.x + "," + inWindow.y + "]")); + Rect localVisible = getLocalVisibleRect(); + writer.println("localVisibleRect: " + + (localVisible == null ? "null" : localVisible.toShortString())); + } } diff --git a/core/java/android/view/ScrollCaptureTargetResolver.java b/core/java/android/view/ScrollCaptureTargetResolver.java deleted file mode 100644 index e4316bbc9397..000000000000 --- a/core/java/android/view/ScrollCaptureTargetResolver.java +++ /dev/null @@ -1,337 +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.view; - -import android.annotation.AnyThread; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UiThread; -import android.graphics.Rect; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - - -import java.util.Queue; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; - -/** - * Queries additional state from a list of {@link ScrollCaptureTarget targets} via asynchronous - * callbacks, then aggregates and reduces the target list to a single target, or null if no target - * is suitable. - * <p> - * The rules for selection are (in order): - * <ul> - * <li>prefer getScrollBounds(): non-empty - * <li>prefer View.getScrollCaptureHint == SCROLL_CAPTURE_HINT_INCLUDE - * <li>prefer descendants before parents - * <li>prefer larger area for getScrollBounds() (clipped to view bounds) - * </ul> - * - * <p> - * All calls to {@link ScrollCaptureCallback#onScrollCaptureSearch} are made on the main thread, - * with results are queued and consumed to the main thread as well. - * - * @see #start(Handler, long, Consumer) - * - * @hide - */ -@UiThread -public class ScrollCaptureTargetResolver { - private static final String TAG = "ScrollCaptureTargetRes"; - - private final Object mLock = new Object(); - - private final Queue<ScrollCaptureTarget> mTargets; - private Handler mHandler; - private long mTimeLimitMillis; - - private Consumer<ScrollCaptureTarget> mWhenComplete; - private int mPendingBoundsRequests; - private long mDeadlineMillis; - - private ScrollCaptureTarget mResult; - private boolean mFinished; - - private boolean mStarted; - - private static int area(Rect r) { - return r.width() * r.height(); - } - - private static boolean nullOrEmpty(Rect r) { - return r == null || r.isEmpty(); - } - - /** - * Binary operator which selects the best {@link ScrollCaptureTarget}. - */ - private static ScrollCaptureTarget chooseTarget(ScrollCaptureTarget a, ScrollCaptureTarget b) { - if (a == null && b == null) { - return null; - } else if (a == null || b == null) { - ScrollCaptureTarget c = (a == null) ? b : a; - return c; - } - - boolean emptyScrollBoundsA = nullOrEmpty(a.getScrollBounds()); - boolean emptyScrollBoundsB = nullOrEmpty(b.getScrollBounds()); - if (emptyScrollBoundsA || emptyScrollBoundsB) { - if (emptyScrollBoundsA && emptyScrollBoundsB) { - // Both have an empty or null scrollBounds - Log.d(TAG, "chooseTarget: (both have empty or null bounds) return " + null); - return null; - } - // Prefer the one with a non-empty scroll bounds - if (emptyScrollBoundsA) { - Log.d(TAG, "chooseTarget: (a has empty or null bounds) return " + b); - return b; - } - Log.d(TAG, "chooseTarget: (b has empty or null bounds) return " + a); - return a; - } - - final View viewA = a.getContainingView(); - final View viewB = b.getContainingView(); - - // Prefer any view with scrollCaptureHint="INCLUDE", over one without - // This is an escape hatch for the next rule (descendants first) - boolean hintIncludeA = hasIncludeHint(viewA); - boolean hintIncludeB = hasIncludeHint(viewB); - if (hintIncludeA != hintIncludeB) { - ScrollCaptureTarget c = (hintIncludeA) ? a : b; - Log.d(TAG, "chooseTarget: (has hint=INCLUDE) return " + c); - return c; - } - - // If the views are relatives, prefer the descendant. This allows implementations to - // leverage nested scrolling APIs by interacting with the innermost scrollable view (as - // would happen with touch input). - if (isDescendant(viewA, viewB)) { - Log.d(TAG, "chooseTarget: (b is descendant of a) return " + b); - return b; - } - if (isDescendant(viewB, viewA)) { - Log.d(TAG, "chooseTarget: (a is descendant of b) return " + a); - return a; - } - - // finally, prefer one with larger scroll bounds - int scrollAreaA = area(a.getScrollBounds()); - int scrollAreaB = area(b.getScrollBounds()); - ScrollCaptureTarget c = (scrollAreaA >= scrollAreaB) ? a : b; - Log.d(TAG, "chooseTarget: return " + c); - return c; - } - - /** - * Creates an instance to query and filter {@code target}. - * - * @param targets a list of {@link ScrollCaptureTarget} as collected by {@link - * View#dispatchScrollCaptureSearch}. - * @see #start(Handler, long, Consumer) - */ - public ScrollCaptureTargetResolver(Queue<ScrollCaptureTarget> targets) { - mTargets = targets; - } - - void checkThread() { - if (mHandler.getLooper() != Looper.myLooper()) { - throw new IllegalStateException("Called from wrong thread! (" - + Thread.currentThread().getName() + ")"); - } - } - - /** - * Blocks until a result is returned (after completion or timeout). - * <p> - * For testing only. Normal usage should receive a callback after calling {@link #start}. - */ - @VisibleForTesting - public ScrollCaptureTarget waitForResult() throws InterruptedException { - synchronized (mLock) { - while (!mFinished) { - mLock.wait(); - } - } - return mResult; - } - - private void supplyResult(ScrollCaptureTarget target) { - checkThread(); - if (mFinished) { - return; - } - mResult = chooseTarget(mResult, target); - boolean finish = mPendingBoundsRequests == 0 - || SystemClock.uptimeMillis() >= mDeadlineMillis; - if (finish) { - mPendingBoundsRequests = 0; - mWhenComplete.accept(mResult); - synchronized (mLock) { - mFinished = true; - mLock.notify(); - } - mWhenComplete = null; - } - } - - /** - * Asks all targets for {@link ScrollCaptureCallback#onScrollCaptureSearch(Consumer) - * scrollBounds}, and selects the primary target according to the {@link - * #chooseTarget} function. - * - * @param timeLimitMillis the amount of time to wait for all responses before delivering the top - * result - * @param resultConsumer the consumer to receive the primary target - */ - @AnyThread - public void start(Handler uiHandler, long timeLimitMillis, - Consumer<ScrollCaptureTarget> resultConsumer) { - synchronized (mLock) { - if (mStarted) { - throw new IllegalStateException("already started!"); - } - if (timeLimitMillis < 0) { - throw new IllegalArgumentException("Time limit must be positive"); - } - mHandler = uiHandler; - mTimeLimitMillis = timeLimitMillis; - mWhenComplete = resultConsumer; - if (mTargets.isEmpty()) { - mHandler.post(() -> supplyResult(null)); - return; - } - mStarted = true; - uiHandler.post(this::run); - } - } - - private void run() { - checkThread(); - - mPendingBoundsRequests = mTargets.size(); - for (ScrollCaptureTarget target : mTargets) { - queryTarget(target); - } - mDeadlineMillis = SystemClock.uptimeMillis() + mTimeLimitMillis; - mHandler.postAtTime(mTimeoutRunnable, mDeadlineMillis); - } - - private final Runnable mTimeoutRunnable = () -> { - checkThread(); - supplyResult(null); - }; - - /** - * Adds a target to the list and requests {@link ScrollCaptureCallback#onScrollCaptureSearch} - * scrollBounds} from it. Results are returned by a call to {@link #onScrollBoundsProvided}. - * - * @param target the target to add - */ - @UiThread - private void queryTarget(@NonNull ScrollCaptureTarget target) { - checkThread(); - final ScrollCaptureCallback callback = target.getCallback(); - // from the UI thread, request scroll bounds - callback.onScrollCaptureSearch( - // allow only one callback to onReady.accept(): - new SingletonConsumer<Rect>( - // Queue and consume on the UI thread - ((scrollBounds) -> mHandler.post( - () -> onScrollBoundsProvided(target, scrollBounds))))); - } - - @UiThread - private void onScrollBoundsProvided(ScrollCaptureTarget target, @Nullable Rect scrollBounds) { - checkThread(); - if (mFinished) { - return; - } - - // Record progress. - mPendingBoundsRequests--; - - // Remove the timeout. - mHandler.removeCallbacks(mTimeoutRunnable); - - boolean doneOrTimedOut = mPendingBoundsRequests == 0 - || SystemClock.uptimeMillis() >= mDeadlineMillis; - - final View containingView = target.getContainingView(); - if (!nullOrEmpty(scrollBounds) && containingView.isAggregatedVisible()) { - target.updatePositionInWindow(); - target.setScrollBounds(scrollBounds); - supplyResult(target); - } - - if (!mFinished) { - // Reschedule the timeout. - mHandler.postAtTime(mTimeoutRunnable, mDeadlineMillis); - } - } - - private static boolean hasIncludeHint(View view) { - return (view.getScrollCaptureHint() & View.SCROLL_CAPTURE_HINT_INCLUDE) != 0; - } - - /** - * Determines if {@code otherView} is a descendant of {@code view}. - * - * @param view a view - * @param otherView another view - * @return true if {@code view} is an ancestor of {@code otherView} - */ - private static boolean isDescendant(@NonNull View view, @NonNull View otherView) { - if (view == otherView) { - return false; - } - ViewParent otherParent = otherView.getParent(); - while (otherParent != view && otherParent != null) { - otherParent = otherParent.getParent(); - } - return otherParent == view; - } - - /** - * A safe wrapper for a consumer callbacks intended to accept a single value. It ensures - * that the receiver of the consumer does not retain a reference to {@code target} after use nor - * cause race conditions by invoking {@link Consumer#accept accept} more than once. - */ - static class SingletonConsumer<T> implements Consumer<T> { - final AtomicReference<Consumer<T>> mAtomicRef; - - /** - * @param target the target consumer - **/ - SingletonConsumer(Consumer<T> target) { - mAtomicRef = new AtomicReference<>(target); - } - - @Override - public void accept(T t) { - final Consumer<T> consumer = mAtomicRef.getAndSet(null); - if (consumer != null) { - consumer.accept(t); - } - } - } -} diff --git a/core/java/android/view/SoundEffectConstants.java b/core/java/android/view/SoundEffectConstants.java index 8d891bbc2cfc..f177451783dc 100644 --- a/core/java/android/view/SoundEffectConstants.java +++ b/core/java/android/view/SoundEffectConstants.java @@ -16,12 +16,21 @@ package android.view; +import android.media.AudioManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; + +import java.util.Random; + /** - * Constants to be used to play sound effects via {@link View#playSoundEffect(int)} + * Constants to be used to play sound effects via {@link View#playSoundEffect(int)} */ public class SoundEffectConstants { private SoundEffectConstants() {} + private static final Random NAVIGATION_REPEAT_RANDOMIZER = new Random(); + private static int sLastNavigationRepeatSoundEffectId = -1; public static final int CLICK = 0; @@ -29,6 +38,14 @@ public class SoundEffectConstants { public static final int NAVIGATION_UP = 2; public static final int NAVIGATION_RIGHT = 3; public static final int NAVIGATION_DOWN = 4; + /** Sound effect for a repeatedly triggered navigation, e.g. due to long pressing a button */ + public static final int NAVIGATION_REPEAT_LEFT = 5; + /** @see #NAVIGATION_REPEAT_LEFT */ + public static final int NAVIGATION_REPEAT_UP = 6; + /** @see #NAVIGATION_REPEAT_LEFT */ + public static final int NAVIGATION_REPEAT_RIGHT = 7; + /** @see #NAVIGATION_REPEAT_LEFT */ + public static final int NAVIGATION_REPEAT_DOWN = 8; /** * Get the sonification constant for the focus directions. @@ -40,7 +57,7 @@ public class SoundEffectConstants { * @throws {@link IllegalArgumentException} when the passed direction is not one of the * documented values. */ - public static int getContantForFocusDirection(int direction) { + public static int getContantForFocusDirection(@View.FocusDirection int direction) { switch (direction) { case View.FOCUS_RIGHT: return SoundEffectConstants.NAVIGATION_RIGHT; @@ -56,4 +73,65 @@ public class SoundEffectConstants { throw new IllegalArgumentException("direction must be one of " + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, FOCUS_FORWARD, FOCUS_BACKWARD}."); } + + /** + * Get the sonification constant for the focus directions + * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN}, + * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD} + * or {@link View#FOCUS_BACKWARD} + * @param repeating True if the user long-presses a direction + * @return The appropriate sonification constant + * @throws IllegalArgumentException when the passed direction is not one of the + * documented values. + */ + public static int getConstantForFocusDirection(@View.FocusDirection int direction, + boolean repeating) { + if (repeating) { + switch (direction) { + case View.FOCUS_RIGHT: + return SoundEffectConstants.NAVIGATION_REPEAT_RIGHT; + case View.FOCUS_FORWARD: + case View.FOCUS_DOWN: + return SoundEffectConstants.NAVIGATION_REPEAT_DOWN; + case View.FOCUS_LEFT: + return SoundEffectConstants.NAVIGATION_REPEAT_LEFT; + case View.FOCUS_BACKWARD: + case View.FOCUS_UP: + return SoundEffectConstants.NAVIGATION_REPEAT_UP; + } + throw new IllegalArgumentException("direction must be one of {FOCUS_UP, FOCUS_DOWN, " + + "FOCUS_LEFT, FOCUS_RIGHT, FOCUS_FORWARD, FOCUS_BACKWARD}."); + } else { + return getContantForFocusDirection(direction); + } + } + + /** + * @param effectId any of the effect ids defined in {@link SoundEffectConstants} + * @return true if the given effect id is a navigation repeat one + * @hide + */ + @VisibleForTesting(visibility = Visibility.PACKAGE) + public static boolean isNavigationRepeat(int effectId) { + return effectId == SoundEffectConstants.NAVIGATION_REPEAT_DOWN + || effectId == SoundEffectConstants.NAVIGATION_REPEAT_LEFT + || effectId == SoundEffectConstants.NAVIGATION_REPEAT_RIGHT + || effectId == SoundEffectConstants.NAVIGATION_REPEAT_UP; + } + + /** + * @return The next navigation repeat sound effect id, chosen at random in a non-repeating + * fashion + * @hide + */ + @VisibleForTesting(visibility = Visibility.PACKAGE) + public static int nextNavigationRepeatSoundEffectId() { + int next = NAVIGATION_REPEAT_RANDOMIZER.nextInt( + AudioManager.NUM_NAVIGATION_REPEAT_SOUND_EFFECTS - 1); + if (next >= sLastNavigationRepeatSoundEffectId) { + next++; + } + sLastNavigationRepeatSoundEffectId = next; + return AudioManager.getNthNavigationRepeatSoundEffect(next); + } } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 66b9617714a6..03dd10050724 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -68,6 +68,7 @@ import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.Arrays; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -141,6 +142,9 @@ public final class SurfaceControl implements Parcelable { int layerStack); private static native void nativeSetBlurRegions(long transactionObj, long nativeObj, float[][] regions, int length); + private static native void nativeSetStretchEffect(long transactionObj, long nativeObj, + float left, float top, float right, float bottom, float vecX, float vecY, + float maxStretchAmount); private static native boolean nativeClearContentFrameStats(long nativeObject); private static native boolean nativeGetContentFrameStats(long nativeObject, WindowContentFrameStats outStats); @@ -161,25 +165,21 @@ public final class SurfaceControl implements Parcelable { int L, int T, int R, int B); private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken, int width, int height); - private static native DisplayInfo nativeGetDisplayInfo(IBinder displayToken); - private static native DisplayMode[] nativeGetDisplayModes( - IBinder displayToken); + private static native StaticDisplayInfo nativeGetStaticDisplayInfo(IBinder displayToken); + private static native DynamicDisplayInfo nativeGetDynamicDisplayInfo(IBinder displayToken); private static native DisplayedContentSamplingAttributes nativeGetDisplayedContentSamplingAttributes(IBinder displayToken); private static native boolean nativeSetDisplayedContentSamplingEnabled(IBinder displayToken, boolean enable, int componentMask, int maxFrames); private static native DisplayedContentSample nativeGetDisplayedContentSample( IBinder displayToken, long numFrames, long timestamp); - private static native int nativeGetActiveDisplayMode(IBinder displayToken); private static native boolean nativeSetDesiredDisplayModeSpecs(IBinder displayToken, DesiredDisplayModeSpecs desiredDisplayModeSpecs); private static native DesiredDisplayModeSpecs nativeGetDesiredDisplayModeSpecs(IBinder displayToken); - private static native int[] nativeGetDisplayColorModes(IBinder displayToken); private static native DisplayPrimaries nativeGetDisplayNativePrimaries( IBinder displayToken); private static native int[] nativeGetCompositionDataspaces(); - private static native int nativeGetActiveColorMode(IBinder displayToken); private static native boolean nativeSetActiveColorMode(IBinder displayToken, int colorMode); private static native void nativeSetAutoLowLatencyMode(IBinder displayToken, boolean on); @@ -191,11 +191,6 @@ public final class SurfaceControl implements Parcelable { private static native void nativeReparent(long transactionObj, long nativeObject, long newParentNativeObject); - private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken); - - private static native boolean nativeGetAutoLowLatencyModeSupport(IBinder displayToken); - private static native boolean nativeGetGameContentTypeSupport(IBinder displayToken); - private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject, InputWindowHandle handle); @@ -1707,7 +1702,7 @@ public final class SurfaceControl implements Parcelable { * * @hide */ - public static final class DisplayInfo { + public static final class StaticDisplayInfo { public boolean isInternal; public float density; public boolean secure; @@ -1715,7 +1710,7 @@ public final class SurfaceControl implements Parcelable { @Override public String toString() { - return "DisplayInfo{isInternal=" + isInternal + return "StaticDisplayInfo{isInternal=" + isInternal + ", density=" + density + ", secure=" + secure + ", deviceProductInfo=" + deviceProductInfo + "}"; @@ -1725,7 +1720,7 @@ public final class SurfaceControl implements Parcelable { public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - DisplayInfo that = (DisplayInfo) o; + StaticDisplayInfo that = (StaticDisplayInfo) o; return isInternal == that.isInternal && density == that.density && secure == that.secure @@ -1739,6 +1734,54 @@ public final class SurfaceControl implements Parcelable { } /** + * Dynamic information about physical display. + * + * @hide + */ + public static final class DynamicDisplayInfo { + public DisplayMode[] supportedDisplayModes; + public int activeDisplayModeId; + + public int[] supportedColorModes; + public int activeColorMode; + + public Display.HdrCapabilities hdrCapabilities; + + public boolean autoLowLatencyModeSupported; + public boolean gameContentTypeSupported; + + @Override + public String toString() { + return "DynamicDisplayInfo{" + + "supportedDisplayModes=" + Arrays.toString(supportedDisplayModes) + + ", activeDisplayModeId=" + activeDisplayModeId + + ", supportedColorModes=" + Arrays.toString(supportedColorModes) + + ", activeColorMode=" + activeColorMode + + ", hdrCapabilities=" + hdrCapabilities + + ", autoLowLatencyModeSupported=" + autoLowLatencyModeSupported + + ", gameContentTypeSupported" + gameContentTypeSupported + "}"; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DynamicDisplayInfo that = (DynamicDisplayInfo) o; + return Arrays.equals(supportedDisplayModes, that.supportedDisplayModes) + && activeDisplayModeId == that.activeDisplayModeId + && Arrays.equals(supportedColorModes, that.supportedColorModes) + && activeColorMode == that.activeColorMode + && Objects.equals(hdrCapabilities, that.hdrCapabilities); + } + + @Override + public int hashCode() { + return Objects.hash(supportedDisplayModes, activeDisplayModeId, activeDisplayModeId, + activeColorMode, hdrCapabilities); + } + } + + /** * Configuration supported by physical display. * * @hide @@ -1749,6 +1792,7 @@ public final class SurfaceControl implements Parcelable { */ public static final int INVALID_DISPLAY_MODE_ID = -1; + public int id; public int width; public int height; public float xDpi; @@ -1768,7 +1812,8 @@ public final class SurfaceControl implements Parcelable { @Override public String toString() { - return "DisplayConfig{width=" + width + return "DisplayMode{id=" + id + + ", width=" + width + ", height=" + height + ", xDpi=" + xDpi + ", yDpi=" + yDpi @@ -1777,46 +1822,58 @@ public final class SurfaceControl implements Parcelable { + ", presentationDeadlineNanos=" + presentationDeadlineNanos + ", group=" + group + "}"; } - } - /** - * @hide - */ - public static void setDisplayPowerMode(IBinder displayToken, int mode) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DisplayMode that = (DisplayMode) o; + return id == that.id + && width == that.width + && height == that.height + && Float.compare(that.xDpi, xDpi) == 0 + && Float.compare(that.yDpi, yDpi) == 0 + && Float.compare(that.refreshRate, refreshRate) == 0 + && appVsyncOffsetNanos == that.appVsyncOffsetNanos + && presentationDeadlineNanos == that.presentationDeadlineNanos + && group == that.group; + } + + @Override + public int hashCode() { + return Objects.hash(id, width, height, xDpi, yDpi, refreshRate, appVsyncOffsetNanos, + presentationDeadlineNanos, group); } - nativeSetDisplayPowerMode(displayToken, mode); } /** * @hide */ - public static SurfaceControl.DisplayInfo getDisplayInfo(IBinder displayToken) { + public static void setDisplayPowerMode(IBinder displayToken, int mode) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } - return nativeGetDisplayInfo(displayToken); + nativeSetDisplayPowerMode(displayToken, mode); } /** * @hide */ - public static DisplayMode[] getDisplayModes(IBinder displayToken) { + public static StaticDisplayInfo getStaticDisplayInfo(IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } - return nativeGetDisplayModes(displayToken); + return nativeGetStaticDisplayInfo(displayToken); } /** * @hide */ - public static int getActiveDisplayMode(IBinder displayToken) { + public static DynamicDisplayInfo getDynamicDisplayInfo(IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } - return nativeGetActiveDisplayMode(displayToken); + return nativeGetDynamicDisplayInfo(displayToken); } /** @@ -1978,16 +2035,6 @@ public final class SurfaceControl implements Parcelable { } /** - * @hide - */ - public static int[] getDisplayColorModes(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - return nativeGetDisplayColorModes(displayToken); - } - - /** * Color coordinates in CIE1931 XYZ color space * * @hide @@ -2057,16 +2104,6 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - public static int getActiveColorMode(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - return nativeGetActiveColorMode(displayToken); - } - - /** - * @hide - */ public static boolean setActiveColorMode(IBinder displayToken, int colorMode) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); @@ -2169,38 +2206,6 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - public static Display.HdrCapabilities getHdrCapabilities(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - return nativeGetHdrCapabilities(displayToken); - } - - /** - * @hide - */ - public static boolean getAutoLowLatencyModeSupport(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - - return nativeGetAutoLowLatencyModeSupport(displayToken); - } - - /** - * @hide - */ - public static boolean getGameContentTypeSupport(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - - return nativeGetGameContentTypeSupport(displayToken); - } - - /** - * @hide - */ @UnsupportedAppUsage public static IBinder createDisplay(String name, boolean secure) { if (name == null) { @@ -2951,6 +2956,17 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ + public Transaction setStretchEffect(SurfaceControl sc, float left, float top, float right, + float bottom, float vecX, float vecY, float maxStretchAmount) { + checkPreconditions(sc); + nativeSetStretchEffect(mNativeObject, sc.mNativeObject, left, top, right, bottom, + vecX, vecY, maxStretchAmount); + return this; + } + + /** + * @hide + */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O) public Transaction setLayerStack(SurfaceControl sc, int layerStack) { checkPreconditions(sc); @@ -3488,64 +3504,4 @@ public final class SurfaceControl implements Parcelable { public static Transaction getGlobalTransaction() { return sGlobalTransaction; } - - /** - * Wrapper for sending blur data to SurfaceFlinger. - * @hide - */ - public static final class BlurRegion { - public int blurRadius; - public float cornerRadiusTL; - public float cornerRadiusTR; - public float cornerRadiusBL; - public float cornerRadiusBR; - public float alpha = 1; - public boolean visible = true; - public final Rect rect = new Rect(); - - private final float[] mFloatArray = new float[10]; - - public BlurRegion() { - } - - public BlurRegion(BlurRegion other) { - rect.set(other.rect); - blurRadius = other.blurRadius; - alpha = other.alpha; - cornerRadiusTL = other.cornerRadiusTL; - cornerRadiusTR = other.cornerRadiusTR; - cornerRadiusBL = other.cornerRadiusBL; - cornerRadiusBR = other.cornerRadiusBR; - } - - /** - * Serializes this class into a float array that's more JNI friendly. - */ - public float[] toFloatArray() { - mFloatArray[0] = blurRadius; - mFloatArray[1] = alpha; - mFloatArray[2] = rect.left; - mFloatArray[3] = rect.top; - mFloatArray[4] = rect.right; - mFloatArray[5] = rect.bottom; - mFloatArray[6] = cornerRadiusTL; - mFloatArray[7] = cornerRadiusTR; - mFloatArray[8] = cornerRadiusBL; - mFloatArray[9] = cornerRadiusBR; - return mFloatArray; - } - - @Override - public String toString() { - return "BlurRegion{" - + "blurRadius=" + blurRadius - + ", corners={" + cornerRadiusTL - + "," + cornerRadiusTR - + "," + cornerRadiusBL - + "," + cornerRadiusBR - + "}, alpha=" + alpha - + ", rect=" + rect - + "}"; - } - } } diff --git a/core/java/android/view/SurfaceControlFpsListener.java b/core/java/android/view/SurfaceControlFpsListener.java new file mode 100644 index 000000000000..517b0fb8ccd3 --- /dev/null +++ b/core/java/android/view/SurfaceControlFpsListener.java @@ -0,0 +1,93 @@ +/* + * 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.view; + +import android.annotation.NonNull; + +/** + * Listener for sampling the frames per second for a SurfaceControl and its children. + * This should only be used by a system component that needs to listen to a SurfaceControl's + * tree's FPS when it is not actively submitting transactions for that SurfaceControl. + * Otherwise, ASurfaceTransaction_OnComplete callbacks should be used. + * + * @hide + */ +public abstract class SurfaceControlFpsListener { + private long mNativeListener; + + public SurfaceControlFpsListener() { + mNativeListener = nativeCreate(this); + } + + protected void destroy() { + if (mNativeListener == 0) { + return; + } + unregister(); + nativeDestroy(mNativeListener); + mNativeListener = 0; + } + + @Override + protected void finalize() throws Throwable { + try { + destroy(); + } finally { + super.finalize(); + } + } + + /** + * Reports the fps from the registered SurfaceControl + */ + public abstract void onFpsReported(float fps); + + /** + * Registers the sampling listener. + */ + public void register(@NonNull SurfaceControl layer) { + if (mNativeListener == 0) { + return; + } + + nativeRegister(mNativeListener, layer.mNativeObject); + } + + /** + * Unregisters the sampling listener. + */ + public void unregister() { + if (mNativeListener == 0) { + return; + } + nativeUnregister(mNativeListener); + } + + /** + * Dispatch the collected sample. + * + * Called from native code on a binder thread. + */ + private static void dispatchOnFpsReported(SurfaceControlFpsListener listener, float fps) { + listener.onFpsReported(fps); + } + + private static native long nativeCreate(SurfaceControlFpsListener thiz); + private static native void nativeDestroy(long ptr); + private static native void nativeRegister(long ptr, long layerObject); + private static native void nativeUnregister(long ptr); +} diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 18029af6a85e..870fd8cc4f5d 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -218,16 +218,6 @@ public class SurfaceControlViewHost { } /** - * @hide - */ - @TestApi - public void setView(@NonNull View view, @NonNull WindowManager.LayoutParams attrs) { - Objects.requireNonNull(view); - view.setLayoutParams(attrs); - mViewRoot.setView(view, attrs, null); - } - - /** * Set the root view of the SurfaceControlViewHost. This view will render in to * the SurfaceControl, and receive input based on the SurfaceControls positioning on * screen. It will be laid as if it were in a window of the passed in width and height. @@ -240,11 +230,21 @@ public class SurfaceControlViewHost { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); - lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; setView(view, lp); } /** + * @hide + */ + @TestApi + public void setView(@NonNull View view, @NonNull WindowManager.LayoutParams attrs) { + Objects.requireNonNull(view); + attrs.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; + view.setLayoutParams(attrs); + mViewRoot.setView(view, attrs, null); + } + + /** * @return The view passed to setView, or null if none has been passed. */ public @Nullable View getView() { diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 6eba83fee48c..9688c677b900 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -544,6 +544,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall // recreate this Surface, so only release it when we are fully // detached. if (mSurfacePackage != null) { + mTmpTransaction.reparent(mSurfacePackage.getSurfaceControl(), null).apply(); mSurfacePackage.release(); mSurfacePackage = null; } @@ -1444,6 +1445,14 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } @Override + public void applyStretch(long frameNumber, float left, float top, float right, + float bottom, float vecX, float vecY, float maxStretch) { + mRtTransaction.setStretchEffect(mSurfaceControl, left, top, right, bottom, vecX, vecY, + maxStretch); + applyRtTransaction(frameNumber); + } + + @Override public void positionLost(long frameNumber) { if (DEBUG) { Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d", diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 44d4d6b24f58..ebef4646b0d9 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -18,6 +18,12 @@ package android.view; import static android.content.res.Resources.ID_NULL; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED; +import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS; +import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW; +import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN; +import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN; +import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH; +import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE; import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS; import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS; @@ -89,6 +95,7 @@ import android.os.IBinder; import android.os.Message; import android.os.Parcel; import android.os.Parcelable; +import android.os.RemoteCallback; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; @@ -135,6 +142,9 @@ import android.view.autofill.AutofillValue; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.ContentCaptureSession; +import android.view.displayhash.DisplayHash; +import android.view.displayhash.DisplayHashManager; +import android.view.displayhash.DisplayHashResultCallback; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inspector.InspectableProperty; @@ -174,9 +184,10 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Queue; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -1473,7 +1484,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #getScrollCaptureHint() * @see #setScrollCaptureHint(int) - * @hide */ public static final int SCROLL_CAPTURE_HINT_AUTO = 0; @@ -1484,7 +1494,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #getScrollCaptureHint() * @see #setScrollCaptureHint(int) - * @hide */ public static final int SCROLL_CAPTURE_HINT_EXCLUDE = 0x1; @@ -1495,7 +1504,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #getScrollCaptureHint() * @see #setScrollCaptureHint(int) - * @hide */ public static final int SCROLL_CAPTURE_HINT_INCLUDE = 0x2; @@ -1506,7 +1514,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @see #getScrollCaptureHint() * @see #setScrollCaptureHint(int) - * @hide */ public static final int SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS = 0x4; @@ -6985,6 +6992,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #getScrollIndicators() * @attr ref android.R.styleable#View_scrollIndicators */ + @RemotableViewMethod public void setScrollIndicators(@ScrollIndicators int indicators) { setScrollIndicators(indicators, SCROLL_INDICATORS_PFLAG3_MASK >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT); @@ -11870,6 +11878,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setFocusable(int) * @attr ref android.R.styleable#View_focusable */ + @RemotableViewMethod public void setFocusable(boolean focusable) { setFocusable(focusable ? FOCUSABLE : NOT_FOCUSABLE); } @@ -11888,6 +11897,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setFocusableInTouchMode(boolean) * @attr ref android.R.styleable#View_focusable */ + @RemotableViewMethod public void setFocusable(@Focusable int focusable) { if ((focusable & (FOCUSABLE_AUTO | FOCUSABLE)) == 0) { setFlags(0, FOCUSABLE_IN_TOUCH_MODE); @@ -11906,6 +11916,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #setFocusable(boolean) * @attr ref android.R.styleable#View_focusableInTouchMode */ + @RemotableViewMethod public void setFocusableInTouchMode(boolean focusableInTouchMode) { // Focusable in touch mode should always be set before the focusable flag // otherwise, setting the focusable flag will trigger a focusableViewAvailable() @@ -12860,6 +12871,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_focusedByDefault */ + @RemotableViewMethod public void setFocusedByDefault(boolean isFocusedByDefault) { if (isFocusedByDefault == ((mPrivateFlags3 & PFLAG3_FOCUSED_BY_DEFAULT) != 0)) { return; @@ -16839,6 +16851,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_rotation */ + @RemotableViewMethod public void setRotation(float rotation) { if (rotation != getRotation()) { // Double-invalidation is necessary to capture view's old and new areas @@ -16885,6 +16898,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_rotationY */ + @RemotableViewMethod public void setRotationY(float rotationY) { if (rotationY != getRotationY()) { invalidateViewProperty(true, false); @@ -16930,6 +16944,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_rotationX */ + @RemotableViewMethod public void setRotationX(float rotationX) { if (rotationX != getRotationX()) { invalidateViewProperty(true, false); @@ -16967,6 +16982,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_scaleX */ + @RemotableViewMethod public void setScaleX(float scaleX) { if (scaleX != getScaleX()) { scaleX = sanitizeFloatPropertyValue(scaleX, "scaleX"); @@ -17005,6 +17021,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_scaleY */ + @RemotableViewMethod public void setScaleY(float scaleY) { if (scaleY != getScaleY()) { scaleY = sanitizeFloatPropertyValue(scaleY, "scaleY"); @@ -17050,6 +17067,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_transformPivotX */ + @RemotableViewMethod public void setPivotX(float pivotX) { if (!mRenderNode.isPivotExplicitlySet() || pivotX != getPivotX()) { invalidateViewProperty(true, false); @@ -17092,6 +17110,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_transformPivotY */ + @RemotableViewMethod public void setPivotY(float pivotY) { if (!mRenderNode.isPivotExplicitlySet() || pivotY != getPivotY()) { invalidateViewProperty(true, false); @@ -17232,6 +17251,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_alpha */ + @RemotableViewMethod public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) { ensureTransformationInfo(); if (mTransformationInfo.mAlpha != alpha) { @@ -17721,6 +17741,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_elevation */ + @RemotableViewMethod public void setElevation(float elevation) { if (elevation != getElevation()) { elevation = sanitizeFloatPropertyValue(elevation, "elevation"); @@ -17755,6 +17776,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_translationX */ + @RemotableViewMethod public void setTranslationX(float translationX) { if (translationX != getTranslationX()) { invalidateViewProperty(true, false); @@ -17790,6 +17812,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_translationY */ + @RemotableViewMethod public void setTranslationY(float translationY) { if (translationY != getTranslationY()) { invalidateViewProperty(true, false); @@ -17817,6 +17840,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @attr ref android.R.styleable#View_translationZ */ + @RemotableViewMethod public void setTranslationZ(float translationZ) { if (translationZ != getTranslationZ()) { translationZ = sanitizeFloatPropertyValue(translationZ, "translationZ"); @@ -23978,6 +24002,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #getBackgroundTintList() * @see Drawable#setTintList(ColorStateList) */ + @RemotableViewMethod public void setBackgroundTintList(@Nullable ColorStateList tint) { if (mBackgroundTint == null) { mBackgroundTint = new TintInfo(); @@ -24237,6 +24262,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see #getForegroundTintList() * @see Drawable#setTintList(ColorStateList) */ + @RemotableViewMethod public void setForegroundTintList(@Nullable ColorStateList tint) { if (mForegroundInfo == null) { mForegroundInfo = new ForegroundInfo(); @@ -30023,8 +30049,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Returns the current scroll capture hint for this view. * * @return the current scroll capture hint - * - * @hide */ @ScrollCaptureHint public int getScrollCaptureHint() { @@ -30037,8 +30061,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * scroll capture targets. * * @param hint the scrollCaptureHint flags value to set - * - * @hide */ public void setScrollCaptureHint(@ScrollCaptureHint int hint) { mPrivateFlags4 &= ~PFLAG4_SCROLL_CAPTURE_HINT_MASK; @@ -30058,10 +30080,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * setting a custom callback to help ensure it is selected as the target. * * @param callback the new callback to assign - * - * @hide */ - public void setScrollCaptureCallback(@Nullable ScrollCaptureCallback callback) { + public final void setScrollCaptureCallback(@Nullable ScrollCaptureCallback callback) { getListenerInfo().mScrollCaptureCallback = callback; } @@ -30080,29 +30100,54 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Dispatch a scroll capture search request down the view hierarchy. + * + * @param localVisibleRect the visible area of this ViewGroup in local coordinates, according to + * the parent + * @param windowOffset the offset of this view within the window + * @param targets accepts potential scroll capture targets; {@link Consumer#accept + * results.accept} may be called zero or more times on the calling + * thread before onScrollCaptureSearch returns + */ + public void dispatchScrollCaptureSearch( + @NonNull Rect localVisibleRect, @NonNull Point windowOffset, + @NonNull Consumer<ScrollCaptureTarget> targets) { + onScrollCaptureSearch(localVisibleRect, windowOffset, targets); + } + + /** * Called when scroll capture is requested, to search for appropriate content to scroll. If * applicable, this view adds itself to the provided list for consideration, subject to the * flags set by {@link #setScrollCaptureHint}. * * @param localVisibleRect the local visible rect of this view * @param windowOffset the offset of localVisibleRect within the window - * @param targets a queue which collects potential targets - * + * @param targets accepts potential scroll capture targets; {@link Consumer#accept + * results.accept} may be called zero or more times on the calling + * thread before onScrollCaptureSearch returns * @throws IllegalStateException if this view is not attached to a window - * @hide */ - public void dispatchScrollCaptureSearch(@NonNull Rect localVisibleRect, - @NonNull Point windowOffset, @NonNull Queue<ScrollCaptureTarget> targets) { + public void onScrollCaptureSearch(@NonNull Rect localVisibleRect, + @NonNull Point windowOffset, @NonNull Consumer<ScrollCaptureTarget> targets) { int hint = getScrollCaptureHint(); if ((hint & SCROLL_CAPTURE_HINT_EXCLUDE) != 0) { return; } + boolean rectIsVisible = true; + + // Apply clipBounds if present. + if (mClipBounds != null) { + rectIsVisible = localVisibleRect.intersect(mClipBounds); + } + if (!rectIsVisible) { + return; + } // Get a callback provided by the framework, library or application. ScrollCaptureCallback callback = (mListenerInfo == null) ? null : mListenerInfo.mScrollCaptureCallback; - // Try internal support for standard scrolling containers. + // Try framework support for standard scrolling containers. if (callback == null) { callback = createScrollCaptureCallbackInternal(localVisibleRect, windowOffset); } @@ -30112,7 +30157,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Add to the list for consideration Point offset = new Point(windowOffset.x, windowOffset.y); Rect rect = new Rect(localVisibleRect); - targets.add(new ScrollCaptureTarget(this, rect, offset, callback)); + targets.accept(new ScrollCaptureTarget(this, rect, offset, callback)); } } @@ -30707,4 +30752,71 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void onTranslationComplete(@NonNull TranslationRequest request) { // no-op } + + /** + * Called to generate a {@link DisplayHash} for this view. + * + * @param hashAlgorithm The hash algorithm to use when hashing the display. Must be one of + * the values returned from + * {@link DisplayHashManager#getSupportedHashAlgorithms()} + * @param bounds The bounds for the content within the View to generate the hash for. If + * bounds are null, the entire View's bounds will be used. If empty, it will + * invoke the callback + * {@link DisplayHashResultCallback#onDisplayHashError} with error + * {@link DisplayHashResultCallback#DISPLAY_HASH_ERROR_INVALID_BOUNDS} + * @param executor The executor that the callback should be invoked on. + * @param callback The callback to handle the results of generating the display hash + */ + @Nullable + public void generateDisplayHash(@NonNull String hashAlgorithm, + @Nullable Rect bounds, @NonNull Executor executor, + @NonNull DisplayHashResultCallback callback) { + IWindowSession session = getWindowSession(); + if (session == null) { + callback.onDisplayHashError(DISPLAY_HASH_ERROR_MISSING_WINDOW); + return; + } + IWindow window = getWindow(); + if (window == null) { + callback.onDisplayHashError(DISPLAY_HASH_ERROR_MISSING_WINDOW); + return; + } + + Rect visibleBounds = new Rect(); + getGlobalVisibleRect(visibleBounds); + + if (bounds != null && bounds.isEmpty()) { + callback.onDisplayHashError(DISPLAY_HASH_ERROR_INVALID_BOUNDS); + return; + } + + if (bounds != null) { + bounds.offset(visibleBounds.left, visibleBounds.top); + visibleBounds.intersectUnchecked(bounds); + } + + if (visibleBounds.isEmpty()) { + callback.onDisplayHashError(DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN); + return; + } + + RemoteCallback remoteCallback = new RemoteCallback(result -> + executor.execute(() -> { + DisplayHash displayHash = result.getParcelable(EXTRA_DISPLAY_HASH); + int errorCode = result.getInt(EXTRA_DISPLAY_HASH_ERROR_CODE, + DISPLAY_HASH_ERROR_UNKNOWN); + if (displayHash != null) { + callback.onDisplayHashResult(displayHash); + } else { + callback.onDisplayHashError(errorCode); + } + })); + + try { + session.generateDisplayHash(window, visibleBounds, hashAlgorithm, remoteCallback); + } catch (RemoteException e) { + Log.e(VIEW_LOG_TAG, "Failed to call generateDisplayHash"); + callback.onDisplayHashError(DISPLAY_HASH_ERROR_UNKNOWN); + } + } } diff --git a/core/java/android/view/ViewFrameInfo.java b/core/java/android/view/ViewFrameInfo.java index 890d071f8090..d4aaa611f800 100644 --- a/core/java/android/view/ViewFrameInfo.java +++ b/core/java/android/view/ViewFrameInfo.java @@ -58,8 +58,8 @@ public class ViewFrameInfo { public void populateFrameInfo(FrameInfo frameInfo) { frameInfo.frameInfo[FrameInfo.FLAGS] |= flags; frameInfo.frameInfo[FrameInfo.DRAW_START] = drawStart; - frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT] = oldestInputEventTime; - frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT] = newestInputEventTime; + // TODO(b/169866723): Use InputEventAssigner + frameInfo.frameInfo[FrameInfo.INPUT_EVENT_ID] = newestInputEventTime; } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 37bea5821e42..38a59373554c 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -77,7 +77,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Queue; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -7463,92 +7463,73 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** - * Offsets the given rectangle in parent's local coordinates into child's coordinate space - * and clips the result to the child View's bounds, padding and clipRect if appropriate. If the - * resulting rectangle is not empty, the request is forwarded to the child. - * <p> - * Note: This method does not account for any static View transformations which may be - * applied to the child view. - * - * @param child the child to dispatch to - * @param localVisibleRect the visible (clipped) area of this ViewGroup, in local coordinates - * @param windowOffset the offset of localVisibleRect within the window - * @param targets a queue to collect located targets - */ - private void dispatchTransformedScrollCaptureSearch(View child, Rect localVisibleRect, - Point windowOffset, Queue<ScrollCaptureTarget> targets) { - - // copy local visible rect for modification and dispatch - final Rect childVisibleRect = getTempRect(); - childVisibleRect.set(localVisibleRect); - - // transform to child coords - final Point childWindowOffset = getTempPoint(); - childWindowOffset.set(windowOffset.x, windowOffset.y); - - final int dx = child.mLeft - mScrollX; - final int dy = child.mTop - mScrollY; - - childVisibleRect.offset(-dx, -dy); - childWindowOffset.offset(dx, dy); - - boolean rectIsVisible = true; - final int width = mRight - mLeft; - final int height = mBottom - mTop; - - // Clip to child bounds - if (getClipChildren()) { - rectIsVisible = childVisibleRect.intersect(0, 0, child.getWidth(), child.getHeight()); - } - - // Clip to child padding. - if (rectIsVisible && (child instanceof ViewGroup) - && ((ViewGroup) child).getClipToPadding()) { - rectIsVisible = childVisibleRect.intersect( - child.mPaddingLeft, child.mPaddingTop, - child.getWidth() - child.mPaddingRight, - child.getHeight() - child.mPaddingBottom); - } - // Clip to child clipBounds. - if (rectIsVisible && child.mClipBounds != null) { - rectIsVisible = childVisibleRect.intersect(child.mClipBounds); - } - if (rectIsVisible) { - child.dispatchScrollCaptureSearch(childVisibleRect, childWindowOffset, targets); - } - } - - /** * Handle the scroll capture search request by checking this view if applicable, then to each * child view. * * @param localVisibleRect the visible area of this ViewGroup in local coordinates, according to * the parent * @param windowOffset the offset of this view within the window - * @param targets the collected list of scroll capture targets - * - * @hide + * @param targets accepts potential scroll capture targets; {@link Consumer#accept + * results.accept} may be called zero or more times on the calling + * thread before onScrollCaptureSearch returns */ @Override public void dispatchScrollCaptureSearch( @NonNull Rect localVisibleRect, @NonNull Point windowOffset, - @NonNull Queue<ScrollCaptureTarget> targets) { + @NonNull Consumer<ScrollCaptureTarget> targets) { + + // copy local visible rect for modification and dispatch + final Rect rect = getTempRect(); + rect.set(localVisibleRect); + + if (getClipToPadding()) { + rect.inset(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom); + } // Dispatch to self first. super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets); - // Then dispatch to children, if not excluding descendants. - if ((getScrollCaptureHint() & SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS) == 0) { - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - // Only visible views can be captured. - if (child.getVisibility() != View.VISIBLE) { - continue; - } - // Transform to child coords and dispatch - dispatchTransformedScrollCaptureSearch(child, localVisibleRect, windowOffset, - targets); + // Skip children if descendants excluded. + if ((getScrollCaptureHint() & SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS) != 0) { + return; + } + + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + // Only visible views can be captured. + if (child.getVisibility() != View.VISIBLE) { + continue; + } + // Offset the given rectangle (in parent's local coordinates) into child's coordinate + // space and clip the result to the child View's bounds, padding and clipRect as needed. + // If the resulting rectangle is not empty, the request is forwarded to the child. + + // copy local visible rect for modification and dispatch + final Rect childVisibleRect = getTempRect(); + childVisibleRect.set(localVisibleRect); + + // transform to child coords + final Point childWindowOffset = getTempPoint(); + childWindowOffset.set(windowOffset.x, windowOffset.y); + + final int dx = child.mLeft - mScrollX; + final int dy = child.mTop - mScrollY; + + childVisibleRect.offset(-dx, -dy); + childWindowOffset.offset(dx, dy); + + boolean rectIsVisible = true; + + // Clip to child bounds + if (getClipChildren()) { + rectIsVisible = childVisibleRect.intersect(0, 0, child.getWidth(), + child.getHeight()); + } + + // Clip to child padding. + if (rectIsVisible) { + child.dispatchScrollCaptureSearch(childVisibleRect, childWindowOffset, targets); } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f1f6786aa43e..f8e65bd0d056 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -141,6 +141,7 @@ import android.util.AndroidRuntimeException; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; +import android.util.IndentingPrintWriter; import android.util.Log; import android.util.LongArray; import android.util.MergedConfiguration; @@ -202,6 +203,7 @@ import com.android.internal.view.SurfaceCallbackHelper; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashSet; @@ -274,6 +276,11 @@ public final class ViewRootImpl implements ViewParent, */ private static final int CONTENT_CAPTURE_ENABLED_FALSE = 2; + /** + * Maximum time to wait for {@link View#dispatchScrollCaptureSearch} to complete. + */ + private static final int SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS = 2500; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); @@ -323,6 +330,8 @@ public final class ViewRootImpl implements ViewParent, private boolean mForceDisableBLAST; private boolean mEnableTripleBuffering; + private boolean mFastScrollSoundEffectsEnabled; + /** * Signals that compatibility booleans have been initialized according to * target SDK versions. @@ -666,8 +675,6 @@ public final class ViewRootImpl implements ViewParent, private final InsetsController mInsetsController; private final ImeFocusController mImeFocusController; - private ScrollCaptureConnection mScrollCaptureConnection; - private boolean mIsSurfaceOpaque; private final BackgroundBlurDrawable.Aggregator mBlurRegionAggregator = @@ -681,12 +688,6 @@ public final class ViewRootImpl implements ViewParent, return mImeFocusController; } - /** @return The current {@link ScrollCaptureConnection} for this instance, if any is active. */ - @Nullable - public ScrollCaptureConnection getScrollCaptureConnection() { - return mScrollCaptureConnection; - } - private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker(); private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; @@ -728,6 +729,8 @@ public final class ViewRootImpl implements ViewParent, private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks; + private long mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS; + /** * Increment this value when the surface has been replaced. */ @@ -813,6 +816,10 @@ public final class ViewRootImpl implements ViewParent, loadSystemProperties(); mImeFocusController = new ImeFocusController(this); + AudioManager audioManager = mContext.getSystemService(AudioManager.class); + mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled(); + + mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS; } public static void addFirstDrawHandler(Runnable callback) { @@ -1677,7 +1684,8 @@ public final class ViewRootImpl implements ViewParent, // See comment for View.sForceLayoutWhenInsetsChanged if (View.sForceLayoutWhenInsetsChanged && mView != null - && mWindowAttributes.softInputMode == SOFT_INPUT_ADJUST_RESIZE) { + && (mWindowAttributes.softInputMode & SOFT_INPUT_MASK_ADJUST) + == SOFT_INPUT_ADJUST_RESIZE) { forceLayout(mView); } @@ -3961,11 +3969,12 @@ public final class ViewRootImpl implements ViewParent, } private void addFrameCallbackIfNeeded() { - boolean nextDrawUseBlastSync = mNextDrawUseBlastSync; - boolean hasBlur = mBlurRegionAggregator.hasRegions(); - boolean reportNextDraw = mReportNextDraw; + final boolean nextDrawUseBlastSync = mNextDrawUseBlastSync; + final boolean reportNextDraw = mReportNextDraw; + final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates(); + final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions(); - if (!nextDrawUseBlastSync && !reportNextDraw && !hasBlur) { + if (!nextDrawUseBlastSync && !reportNextDraw && !needsCallbackForBlur) { return; } @@ -3973,18 +3982,22 @@ public final class ViewRootImpl implements ViewParent, Log.d(mTag, "Creating frameDrawingCallback" + " nextDrawUseBlastSync=" + nextDrawUseBlastSync + " reportNextDraw=" + reportNextDraw - + " hasBlur=" + hasBlur); + + " hasBlurUpdates=" + hasBlurUpdates); } - // The callback will run on a worker thread pool from the render thread. + final BackgroundBlurDrawable.BlurRegion[] blurRegionsForFrame = + needsCallbackForBlur ? mBlurRegionAggregator.getBlurRegionsCopyForRT() : null; + + // The callback will run on the render thread. HardwareRenderer.FrameDrawingCallback frameDrawingCallback = frame -> { if (DEBUG_BLAST) { Log.d(mTag, "Received frameDrawingCallback frameNum=" + frame + "." + " Creating transactionCompleteCallback=" + nextDrawUseBlastSync); } - if (hasBlur) { - mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frame); + if (needsCallbackForBlur) { + mBlurRegionAggregator + .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates); } if (mBlastBufferQueue == null) { @@ -6080,8 +6093,10 @@ public final class ViewRootImpl implements ViewParent, v, mTempRect); } if (v.requestFocus(direction, mTempRect)) { - playSoundEffect(SoundEffectConstants - .getContantForFocusDirection(direction)); + boolean isFastScrolling = event.getRepeatCount() > 0; + playSoundEffect( + SoundEffectConstants.getConstantForFocusDirection(direction, + isFastScrolling)); return true; } } @@ -7742,20 +7757,31 @@ public final class ViewRootImpl implements ViewParent, try { final AudioManager audioManager = getAudioManager(); + if (mFastScrollSoundEffectsEnabled + && SoundEffectConstants.isNavigationRepeat(effectId)) { + audioManager.playSoundEffect( + SoundEffectConstants.nextNavigationRepeatSoundEffectId()); + return; + } + switch (effectId) { case SoundEffectConstants.CLICK: audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); return; case SoundEffectConstants.NAVIGATION_DOWN: + case SoundEffectConstants.NAVIGATION_REPEAT_DOWN: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN); return; case SoundEffectConstants.NAVIGATION_LEFT: + case SoundEffectConstants.NAVIGATION_REPEAT_LEFT: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT); return; case SoundEffectConstants.NAVIGATION_RIGHT: + case SoundEffectConstants.NAVIGATION_REPEAT_RIGHT: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT); return; case SoundEffectConstants.NAVIGATION_UP: + case SoundEffectConstants.NAVIGATION_REPEAT_UP: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP); return; default: @@ -9205,9 +9231,9 @@ public final class ViewRootImpl implements ViewParent, * Collect and include any ScrollCaptureCallback instances registered with the window. * * @see #addScrollCaptureCallback(ScrollCaptureCallback) - * @param targets the search queue for targets + * @param results an object to collect the results of the search */ - private void collectRootScrollCaptureTargets(Queue<ScrollCaptureTarget> targets) { + private void collectRootScrollCaptureTargets(ScrollCaptureSearchResults results) { if (mRootScrollCaptureCallbacks == null) { return; } @@ -9215,26 +9241,45 @@ public final class ViewRootImpl implements ViewParent, // Add to the list for consideration Point offset = new Point(mView.getLeft(), mView.getTop()); Rect rect = new Rect(0, 0, mView.getWidth(), mView.getHeight()); - targets.add(new ScrollCaptureTarget(mView, rect, offset, cb)); + results.addTarget(new ScrollCaptureTarget(mView, rect, offset, cb)); } } /** - * Handles an inbound request for scroll capture from the system. If a client is not already - * active, a search will be dispatched through the view tree to locate scrolling content. + * Update the timeout for scroll capture requests. Only affects this view root. + * The default value is {@link #SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS}. + * + * @param timeMillis the new timeout in milliseconds + */ + public void setScrollCaptureRequestTimeout(int timeMillis) { + mScrollCaptureRequestTimeout = timeMillis; + } + + /** + * Get the current timeout for scroll capture requests. + * + * @return the timeout in milliseconds + */ + public long getScrollCaptureRequestTimeout() { + return mScrollCaptureRequestTimeout; + } + + /** + * Handles an inbound request for scroll capture from the system. A search will be + * dispatched through the view tree to locate scrolling content. * <p> - * Either {@link IScrollCaptureCallbacks#onConnected(IScrollCaptureConnection, Rect, - * Point)} or {@link IScrollCaptureCallbacks#onUnavailable()} will be returned - * depending on the results of the search. + * A call to {@link IScrollCaptureCallbacks#onScrollCaptureResponse(ScrollCaptureResponse)} + * will follow. * * @param callbacks to receive responses - * @see ScrollCaptureTargetResolver + * @see ScrollCaptureTargetSelector */ public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) { - LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); + ScrollCaptureSearchResults results = + new ScrollCaptureSearchResults(mContext.getMainExecutor()); // Window (root) level callbacks - collectRootScrollCaptureTargets(targetList); + collectRootScrollCaptureTargets(results); // Search through View-tree View rootView = getView(); @@ -9242,58 +9287,70 @@ public final class ViewRootImpl implements ViewParent, Point point = new Point(); Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight()); getChildVisibleRect(rootView, rect, point); - rootView.dispatchScrollCaptureSearch(rect, point, targetList); + rootView.dispatchScrollCaptureSearch(rect, point, results::addTarget); } - - // No-op path. Scroll capture not offered for this window. - if (targetList.isEmpty()) { - dispatchScrollCaptureSearchResult(callbacks, null); - return; + Runnable onComplete = () -> dispatchScrollCaptureSearchResult(callbacks, results); + results.setOnCompleteListener(onComplete); + if (!results.isComplete()) { + mHandler.postDelayed(results::finish, getScrollCaptureRequestTimeout()); } - - // Request scrollBounds from each of the targets. - // Continues with the consumer once all responses are consumed, or the timeout expires. - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetList); - resolver.start(mHandler, 1000, - (selected) -> dispatchScrollCaptureSearchResult(callbacks, selected)); } /** Called by {@link #handleScrollCaptureRequest} when a result is returned */ private void dispatchScrollCaptureSearchResult( @NonNull IScrollCaptureCallbacks callbacks, - @Nullable ScrollCaptureTarget selectedTarget) { + @NonNull ScrollCaptureSearchResults results) { + + ScrollCaptureTarget selectedTarget = results.getTopResult(); + + ScrollCaptureResponse.Builder response = new ScrollCaptureResponse.Builder(); + response.setWindowTitle(getTitle().toString()); + + StringWriter writer = new StringWriter(); + IndentingPrintWriter pw = new IndentingPrintWriter(writer); + results.dump(pw); + pw.flush(); + response.addMessage(writer.toString()); - // If timeout or no eligible targets found. if (selectedTarget == null) { + response.setDescription("No scrollable targets found in window"); try { - if (DEBUG_SCROLL_CAPTURE) { - Log.d(TAG, "scrollCaptureSearch returned no targets available."); - } - callbacks.onUnavailable(); + callbacks.onScrollCaptureResponse(response.build()); } catch (RemoteException e) { - if (DEBUG_SCROLL_CAPTURE) { - Log.w(TAG, "Failed to send scroll capture search result.", e); - } + Log.e(TAG, "Failed to send scroll capture search result", e); } return; } - // Create a client instance and return it to the caller - mScrollCaptureConnection = new ScrollCaptureConnection(selectedTarget, callbacks); + response.setDescription("Connected"); + + // Compute area covered by scrolling content within window + Rect boundsInWindow = new Rect(); + View containingView = selectedTarget.getContainingView(); + containingView.getLocationInWindow(mAttachInfo.mTmpLocation); + boundsInWindow.set(selectedTarget.getScrollBounds()); + boundsInWindow.offset(mAttachInfo.mTmpLocation[0], mAttachInfo.mTmpLocation[1]); + response.setBoundsInWindow(boundsInWindow); + + // Compute the area on screen covered by the window + Rect boundsOnScreen = new Rect(); + mView.getLocationOnScreen(mAttachInfo.mTmpLocation); + boundsOnScreen.set(0, 0, mView.getWidth(), mView.getHeight()); + boundsOnScreen.offset(mAttachInfo.mTmpLocation[0], mAttachInfo.mTmpLocation[1]); + response.setWindowBounds(boundsOnScreen); + + // Create a connection and return it to the caller + ScrollCaptureConnection connection = new ScrollCaptureConnection( + mView.getContext().getMainExecutor(), selectedTarget, callbacks); + response.setConnection(connection); + try { - if (DEBUG_SCROLL_CAPTURE) { - Log.d(TAG, "scrollCaptureSearch returning client: " + getScrollCaptureConnection()); - } - callbacks.onConnected( - mScrollCaptureConnection, - selectedTarget.getScrollBounds(), - selectedTarget.getPositionInWindow()); + callbacks.onScrollCaptureResponse(response.build()); } catch (RemoteException e) { if (DEBUG_SCROLL_CAPTURE) { - Log.w(TAG, "Failed to send scroll capture search result.", e); + Log.w(TAG, "Failed to send scroll capture search response.", e); } - mScrollCaptureConnection.disconnect(); - mScrollCaptureConnection = null; + connection.close(); } } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 4ecdd78f5a42..221b3346df58 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -2621,7 +2621,6 @@ public abstract class Window { * callback with the root view of the window. * * @param callback the callback to add - * @hide */ public void registerScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) { } @@ -2630,7 +2629,6 @@ public abstract class Window { * Unregisters a {@link ScrollCaptureCallback} previously registered with this window. * * @param callback the callback to remove - * @hide */ public void unregisterScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) { } diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 39d3c01dd409..7cc2db1c0dab 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -23,8 +23,8 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.IBinder; +import android.os.RemoteCallback; import android.os.RemoteException; -import android.service.screenshot.ScreenshotHash; import android.util.Log; import android.util.MergedConfiguration; import android.window.ClientWindowFrames; @@ -487,8 +487,7 @@ public class WindowlessWindowManager implements IWindowSession { } @Override - public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow, - String hashAlgorithm) { - return null; + public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm, + RemoteCallback callback) { } } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index decbf8c0c59e..4f0c568989de 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -1975,7 +1975,10 @@ public final class AutofillManager { if (client == null) { return false; } - + if (mService == null) { + Log.w(TAG, "Autofill service is null!"); + return false; + } if (mServiceClient == null) { mServiceClient = new AutofillManagerClient(this); try { diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 10f6c610d5d3..46e0306cefc8 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -23,10 +23,13 @@ import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UiThread; +import android.annotation.UserIdInt; +import android.app.Service; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.Context; @@ -755,6 +758,74 @@ public final class ContentCaptureManager { } } + /** + * Resets the temporary content capture service implementation to the default component. + * + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_CONTENT_CAPTURE) + public static void resetTemporaryService(@UserIdInt int userId) { + final IContentCaptureManager service = getService(); + if (service == null) { + Log.e(TAG, "IContentCaptureManager is null"); + } + try { + service.resetTemporaryService(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Temporarily sets the content capture service implementation. + * + * @param userId user Id to set the temporary service on. + * @param serviceName name of the new component + * @param duration how long the change will be valid (the service will be automatically reset + * to the default component after this timeout expires). + * + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_CONTENT_CAPTURE) + public static void setTemporaryService( + @UserIdInt int userId, @NonNull String serviceName, int duration) { + final IContentCaptureManager service = getService(); + if (service == null) { + Log.e(TAG, "IContentCaptureManager is null"); + } + try { + service.setTemporaryService(userId, serviceName, duration); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets whether the default content capture service should be used. + * + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MANAGE_CONTENT_CAPTURE) + public static void setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) { + final IContentCaptureManager service = getService(); + if (service == null) { + Log.e(TAG, "IContentCaptureManager is null"); + } + try { + service.setDefaultServiceEnabled(userId, enabled); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + private static IContentCaptureManager getService() { + return IContentCaptureManager.Stub.asInterface(ServiceManager.getService( + Service.CONTENT_CAPTURE_MANAGER_SERVICE)); + } + private interface MyRunnable { void run(@NonNull SyncResultReceiver receiver) throws RemoteException; } diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl index 01ead4601e59..ef8295c30fdf 100644 --- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl +++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl @@ -85,4 +85,19 @@ oneway interface IContentCaptureManager { * Returns a list with the ContentCaptureConditions for the package (or null if not defined). */ void getContentCaptureConditions(String packageName, in IResultReceiver result); + + /** + * Resets the temporary service implementation to the default component. + */ + void resetTemporaryService(int userId); + + /** + * Temporarily sets the service implementation. + */ + void setTemporaryService(int userId, in String serviceName, int duration); + + /** + * Sets whether the default service should be used. + */ + void setDefaultServiceEnabled(int userId, boolean enabled); } diff --git a/core/java/android/view/displayhash/DisplayHash.aidl b/core/java/android/view/displayhash/DisplayHash.aidl new file mode 100644 index 000000000000..cabf57573fa0 --- /dev/null +++ b/core/java/android/view/displayhash/DisplayHash.aidl @@ -0,0 +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 android.view.displayhash; + +parcelable DisplayHash; diff --git a/core/java/android/view/displayhash/DisplayHash.java b/core/java/android/view/displayhash/DisplayHash.java new file mode 100644 index 000000000000..41484865b98e --- /dev/null +++ b/core/java/android/view/displayhash/DisplayHash.java @@ -0,0 +1,226 @@ +/* + * 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.view.displayhash; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.AnnotationValidations; + +/** + * The DisplayHash used to validate information about what was present on screen. + */ +public final class DisplayHash implements Parcelable { + /** + * The timestamp when the hash was generated. + */ + private final long mTimeMillis; + + /** + * The bounds of the requested area to generate the hash. This is in window space passed in + * by the client. + */ + @NonNull + private final Rect mBoundsInWindow; + + /** + * The selected hash algorithm that generated the image hash. + */ + @NonNull + private final String mHashAlgorithm; + + /** + * The image hash generated when creating the DisplayHash. + */ + @NonNull + private final byte[] mImageHash; + + /** + * The hmac generated by the system and used to verify whether this token was generated by + * the system. + */ + @NonNull + private final byte[] mHmac; + + /** + * Creates a new DisplayHash. + * + * @param timeMillis The timestamp when the hash was generated. + * @param boundsInWindow The bounds of the requested area to generate the hash. This is + * in window space passed in by the client. + * @param hashAlgorithm The selected hash algorithm that generated the image hash. + * @param imageHash The image hash generated when creating the DisplayHash. + * @param hmac The hmac generated by the system and used to verify whether this + * token was generated by + * the system. This should only be accessed by a system process. + * @hide + */ + @SystemApi + public DisplayHash(long timeMillis, @NonNull Rect boundsInWindow, + @NonNull String hashAlgorithm, @NonNull byte[] imageHash, @NonNull byte[] hmac) { + mTimeMillis = timeMillis; + mBoundsInWindow = boundsInWindow; + AnnotationValidations.validate(NonNull.class, null, mBoundsInWindow); + mHashAlgorithm = hashAlgorithm; + AnnotationValidations.validate(NonNull.class, null, mHashAlgorithm); + mImageHash = imageHash; + AnnotationValidations.validate(NonNull.class, null, mImageHash); + mHmac = hmac; + AnnotationValidations.validate(NonNull.class, null, mHmac); + } + + /** + * The timestamp when the hash was generated. + * + * @hide + */ + @SystemApi + public long getTimeMillis() { + return mTimeMillis; + } + + /** + * The bounds of the requested area to to generate the hash. This is in window space passed in + * by the client. + * + * @hide + */ + @SystemApi + @NonNull + public Rect getBoundsInWindow() { + return mBoundsInWindow; + } + + /** + * The selected hash algorithm that generated the image hash. + * + * @hide + */ + @SystemApi + @NonNull + public String getHashAlgorithm() { + return mHashAlgorithm; + } + + /** + * The image hash generated when creating the DisplayHash. + * + * @hide + */ + @SystemApi + @NonNull + public byte[] getImageHash() { + return mImageHash; + } + + /** + * The hmac generated by the system and used to verify whether this token was generated by + * the system. This should only be accessed by a system process. + * + * @hide + */ + @SystemApi + @NonNull + public byte[] getHmac() { + return mHmac; + } + + /** @hide **/ + @Override + public String toString() { + return "DisplayHash { " + + "timeMillis = " + mTimeMillis + ", " + + "boundsInWindow = " + mBoundsInWindow + ", " + + "hashAlgorithm = " + mHashAlgorithm + ", " + + "imageHash = " + byteArrayToString(mImageHash) + ", " + + "hmac = " + byteArrayToString(mHmac) + + " }"; + } + + private String byteArrayToString(byte[] byteArray) { + if (byteArray == null) { + return "null"; + } + int iMax = byteArray.length - 1; + if (iMax == -1) { + return "[]"; + } + + StringBuilder b = new StringBuilder(); + b.append('['); + for (int i = 0; ; i++) { + String formatted = String.format("%02X", byteArray[i] & 0xFF); + b.append(formatted); + if (i == iMax) { + return b.append(']').toString(); + } + b.append(", "); + } + } + + /** @hide **/ + @SystemApi + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mTimeMillis); + dest.writeTypedObject(mBoundsInWindow, flags); + dest.writeString(mHashAlgorithm); + dest.writeByteArray(mImageHash); + dest.writeByteArray(mHmac); + } + + /** @hide **/ + @SystemApi + @Override + public int describeContents() { + return 0; + } + + private DisplayHash(@NonNull Parcel in) { + mTimeMillis = in.readLong(); + Rect boundsInWindow = in.readTypedObject(Rect.CREATOR); + String hashAlgorithm = in.readString(); + byte[] imageHash = in.createByteArray(); + byte[] hmac = in.createByteArray(); + + mBoundsInWindow = boundsInWindow; + AnnotationValidations.validate(NonNull.class, null, mBoundsInWindow); + mHashAlgorithm = hashAlgorithm; + AnnotationValidations.validate(NonNull.class, null, mHashAlgorithm); + mImageHash = imageHash; + AnnotationValidations.validate(NonNull.class, null, mImageHash); + mHmac = hmac; + AnnotationValidations.validate(NonNull.class, null, mHmac); + } + + @NonNull + public static final Parcelable.Creator<DisplayHash> CREATOR = + new Parcelable.Creator<DisplayHash>() { + @Override + public DisplayHash[] newArray(int size) { + return new DisplayHash[size]; + } + + @Override + public DisplayHash createFromParcel(@NonNull Parcel in) { + return new DisplayHash(in); + } + }; +} diff --git a/core/java/android/view/displayhash/DisplayHashManager.java b/core/java/android/view/displayhash/DisplayHashManager.java new file mode 100644 index 000000000000..6b0c1a6d7633 --- /dev/null +++ b/core/java/android/view/displayhash/DisplayHashManager.java @@ -0,0 +1,97 @@ +/* + * 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.view.displayhash; + + +import static android.content.Context.DISPLAY_HASH_SERVICE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemService; +import android.os.RemoteException; +import android.util.ArraySet; +import android.util.Log; +import android.view.WindowManagerGlobal; + +import com.android.internal.annotations.GuardedBy; + +import java.util.Collections; +import java.util.Set; + +/** + * Utility class for DisplayHash requests. + */ +@SystemService(DISPLAY_HASH_SERVICE) +public final class DisplayHashManager { + private static final String TAG = "DisplayHashManager"; + + private final Object mSupportedHashingAlgorithmLock = new Object(); + + @GuardedBy("mSupportedAlgorithmLock") + private static Set<String> sSupportedHashAlgorithms; + + /** + * @hide + */ + public DisplayHashManager() { + } + + /** + * Get a Set of DisplayHash algorithms that the device supports. + * + * @return a String Set of supported hashing algorithms. The String value of one + * algorithm should be used when requesting to generate the DisplayHash. + */ + @NonNull + public Set<String> getSupportedHashAlgorithms() { + synchronized (mSupportedHashingAlgorithmLock) { + if (sSupportedHashAlgorithms != null) { + return sSupportedHashAlgorithms; + } + + try { + String[] supportedAlgorithms = WindowManagerGlobal.getWindowManagerService() + .getSupportedDisplayHashAlgorithms(); + if (supportedAlgorithms == null) { + return Collections.emptySet(); + } + sSupportedHashAlgorithms = new ArraySet<>(supportedAlgorithms); + return sSupportedHashAlgorithms; + } catch (RemoteException e) { + Log.e(TAG, "Failed to send request getSupportedHashingAlgorithms", e); + return Collections.emptySet(); + } + } + } + + /** + * Call to verify that the DisplayHash passed in was generated by the system. + * + * @param displayHash The hash to verify that it was generated by the system. + * @return a {@link VerifiedDisplayHash} if the hash was generated by the system or null + * if the hash cannot be verified. + */ + @Nullable + public VerifiedDisplayHash verifyDisplayHash(@NonNull DisplayHash displayHash) { + try { + return WindowManagerGlobal.getWindowManagerService().verifyDisplayHash(displayHash); + } catch (RemoteException e) { + Log.e(TAG, "Failed to send request verifyImpressionToken", e); + return null; + } + } +} diff --git a/core/java/android/view/displayhash/DisplayHashResultCallback.java b/core/java/android/view/displayhash/DisplayHashResultCallback.java new file mode 100644 index 000000000000..15b29adafddd --- /dev/null +++ b/core/java/android/view/displayhash/DisplayHashResultCallback.java @@ -0,0 +1,98 @@ +/* + * 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.view.displayhash; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.graphics.Rect; +import android.view.View; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; + +/** + * Use when calling {@link View#generateDisplayHash(String, Rect, Executor, + * DisplayHashResultCallback)}. + * + * The callback will only invoke either {@link #onDisplayHashResult} when the system successfully + * generated the {@link DisplayHash} or {@link #onDisplayHashError(int)} when it failed. + */ +public interface DisplayHashResultCallback { + /** + * @hide + */ + String EXTRA_DISPLAY_HASH = "DISPLAY_HASH"; + + /** + * @hide + */ + String EXTRA_DISPLAY_HASH_ERROR_CODE = "DISPLAY_HASH_ERROR_CODE"; + + /** + * An unknown error occurred. + */ + int DISPLAY_HASH_ERROR_UNKNOWN = -1; + + /** + * The bounds used when requesting the hash hash were invalid or empty. + */ + int DISPLAY_HASH_ERROR_INVALID_BOUNDS = -2; + + /** + * The window for the view that requested the hash is no longer around. This can happen if the + * window is getting torn down. + */ + int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3; + + /** + * The view that requested the hash is not visible on screen. This could either mean + * that the view bounds are offscreen, window bounds are offscreen, view is not visible, or + * window is not visible. + */ + int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4; + + /** @hide */ + @IntDef(prefix = {"DISPLAY_HASH_ERROR_"}, value = { + DISPLAY_HASH_ERROR_UNKNOWN, + DISPLAY_HASH_ERROR_INVALID_BOUNDS, + DISPLAY_HASH_ERROR_MISSING_WINDOW, + DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN + }) + @Retention(RetentionPolicy.SOURCE) + @interface DisplayHashErrorCode { + } + + /** + * Callback invoked when calling + * {@link android.view.View#generateDisplayHash(String, Rect, Executor, + * DisplayHashResultCallback)} + * + * @param displayHash The DisplayHash generated. If the hash cannot be generated, + * {@link #onDisplayHashError(int)} will be called instead + */ + void onDisplayHashResult(@NonNull DisplayHash displayHash); + + /** + * Callback invoked when + * {@link android.view.View#generateDisplayHash(String, Rect, Executor, + * DisplayHashResultCallback)} results in an error and cannot generate a display hash. + * + * @param errorCode One of the values in {@link DisplayHashErrorCode} + */ + void onDisplayHashError(@DisplayHashErrorCode int errorCode); +} diff --git a/core/java/android/view/displayhash/OWNERS b/core/java/android/view/displayhash/OWNERS new file mode 100644 index 000000000000..0862c05e0ee4 --- /dev/null +++ b/core/java/android/view/displayhash/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/wm/OWNERS diff --git a/core/java/android/view/displayhash/VerifiedDisplayHash.aidl b/core/java/android/view/displayhash/VerifiedDisplayHash.aidl new file mode 100644 index 000000000000..9e7ebefa7ca1 --- /dev/null +++ b/core/java/android/view/displayhash/VerifiedDisplayHash.aidl @@ -0,0 +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 android.view.displayhash; + +parcelable VerifiedDisplayHash; diff --git a/core/java/android/view/displayhash/VerifiedDisplayHash.java b/core/java/android/view/displayhash/VerifiedDisplayHash.java new file mode 100644 index 000000000000..16a428e73005 --- /dev/null +++ b/core/java/android/view/displayhash/VerifiedDisplayHash.java @@ -0,0 +1,242 @@ +/* + * 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.view.displayhash; + +import android.annotation.NonNull; +import android.graphics.Rect; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +/** + * The verified display hash used to validate information about what was present on screen. + */ +@DataClass(genToString = true, genAidl = true) +public final class VerifiedDisplayHash implements Parcelable { + /** + * The timestamp when the hash was generated. + */ + private final long mTimeMillis; + + /** + * The bounds of the requested area to generate the hash. This is in window space passed in + * by the client. + */ + @NonNull + private final Rect mBoundsInWindow; + + /** + * The selected hash algorithm that generated the image hash. + */ + @NonNull + private final String mHashAlgorithm; + + /** + * The image hash generated when creating the DisplayHash. + */ + @NonNull + private final byte[] mImageHash; + + private String imageHashToString() { + return byteArrayToString(mImageHash); + } + + private String byteArrayToString(byte[] byteArray) { + if (byteArray == null) { + return "null"; + } + int iMax = byteArray.length - 1; + if (iMax == -1) { + return "[]"; + } + + StringBuilder b = new StringBuilder(); + b.append('['); + for (int i = 0; ; i++) { + String formatted = String.format("%02X", byteArray[i] & 0xFF); + b.append(formatted); + if (i == iMax) { + return b.append(']').toString(); + } + b.append(", "); + } + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/displayhash/VerifiedDisplayHash.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new VerifiedDisplayHash. + * + * @param timeMillis + * The timestamp when the hash was generated. + * @param boundsInWindow + * The bounds of the requested area to generate the hash. This is in window space passed in + * by the client. + * @param hashAlgorithm + * The selected hash algorithm that generated the image hash. + * @param imageHash + * The image hash generated when creating the DisplayHash. + */ + @DataClass.Generated.Member + public VerifiedDisplayHash( + long timeMillis, + @NonNull Rect boundsInWindow, + @NonNull String hashAlgorithm, + @NonNull byte[] imageHash) { + this.mTimeMillis = timeMillis; + this.mBoundsInWindow = boundsInWindow; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mBoundsInWindow); + this.mHashAlgorithm = hashAlgorithm; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHashAlgorithm); + this.mImageHash = imageHash; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mImageHash); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The timestamp when the hash was generated. + */ + @DataClass.Generated.Member + public long getTimeMillis() { + return mTimeMillis; + } + + /** + * The bounds of the requested area to generate the hash. This is in window space passed in + * by the client. + */ + @DataClass.Generated.Member + public @NonNull Rect getBoundsInWindow() { + return mBoundsInWindow; + } + + /** + * The selected hash algorithm that generated the image hash. + */ + @DataClass.Generated.Member + public @NonNull String getHashAlgorithm() { + return mHashAlgorithm; + } + + /** + * The image hash generated when creating the DisplayHash. + */ + @DataClass.Generated.Member + public @NonNull byte[] getImageHash() { + return mImageHash; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "VerifiedDisplayHash { " + + "timeMillis = " + mTimeMillis + ", " + + "boundsInWindow = " + mBoundsInWindow + ", " + + "hashAlgorithm = " + mHashAlgorithm + ", " + + "imageHash = " + imageHashToString() + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeLong(mTimeMillis); + dest.writeTypedObject(mBoundsInWindow, flags); + dest.writeString(mHashAlgorithm); + dest.writeByteArray(mImageHash); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ VerifiedDisplayHash(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + long timeMillis = in.readLong(); + Rect boundsInWindow = (Rect) in.readTypedObject(Rect.CREATOR); + String hashAlgorithm = in.readString(); + byte[] imageHash = in.createByteArray(); + + this.mTimeMillis = timeMillis; + this.mBoundsInWindow = boundsInWindow; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mBoundsInWindow); + this.mHashAlgorithm = hashAlgorithm; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHashAlgorithm); + this.mImageHash = imageHash; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mImageHash); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<VerifiedDisplayHash> CREATOR + = new Parcelable.Creator<VerifiedDisplayHash>() { + @Override + public VerifiedDisplayHash[] newArray(int size) { + return new VerifiedDisplayHash[size]; + } + + @Override + public VerifiedDisplayHash createFromParcel(@NonNull android.os.Parcel in) { + return new VerifiedDisplayHash(in); + } + }; + + @DataClass.Generated( + time = 1613168749684L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/displayhash/VerifiedDisplayHash.java", + inputSignatures = "private final long mTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate java.lang.String imageHashToString()\nprivate java.lang.String byteArrayToString(byte[])\nclass VerifiedDisplayHash extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index e740cc2b1f7a..39c09b46f2e1 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -85,7 +85,7 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.SurroundingText; import android.view.inspector.InspectableProperty; import android.view.inspector.InspectableProperty.EnumEntry; -import android.widget.RemoteViews.OnClickHandler; +import android.widget.RemoteViews.InteractionHandler; import com.android.internal.R; @@ -715,7 +715,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ @NonNull @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769408) - private EdgeEffect mEdgeGlowTop = new EdgeEffect(mContext); + private EdgeEffect mEdgeGlowTop; /** * Tracks the state of the bottom edge glow. @@ -725,7 +725,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ @NonNull @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768444) - private EdgeEffect mEdgeGlowBottom = new EdgeEffect(mContext); + private EdgeEffect mEdgeGlowBottom; /** * An estimate of how many pixels are between the top of the list and @@ -847,6 +847,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public AbsListView(Context context) { super(context); + mEdgeGlowBottom = new EdgeEffect(context); + mEdgeGlowTop = new EdgeEffect(context); initAbsListView(); mOwnerThread = Thread.currentThread(); @@ -867,6 +869,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public AbsListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + mEdgeGlowBottom = new EdgeEffect(context, attrs); + mEdgeGlowTop = new EdgeEffect(context, attrs); initAbsListView(); mOwnerThread = Thread.currentThread(); @@ -3584,6 +3588,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY; int lastYCorrection = 0; + // First allow releasing existing overscroll effect: + incrementalDeltaY = releaseGlow(incrementalDeltaY, x); + if (mTouchMode == TOUCH_MODE_SCROLL) { if (PROFILE_SCROLLING) { if (!mScrollProfilingStarted) { @@ -3666,14 +3673,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mTouchMode = TOUCH_MODE_OVERSCROLL; } if (incrementalDeltaY > 0) { - mEdgeGlowTop.onPull((float) -overscroll / getHeight(), + mEdgeGlowTop.onPullDistance((float) -overscroll / getHeight(), (float) x / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } invalidateTopGlow(); } else if (incrementalDeltaY < 0) { - mEdgeGlowBottom.onPull((float) overscroll / getHeight(), + mEdgeGlowBottom.onPullDistance((float) overscroll / getHeight(), 1.f - (float) x / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); @@ -3713,14 +3720,15 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits())) { if (rawDeltaY > 0) { - mEdgeGlowTop.onPull((float) overScrollDistance / getHeight(), + mEdgeGlowTop.onPullDistance((float) overScrollDistance / getHeight(), (float) x / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } invalidateTopGlow(); } else if (rawDeltaY < 0) { - mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight(), + mEdgeGlowBottom.onPullDistance( + (float) -overScrollDistance / getHeight(), 1.f - (float) x / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); @@ -3757,6 +3765,44 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + /** + * If the edge glow is currently active, this consumes part or all of deltaY + * on the edge glow. + * + * @param deltaY The pointer motion, in pixels, in the vertical direction, positive + * for moving down and negative for moving up. + * @param x The horizontal position of the pointer. + * @return The remainder of <code>deltaY</code> that has not been consumed by the + * edge glow. + */ + private int releaseGlow(int deltaY, int x) { + // First allow releasing existing overscroll effect: + float consumed = 0; + if (mEdgeGlowTop.getDistance() != 0) { + consumed = mEdgeGlowTop.onPullDistance((float) deltaY / getHeight(), + (float) x / getWidth()); + if (consumed != 0f) { + invalidateTopGlow(); + } + } else if (mEdgeGlowBottom.getDistance() != 0) { + consumed = -mEdgeGlowBottom.onPullDistance((float) -deltaY / getHeight(), + 1f - (float) x / getWidth()); + if (consumed != 0f) { + invalidateBottomGlow(); + } + } + int pixelsConsumed = Math.round(consumed * getHeight()); + return deltaY - pixelsConsumed; + } + + /** + * @return <code>true</code> if either the top or bottom edge glow is currently active or + * <code>false</code> if it has no value to release. + */ + private boolean isGlowActive() { + return mEdgeGlowBottom.getDistance() != 0 || mEdgeGlowTop.getDistance() != 0; + } + private void invalidateTopGlow() { if (!shouldDisplayEdgeEffects()) { return; @@ -3926,7 +3972,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (mTouchMode == TOUCH_MODE_OVERFLING) { // Stopped the fling. It is a scroll. - mFlingRunnable.endFling(); + if (mFlingRunnable != null) { + mFlingRunnable.endFling(); + } if (mPositionScroller != null) { mPositionScroller.stop(); } @@ -3936,6 +3984,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mLastY = mMotionY; mMotionCorrection = 0; mDirection = 0; + stopEdgeGlowRecede(ev.getX()); } else { final int x = (int) ev.getX(); final int y = (int) ev.getY(); @@ -3948,7 +3997,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mTouchMode = TOUCH_MODE_SCROLL; mMotionCorrection = 0; motionPosition = findMotionRow(y); - mFlingRunnable.flywheelTouch(); + if (mFlingRunnable != null) { + mFlingRunnable.flywheelTouch(); + } + stopEdgeGlowRecede(x); } else if ((motionPosition >= 0) && getAdapter().isEnabled(motionPosition)) { // User clicked on an actual view (and was not stopping a // fling). It might be a click or a scroll. Assume it is a @@ -3984,6 +4036,15 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + private void stopEdgeGlowRecede(float x) { + if (mEdgeGlowTop.getDistance() != 0) { + mEdgeGlowTop.onPullDistance(0, x / getWidth()); + } + if (mEdgeGlowBottom.getDistance() != 0) { + mEdgeGlowBottom.onPullDistance(0, x / getWidth()); + } + } + private void onTouchMove(MotionEvent ev, MotionEvent vtev) { if (mHasPerformedLongPress) { // Consume all move events following a successful long press. @@ -4489,73 +4550,76 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } switch (actionMasked) { - case MotionEvent.ACTION_DOWN: { - int touchMode = mTouchMode; - if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) { - mMotionCorrection = 0; - return true; - } + case MotionEvent.ACTION_DOWN: { + int touchMode = mTouchMode; + if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) { + mMotionCorrection = 0; + return true; + } - final int x = (int) ev.getX(); - final int y = (int) ev.getY(); - mActivePointerId = ev.getPointerId(0); + final int x = (int) ev.getX(); + final int y = (int) ev.getY(); + mActivePointerId = ev.getPointerId(0); - int motionPosition = findMotionRow(y); - if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) { - // User clicked on an actual view (and was not stopping a fling). - // Remember where the motion event started - v = getChildAt(motionPosition - mFirstPosition); - mMotionViewOriginalTop = v.getTop(); - mMotionX = x; - mMotionY = y; - mMotionPosition = motionPosition; - mTouchMode = TOUCH_MODE_DOWN; - clearScrollingCache(); - } - mLastY = Integer.MIN_VALUE; - initOrResetVelocityTracker(); - mVelocityTracker.addMovement(ev); - mNestedYOffset = 0; - startNestedScroll(SCROLL_AXIS_VERTICAL); - if (touchMode == TOUCH_MODE_FLING) { - return true; - } - break; - } - - case MotionEvent.ACTION_MOVE: { - switch (mTouchMode) { - case TOUCH_MODE_DOWN: - int pointerIndex = ev.findPointerIndex(mActivePointerId); - if (pointerIndex == -1) { - pointerIndex = 0; - mActivePointerId = ev.getPointerId(pointerIndex); + int motionPosition = findMotionRow(y); + if (isGlowActive()) { + // Pressed during edge effect, so this is considered the same as a fling catch. + mTouchMode = TOUCH_MODE_FLING; + } else if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) { + // User clicked on an actual view (and was not stopping a fling). + // Remember where the motion event started + v = getChildAt(motionPosition - mFirstPosition); + mMotionViewOriginalTop = v.getTop(); + mMotionX = x; + mMotionY = y; + mMotionPosition = motionPosition; + mTouchMode = TOUCH_MODE_DOWN; + clearScrollingCache(); } - final int y = (int) ev.getY(pointerIndex); - initVelocityTrackerIfNotExists(); + mLastY = Integer.MIN_VALUE; + initOrResetVelocityTracker(); mVelocityTracker.addMovement(ev); - if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) { + mNestedYOffset = 0; + startNestedScroll(SCROLL_AXIS_VERTICAL); + if (touchMode == TOUCH_MODE_FLING) { return true; } break; } - break; - } - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: { - mTouchMode = TOUCH_MODE_REST; - mActivePointerId = INVALID_POINTER; - recycleVelocityTracker(); - reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); - stopNestedScroll(); - break; - } + case MotionEvent.ACTION_MOVE: { + switch (mTouchMode) { + case TOUCH_MODE_DOWN: + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == -1) { + pointerIndex = 0; + mActivePointerId = ev.getPointerId(pointerIndex); + } + final int y = (int) ev.getY(pointerIndex); + initVelocityTrackerIfNotExists(); + mVelocityTracker.addMovement(ev); + if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) { + return true; + } + break; + } + break; + } - case MotionEvent.ACTION_POINTER_UP: { - onSecondaryPointerUp(ev); - break; - } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: { + mTouchMode = TOUCH_MODE_REST; + mActivePointerId = INVALID_POINTER; + recycleVelocityTracker(); + reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); + stopNestedScroll(); + break; + } + + case MotionEvent.ACTION_POINTER_UP: { + onSecondaryPointerUp(ev); + break; + } } return false; @@ -6419,11 +6483,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * * @hide */ - public void setRemoteViewsOnClickHandler(OnClickHandler handler) { + public void setRemoteViewsInteractionHandler(InteractionHandler handler) { // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing // service handling the specified intent. if (mRemoteAdapter != null) { - mRemoteAdapter.setRemoteViewsOnClickHandler(handler); + mRemoteAdapter.setRemoteViewsInteractionHandler(handler); } } @@ -6548,6 +6612,27 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** + * Returns the {@link EdgeEffect#getType()} for the edge effects. + * @return the {@link EdgeEffect#getType()} for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + @EdgeEffect.EdgeEffectType + public int getEdgeEffectType() { + return mEdgeGlowTop.getType(); + } + + /** + * Sets the {@link EdgeEffect#setType(int)} for the edge effects. + * @param type The edge effect type to use for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) { + mEdgeGlowTop.setType(type); + mEdgeGlowBottom.setType(type); + invalidate(); + } + + /** * Sets the recycler listener to be notified whenever a View is set aside in * the recycler for later reuse. This listener can be used to free resources * associated to the View. diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java index d93b635cbcf5..d8f7f4c5f326 100644 --- a/core/java/android/widget/AdapterViewAnimator.java +++ b/core/java/android/widget/AdapterViewAnimator.java @@ -29,7 +29,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.widget.RemoteViews.OnClickHandler; +import android.widget.RemoteViews.InteractionHandler; import java.util.ArrayList; import java.util.HashMap; @@ -1016,11 +1016,11 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> * * @hide */ - public void setRemoteViewsOnClickHandler(OnClickHandler handler) { + public void setRemoteViewsOnClickHandler(InteractionHandler handler) { // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing // service handling the specified intent. if (mRemoteViewsAdapter != null) { - mRemoteViewsAdapter.setRemoteViewsOnClickHandler(handler); + mRemoteViewsAdapter.setRemoteViewsInteractionHandler(handler); } } diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java index 93b2d8ae3c9a..34fe51e82e8f 100644 --- a/core/java/android/widget/AnalogClock.java +++ b/core/java/android/widget/AnalogClock.java @@ -23,8 +23,10 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; @@ -68,12 +70,16 @@ public class AnalogClock extends View { @UnsupportedAppUsage private Drawable mHourHand; + private final TintInfo mHourHandTintInfo = new TintInfo(); @UnsupportedAppUsage private Drawable mMinuteHand; + private final TintInfo mMinuteHandTintInfo = new TintInfo(); @Nullable private Drawable mSecondHand; + private final TintInfo mSecondHandTintInfo = new TintInfo(); @UnsupportedAppUsage private Drawable mDial; + private final TintInfo mDialTintInfo = new TintInfo(); private int mDialWidth; private int mDialHeight; @@ -111,18 +117,86 @@ public class AnalogClock extends View { mDial = context.getDrawable(com.android.internal.R.drawable.clock_dial); } + ColorStateList dialTintList = a.getColorStateList( + com.android.internal.R.styleable.AnalogClock_dialTint); + if (dialTintList != null) { + mDialTintInfo.mTintList = dialTintList; + mDialTintInfo.mHasTintList = true; + } + BlendMode dialTintMode = Drawable.parseBlendMode( + a.getInt(com.android.internal.R.styleable.AnalogClock_dialTintMode, -1), + null); + if (dialTintMode != null) { + mDialTintInfo.mTintBlendMode = dialTintMode; + mDialTintInfo.mHasTintBlendMode = true; + } + if (mDialTintInfo.mHasTintList || mDialTintInfo.mHasTintBlendMode) { + mDial = mDialTintInfo.apply(mDial); + } + mHourHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_hour); if (mHourHand == null) { mHourHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_hour); } + ColorStateList hourHandTintList = a.getColorStateList( + com.android.internal.R.styleable.AnalogClock_hand_hourTint); + if (hourHandTintList != null) { + mHourHandTintInfo.mTintList = hourHandTintList; + mHourHandTintInfo.mHasTintList = true; + } + BlendMode hourHandTintMode = Drawable.parseBlendMode( + a.getInt(com.android.internal.R.styleable.AnalogClock_hand_hourTintMode, -1), + null); + if (hourHandTintMode != null) { + mHourHandTintInfo.mTintBlendMode = hourHandTintMode; + mHourHandTintInfo.mHasTintBlendMode = true; + } + if (mHourHandTintInfo.mHasTintList || mHourHandTintInfo.mHasTintBlendMode) { + mHourHand = mHourHandTintInfo.apply(mHourHand); + } + mMinuteHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_minute); if (mMinuteHand == null) { mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute); } + ColorStateList minuteHandTintList = a.getColorStateList( + com.android.internal.R.styleable.AnalogClock_hand_minuteTint); + if (minuteHandTintList != null) { + mMinuteHandTintInfo.mTintList = minuteHandTintList; + mMinuteHandTintInfo.mHasTintList = true; + } + BlendMode minuteHandTintMode = Drawable.parseBlendMode( + a.getInt(com.android.internal.R.styleable.AnalogClock_hand_minuteTintMode, -1), + null); + if (minuteHandTintMode != null) { + mMinuteHandTintInfo.mTintBlendMode = minuteHandTintMode; + mMinuteHandTintInfo.mHasTintBlendMode = true; + } + if (mMinuteHandTintInfo.mHasTintList || mMinuteHandTintInfo.mHasTintBlendMode) { + mMinuteHand = mMinuteHandTintInfo.apply(mMinuteHand); + } + mSecondHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_second); + ColorStateList secondHandTintList = a.getColorStateList( + com.android.internal.R.styleable.AnalogClock_hand_secondTint); + if (secondHandTintList != null) { + mSecondHandTintInfo.mTintList = secondHandTintList; + mSecondHandTintInfo.mHasTintList = true; + } + BlendMode secondHandTintMode = Drawable.parseBlendMode( + a.getInt(com.android.internal.R.styleable.AnalogClock_hand_secondTintMode, -1), + null); + if (secondHandTintMode != null) { + mSecondHandTintInfo.mTintBlendMode = secondHandTintMode; + mSecondHandTintInfo.mHasTintBlendMode = true; + } + if (mSecondHandTintInfo.mHasTintList || mSecondHandTintInfo.mHasTintBlendMode) { + mSecondHand = mSecondHandTintInfo.apply(mSecondHand); + } + mTimeZone = toZoneId(a.getString(com.android.internal.R.styleable.AnalogClock_timeZone)); createClock(); @@ -141,6 +215,68 @@ public class AnalogClock extends View { invalidate(); } + /** + * Applies a tint to the dial drawable. + * <p> + * Subsequent calls to {@link #setDial(Icon)} will + * automatically mutate the drawable and apply the specified tint and tint + * mode using {@link Drawable#setTintList(ColorStateList)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#AnalogClock_dialTint + * @see #getDialTintList() + * @see Drawable#setTintList(ColorStateList) + */ + @RemotableViewMethod + public void setDialTintList(@Nullable ColorStateList tint) { + mDialTintInfo.mTintList = tint; + mDialTintInfo.mHasTintList = true; + + mDial = mDialTintInfo.apply(mDial); + } + + /** + * @return the tint applied to the dial drawable + * @attr ref android.R.styleable#AnalogClock_dialTint + * @see #setDialTintList(ColorStateList) + */ + @InspectableProperty(attributeId = com.android.internal.R.styleable.AnalogClock_dialTint) + @Nullable + public ColorStateList getDialTintList() { + return mDialTintInfo.mTintList; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setDialTintList(ColorStateList)}} to the dial drawable. + * The default mode is {@link BlendMode#SRC_IN}. + * + * @param blendMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * @attr ref android.R.styleable#AnalogClock_dialTintMode + * @see #getDialTintBlendMode() + * @see Drawable#setTintBlendMode(BlendMode) + */ + @RemotableViewMethod + public void setDialTintBlendMode(@Nullable BlendMode blendMode) { + mDialTintInfo.mTintBlendMode = blendMode; + mDialTintInfo.mHasTintBlendMode = true; + + mDial = mDialTintInfo.apply(mDial); + } + + /** + * @return the blending mode used to apply the tint to the dial drawable + * @attr ref android.R.styleable#AnalogClock_dialTintMode + * @see #setDialTintBlendMode(BlendMode) + */ + @InspectableProperty(attributeId = com.android.internal.R.styleable.AnalogClock_dialTintMode) + @Nullable + public BlendMode getDialTintBlendMode() { + return mDialTintInfo.mTintBlendMode; + } + /** Sets the hour hand of the clock to the specified Icon. */ @RemotableViewMethod public void setHourHand(@NonNull Icon icon) { @@ -150,6 +286,71 @@ public class AnalogClock extends View { invalidate(); } + /** + * Applies a tint to the hour hand drawable. + * <p> + * Subsequent calls to {@link #setHourHand(Icon)} will + * automatically mutate the drawable and apply the specified tint and tint + * mode using {@link Drawable#setTintList(ColorStateList)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#AnalogClock_hand_hourTint + * @see #getHourHandTintList() + * @see Drawable#setTintList(ColorStateList) + */ + @RemotableViewMethod + public void setHourHandTintList(@Nullable ColorStateList tint) { + mHourHandTintInfo.mTintList = tint; + mHourHandTintInfo.mHasTintList = true; + + mHourHand = mHourHandTintInfo.apply(mHourHand); + } + + /** + * @return the tint applied to the hour hand drawable + * @attr ref android.R.styleable#AnalogClock_hand_hourTint + * @see #setHourHandTintList(ColorStateList) + */ + @InspectableProperty( + attributeId = com.android.internal.R.styleable.AnalogClock_hand_hourTint + ) + @Nullable + public ColorStateList getHourHandTintList() { + return mHourHandTintInfo.mTintList; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setHourHandTintList(ColorStateList)}} to the hour hand drawable. + * The default mode is {@link BlendMode#SRC_IN}. + * + * @param blendMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * @attr ref android.R.styleable#AnalogClock_hand_hourTintMode + * @see #getHourHandTintBlendMode() + * @see Drawable#setTintBlendMode(BlendMode) + */ + @RemotableViewMethod + public void setHourHandTintBlendMode(@Nullable BlendMode blendMode) { + mHourHandTintInfo.mTintBlendMode = blendMode; + mHourHandTintInfo.mHasTintBlendMode = true; + + mHourHand = mHourHandTintInfo.apply(mHourHand); + } + + /** + * @return the blending mode used to apply the tint to the hour hand drawable + * @attr ref android.R.styleable#AnalogClock_hand_hourTintMode + * @see #setHourHandTintBlendMode(BlendMode) + */ + @InspectableProperty( + attributeId = com.android.internal.R.styleable.AnalogClock_hand_hourTintMode) + @Nullable + public BlendMode getHourHandTintBlendMode() { + return mHourHandTintInfo.mTintBlendMode; + } + /** Sets the minute hand of the clock to the specified Icon. */ @RemotableViewMethod public void setMinuteHand(@NonNull Icon icon) { @@ -160,6 +361,71 @@ public class AnalogClock extends View { } /** + * Applies a tint to the minute hand drawable. + * <p> + * Subsequent calls to {@link #setMinuteHand(Icon)} will + * automatically mutate the drawable and apply the specified tint and tint + * mode using {@link Drawable#setTintList(ColorStateList)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#AnalogClock_hand_minuteTint + * @see #getMinuteHandTintList() + * @see Drawable#setTintList(ColorStateList) + */ + @RemotableViewMethod + public void setMinuteHandTintList(@Nullable ColorStateList tint) { + mMinuteHandTintInfo.mTintList = tint; + mMinuteHandTintInfo.mHasTintList = true; + + mMinuteHand = mMinuteHandTintInfo.apply(mMinuteHand); + } + + /** + * @return the tint applied to the minute hand drawable + * @attr ref android.R.styleable#AnalogClock_hand_minuteTint + * @see #setMinuteHandTintList(ColorStateList) + */ + @InspectableProperty( + attributeId = com.android.internal.R.styleable.AnalogClock_hand_minuteTint + ) + @Nullable + public ColorStateList getMinuteHandTintList() { + return mMinuteHandTintInfo.mTintList; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setMinuteHandTintList(ColorStateList)}} to the minute hand drawable. + * The default mode is {@link BlendMode#SRC_IN}. + * + * @param blendMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * @attr ref android.R.styleable#AnalogClock_hand_minuteTintMode + * @see #getMinuteHandTintBlendMode() + * @see Drawable#setTintBlendMode(BlendMode) + */ + @RemotableViewMethod + public void setMinuteHandTintBlendMode(@Nullable BlendMode blendMode) { + mMinuteHandTintInfo.mTintBlendMode = blendMode; + mMinuteHandTintInfo.mHasTintBlendMode = true; + + mMinuteHand = mMinuteHandTintInfo.apply(mMinuteHand); + } + + /** + * @return the blending mode used to apply the tint to the minute hand drawable + * @attr ref android.R.styleable#AnalogClock_hand_minuteTintMode + * @see #setMinuteHandTintBlendMode(BlendMode) + */ + @InspectableProperty( + attributeId = com.android.internal.R.styleable.AnalogClock_hand_minuteTintMode) + @Nullable + public BlendMode getMinuteHandTintBlendMode() { + return mMinuteHandTintInfo.mTintBlendMode; + } + + /** * Sets the second hand of the clock to the specified Icon, or hides the second hand if it is * null. */ @@ -173,6 +439,71 @@ public class AnalogClock extends View { } /** + * Applies a tint to the second hand drawable. + * <p> + * Subsequent calls to {@link #setSecondHand(Icon)} will + * automatically mutate the drawable and apply the specified tint and tint + * mode using {@link Drawable#setTintList(ColorStateList)}. + * + * @param tint the tint to apply, may be {@code null} to clear tint + * + * @attr ref android.R.styleable#AnalogClock_hand_secondTint + * @see #getSecondHandTintList() + * @see Drawable#setTintList(ColorStateList) + */ + @RemotableViewMethod + public void setSecondHandTintList(@Nullable ColorStateList tint) { + mSecondHandTintInfo.mTintList = tint; + mSecondHandTintInfo.mHasTintList = true; + + mSecondHand = mSecondHandTintInfo.apply(mSecondHand); + } + + /** + * @return the tint applied to the second hand drawable + * @attr ref android.R.styleable#AnalogClock_hand_secondTint + * @see #setSecondHandTintList(ColorStateList) + */ + @InspectableProperty( + attributeId = com.android.internal.R.styleable.AnalogClock_hand_secondTint + ) + @Nullable + public ColorStateList getSecondHandTintList() { + return mSecondHandTintInfo.mTintList; + } + + /** + * Specifies the blending mode used to apply the tint specified by + * {@link #setSecondHandTintList(ColorStateList)}} to the second hand drawable. + * The default mode is {@link BlendMode#SRC_IN}. + * + * @param blendMode the blending mode used to apply the tint, may be + * {@code null} to clear tint + * @attr ref android.R.styleable#AnalogClock_hand_secondTintMode + * @see #getSecondHandTintBlendMode() + * @see Drawable#setTintBlendMode(BlendMode) + */ + @RemotableViewMethod + public void setSecondHandTintBlendMode(@Nullable BlendMode blendMode) { + mSecondHandTintInfo.mTintBlendMode = blendMode; + mSecondHandTintInfo.mHasTintBlendMode = true; + + mSecondHand = mSecondHandTintInfo.apply(mSecondHand); + } + + /** + * @return the blending mode used to apply the tint to the second hand drawable + * @attr ref android.R.styleable#AnalogClock_hand_secondTintMode + * @see #setSecondHandTintBlendMode(BlendMode) + */ + @InspectableProperty( + attributeId = com.android.internal.R.styleable.AnalogClock_hand_secondTintMode) + @Nullable + public BlendMode getSecondHandTintBlendMode() { + return mSecondHandTintInfo.mTintBlendMode; + } + + /** * Indicates which time zone is currently used by this view. * * @return The ID of the current time zone or null if the default time zone, @@ -462,4 +793,36 @@ public class AnalogClock extends View { return null; } } + + private final class TintInfo { + boolean mHasTintList; + @Nullable ColorStateList mTintList; + boolean mHasTintBlendMode; + @Nullable BlendMode mTintBlendMode; + + /** + * Returns a mutated copy of {@code drawable} with tinting applied, or null if it's null. + */ + @Nullable + Drawable apply(@Nullable Drawable drawable) { + if (drawable == null) return null; + + Drawable newDrawable = drawable.mutate(); + + if (mHasTintList) { + newDrawable.setTintList(mTintList); + } + + if (mHasTintBlendMode) { + newDrawable.setTintBlendMode(mTintBlendMode); + } + + // All drawables should have the same state as the View itself. + if (drawable.isStateful()) { + newDrawable.setState(getDrawableState()); + } + + return newDrawable; + } + } } diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 6dedd12a2730..23915e06335a 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -303,6 +303,27 @@ public class HorizontalScrollView extends FrameLayout { } /** + * Returns the {@link EdgeEffect#getType()} for the edge effects. + * @return the {@link EdgeEffect#getType()} for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + @EdgeEffect.EdgeEffectType + public int getEdgeEffectType() { + return mEdgeGlowLeft.getType(); + } + + /** + * Sets the {@link EdgeEffect#setType(int)} for the edge effects. + * @param type The edge effect type to use for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) { + mEdgeGlowRight.setType(type); + mEdgeGlowLeft.setType(type); + invalidate(); + } + + /** * @return The maximum amount this scroll view will scroll in response to * an arrow event. */ diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 0a08ccd34f02..8aa557bab4e3 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -648,6 +648,7 @@ public class ImageView extends View { * @see #getImageTintList() * @see Drawable#setTintList(ColorStateList) */ + @android.view.RemotableViewMethod public void setImageTintList(@Nullable ColorStateList tint) { mDrawableTintList = tint; mHasDrawableTint = true; diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index a44808eb3120..1b76ebf7c8c6 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -1308,6 +1308,7 @@ public class ProgressBar extends View { * @see #getSecondaryProgressTintList() * @see Drawable#setTintList(ColorStateList) */ + @RemotableViewMethod public void setSecondaryProgressTintList(@Nullable ColorStateList tint) { if (mProgressTintInfo == null) { mProgressTintInfo = new ProgressTintInfo(); @@ -1619,6 +1620,7 @@ public class ProgressBar extends View { * @param stateDescription The state description. */ @Override + @RemotableViewMethod public void setStateDescription(@Nullable CharSequence stateDescription) { mCustomStateDescription = stateDescription; if (stateDescription == null) { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 81572b5bbb45..e0b4ec71b0a0 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -46,6 +46,8 @@ import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; +import android.content.res.loader.ResourcesLoader; +import android.content.res.loader.ResourcesProvider; import android.graphics.Bitmap; import android.graphics.BlendMode; import android.graphics.Outline; @@ -62,16 +64,19 @@ import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.Process; import android.os.StrictMode; import android.os.UserHandle; +import android.system.Os; import android.text.TextUtils; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.IntArray; import android.util.Log; import android.util.Pair; +import android.util.SparseIntArray; import android.util.TypedValue; import android.util.TypedValue.ComplexDimensionUnit; import android.view.ContextThemeWrapper; @@ -86,11 +91,18 @@ import android.view.ViewOutlineProvider; import android.view.ViewParent; import android.view.ViewStub; import android.widget.AdapterView.OnItemClickListener; +import android.widget.CompoundButton.OnCheckedChangeListener; import com.android.internal.R; import com.android.internal.util.ContrastColorUtil; import com.android.internal.util.Preconditions; +import java.io.ByteArrayOutputStream; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -151,6 +163,9 @@ public class RemoteViews implements Parcelable, Filter { private static final String LOG_TAG = "RemoteViews"; + /** The intent extra for whether the view whose checked state changed is currently checked. */ + public static final String EXTRA_CHECKED = "android.widget.extra.CHECKED"; + /** * The intent extra that contains the appWidgetId. * @hide @@ -206,6 +221,8 @@ public class RemoteViews implements Parcelable, Filter { private static final int SET_COMPOUND_BUTTON_CHECKED_TAG = 26; private static final int SET_RADIO_GROUP_CHECKED = 27; private static final int SET_VIEW_OUTLINE_RADIUS_TAG = 28; + private static final int SET_ON_CHECKED_CHANGE_RESPONSE_TAG = 29; + private static final int NIGHT_MODE_REFLECTION_ACTION_TAG = 30; /** @hide **/ @IntDef(prefix = "MARGIN_", value = { @@ -218,35 +235,17 @@ public class RemoteViews implements Parcelable, Filter { }) @Retention(RetentionPolicy.SOURCE) public @interface MarginType {} - /** - * The value will apply to the marginLeft. - * @hide - */ + /** The value will apply to the marginLeft. */ public static final int MARGIN_LEFT = 0; - /** - * The value will apply to the marginTop. - * @hide - */ + /** The value will apply to the marginTop. */ public static final int MARGIN_TOP = 1; - /** - * The value will apply to the marginRight. - * @hide - */ + /** The value will apply to the marginRight. */ public static final int MARGIN_RIGHT = 2; - /** - * The value will apply to the marginBottom. - * @hide - */ + /** The value will apply to the marginBottom. */ public static final int MARGIN_BOTTOM = 3; - /** - * The value will apply to the marginStart. - * @hide - */ + /** The value will apply to the marginStart. */ public static final int MARGIN_START = 4; - /** - * The value will apply to the marginEnd. - * @hide - */ + /** The value will apply to the marginEnd. */ public static final int MARGIN_END = 5; /** @hide **/ @@ -362,8 +361,9 @@ public class RemoteViews implements Parcelable, Filter { /** Class cookies of the Parcel this instance was read from. */ private Map<Class, Object> mClassCookies; - private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = (view, pendingIntent, response) - -> startPendingIntent(view, pendingIntent, response.getLaunchOptions(view)); + private static final InteractionHandler DEFAULT_INTERACTION_HANDLER = + (view, pendingIntent, response) -> + startPendingIntent(view, pendingIntent, response.getLaunchOptions(view)); private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>(); @@ -502,11 +502,26 @@ public class RemoteViews implements Parcelable, Filter { } } - /** @hide */ - public interface OnClickHandler { - - /** @hide */ - boolean onClickHandler(View view, PendingIntent pendingIntent, RemoteResponse response); + /** + * Handler for view interactions (such as clicks) within a RemoteViews. + * + * @hide + */ + public interface InteractionHandler { + /** + * Invoked when the user performs an interaction on the View. + * + * @param view the View with which the user interacted + * @param pendingIntent the base PendingIntent associated with the view + * @param response the response to the interaction, which knows how to fill in the + * attached PendingIntent + * + * @hide + */ + boolean onInteraction( + View view, + PendingIntent pendingIntent, + RemoteResponse response); } /** @@ -516,8 +531,8 @@ public class RemoteViews implements Parcelable, Filter { * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!! */ private abstract static class Action implements Parcelable { - public abstract void apply(View root, ViewGroup rootParent, - OnClickHandler handler) throws ActionException; + public abstract void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException; public static final int MERGE_REPLACE = 0; public static final int MERGE_APPEND = 1; @@ -547,7 +562,8 @@ public class RemoteViews implements Parcelable, Filter { * and return the final action which will run on the UI thread. * Override this if some of the tasks can be performed async. */ - public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) { + public Action initActionAsync(ViewTree root, ViewGroup rootParent, + InteractionHandler handler, ColorResources colorResources) { return this; } @@ -590,7 +606,9 @@ public class RemoteViews implements Parcelable, Filter { // Constant used during async execution. It is not parcelable. private static final Action ACTION_NOOP = new RuntimeAction() { @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { } + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { + } }; /** @@ -708,7 +726,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View view = root.findViewById(viewId); if (!(view instanceof AdapterView<?>)) return; @@ -743,7 +762,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, final InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -751,32 +771,29 @@ public class RemoteViews implements Parcelable, Filter { if (target instanceof AdapterView<?>) { AdapterView<?> av = (AdapterView<?>) target; // The PendingIntent template is stored in the view's tag. - OnItemClickListener listener = new OnItemClickListener() { - public void onItemClick(AdapterView<?> parent, View view, - int position, long id) { - // The view should be a frame layout - if (view instanceof ViewGroup) { - ViewGroup vg = (ViewGroup) view; - - // AdapterViews contain their children in a frame - // so we need to go one layer deeper here. - if (parent instanceof AdapterViewAnimator) { - vg = (ViewGroup) vg.getChildAt(0); - } - if (vg == null) return; - - RemoteResponse response = null; - int childCount = vg.getChildCount(); - for (int i = 0; i < childCount; i++) { - Object tag = vg.getChildAt(i).getTag(com.android.internal.R.id.fillInIntent); - if (tag instanceof RemoteResponse) { - response = (RemoteResponse) tag; - break; - } + OnItemClickListener listener = (parent, view, position, id) -> { + // The view should be a frame layout + if (view instanceof ViewGroup) { + ViewGroup vg = (ViewGroup) view; + + // AdapterViews contain their children in a frame + // so we need to go one layer deeper here. + if (parent instanceof AdapterViewAnimator) { + vg = (ViewGroup) vg.getChildAt(0); + } + if (vg == null) return; + + RemoteResponse response = null; + int childCount = vg.getChildCount(); + for (int i = 0; i < childCount; i++) { + Object tag = vg.getChildAt(i).getTag(R.id.fillInIntent); + if (tag instanceof RemoteResponse) { + response = (RemoteResponse) tag; + break; } - if (response == null) return; - response.handleViewClick(view, handler); } + if (response == null) return; + response.handleViewInteraction(view, handler); } }; av.setOnItemClickListener(listener); @@ -818,7 +835,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -841,7 +859,8 @@ public class RemoteViews implements Parcelable, Filter { if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { ((RemoteViewsListAdapter) a).setViewsList(list); } else { - v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount)); + v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount, + colorResources)); } } else if (target instanceof AdapterViewAnimator) { AdapterViewAnimator v = (AdapterViewAnimator) target; @@ -849,7 +868,8 @@ public class RemoteViews implements Parcelable, Filter { if (a instanceof RemoteViewsListAdapter && viewTypeCount <= a.getViewTypeCount()) { ((RemoteViewsListAdapter) a).setViewsList(list); } else { - v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount)); + v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount, + colorResources)); } } } @@ -880,7 +900,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -907,7 +928,7 @@ public class RemoteViews implements Parcelable, Filter { if (target instanceof AbsListView) { AbsListView v = (AbsListView) target; v.setRemoteViewsAdapter(intent, isAsync); - v.setRemoteViewsOnClickHandler(handler); + v.setRemoteViewsInteractionHandler(handler); } else if (target instanceof AdapterViewAnimator) { AdapterViewAnimator v = (AdapterViewAnimator) target; v.setRemoteViewsAdapter(intent, isAsync); @@ -917,7 +938,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public Action initActionAsync(ViewTree root, ViewGroup rootParent, - OnClickHandler handler) { + InteractionHandler handler, ColorResources colorResources) { SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent); copy.isAsync = true; return copy; @@ -956,7 +977,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, final OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, final InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -991,11 +1013,13 @@ public class RemoteViews implements Parcelable, Filter { return; } } else { - // No intent to apply + // No intent to apply, clear the listener and any tags that were previously set. target.setOnClickListener(null); + target.setTagInternal(R.id.pending_intent_tag, null); + target.setTagInternal(com.android.internal.R.id.fillInIntent, null); return; } - target.setOnClickListener(v -> mResponse.handleViewClick(v, handler)); + target.setOnClickListener(v -> mResponse.handleViewInteraction(v, handler)); } @Override @@ -1006,6 +1030,78 @@ public class RemoteViews implements Parcelable, Filter { final RemoteResponse mResponse; } + /** + * Equivalent to calling + * {@link android.widget.CompoundButton#setOnCheckedChangeListener( + * android.widget.CompoundButton.OnCheckedChangeListener)} + * to launch the provided {@link PendingIntent}. + */ + private class SetOnCheckedChangeResponse extends Action { + + private final RemoteResponse mResponse; + + SetOnCheckedChangeResponse(@IdRes int id, RemoteResponse response) { + this.viewId = id; + this.mResponse = response; + } + + SetOnCheckedChangeResponse(Parcel parcel) { + viewId = parcel.readInt(); + mResponse = new RemoteResponse(); + mResponse.readFromParcel(parcel); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(viewId); + mResponse.writeToParcel(dest, flags); + } + + @Override + public void apply(View root, ViewGroup rootParent, final InteractionHandler handler, + ColorResources colorResources) { + final View target = root.findViewById(viewId); + if (target == null) return; + if (!(target instanceof CompoundButton)) { + Log.w(LOG_TAG, "setOnCheckedChange methods cannot be used on " + + "non-CompoundButton child (id: " + viewId + ")"); + return; + } + CompoundButton button = (CompoundButton) target; + + if (mResponse.mPendingIntent != null) { + // setOnCheckedChangePendingIntent cannot be used with collection children, which + // must use setOnCheckedChangeFillInIntent instead. + if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) { + Log.w(LOG_TAG, "Cannot setOnCheckedChangePendingIntent for collection item " + + "(id: " + viewId + ")"); + return; + } + target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent); + } else if (mResponse.mFillIntent != null) { + if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) { + Log.e(LOG_TAG, "The method setOnCheckedChangeFillInIntent is available " + + "only from RemoteViewsFactory (ie. on collection items)."); + return; + } + } else { + // No intent to apply, clear any existing listener or tag. + button.setOnCheckedChangeListener(null); + button.setTagInternal(R.id.remote_checked_change_listener_tag, null); + return; + } + + OnCheckedChangeListener onCheckedChangeListener = + (v, isChecked) -> mResponse.handleViewInteraction(v, handler); + button.setTagInternal(R.id.remote_checked_change_listener_tag, onCheckedChangeListener); + button.setOnCheckedChangeListener(onCheckedChangeListener); + } + + @Override + public int getActionTag() { + return SET_ON_CHECKED_CHANGE_RESPONSE_TAG; + } + } + /** @hide **/ public static Rect getSourceBounds(View v) { final float appScale = v.getContext().getResources() @@ -1165,7 +1261,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -1222,7 +1319,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -1259,7 +1357,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View view = root.findViewById(viewId); if (view == null) return; @@ -1362,12 +1461,12 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, - OnClickHandler handler) throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { ReflectionAction ra = new ReflectionAction(viewId, methodName, BaseReflectionAction.BITMAP, bitmap); - ra.apply(root, rootParent, handler); + ra.apply(root, rootParent, handler, colorResources); } @Override @@ -1443,7 +1542,8 @@ public class RemoteViews implements Parcelable, Filter { protected abstract Object getParameterValue(@Nullable View view) throws ActionException; @Override - public final void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public final void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View view = root.findViewById(viewId); if (view == null) return; @@ -1461,7 +1561,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public final Action initActionAsync(ViewTree root, ViewGroup rootParent, - OnClickHandler handler) { + InteractionHandler handler, ColorResources colorResources) { final View view = root.findViewById(viewId); if (view == null) return ACTION_NOOP; @@ -1785,6 +1885,73 @@ public class RemoteViews implements Parcelable, Filter { } } + private final class NightModeReflectionAction extends BaseReflectionAction { + + private final Object mLightValue; + private final Object mDarkValue; + + NightModeReflectionAction( + @IdRes int viewId, + String methodName, + int type, + Object lightValue, + Object darkValue) { + super(viewId, methodName, type); + mLightValue = lightValue; + mDarkValue = darkValue; + } + + NightModeReflectionAction(Parcel in) { + super(in); + switch (this.type) { + case ICON: + mLightValue = in.readTypedObject(Icon.CREATOR); + mDarkValue = in.readTypedObject(Icon.CREATOR); + break; + case COLOR_STATE_LIST: + mLightValue = in.readTypedObject(ColorStateList.CREATOR); + mDarkValue = in.readTypedObject(ColorStateList.CREATOR); + break; + case INT: + mLightValue = in.readInt(); + mDarkValue = in.readInt(); + break; + default: + throw new ActionException("Unexpected night mode action type: " + this.type); + } + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + switch (this.type) { + case ICON: + case COLOR_STATE_LIST: + out.writeTypedObject((Parcelable) mLightValue, flags); + out.writeTypedObject((Parcelable) mDarkValue, flags); + break; + case INT: + out.writeInt((int) mLightValue); + out.writeInt((int) mDarkValue); + break; + } + } + + @Nullable + @Override + protected Object getParameterValue(@Nullable View view) throws ActionException { + if (view == null) return null; + + Configuration configuration = view.getResources().getConfiguration(); + return configuration.isNightModeActive() ? mDarkValue : mLightValue; + } + + @Override + public int getActionTag() { + return NIGHT_MODE_REFLECTION_ACTION_TAG; + } + } + /** * This is only used for async execution of actions and it not parcelable. */ @@ -1796,7 +1963,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { mRunnable.run(); } } @@ -1851,7 +2019,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final Context context = root.getContext(); final ViewGroup target = root.findViewById(viewId); @@ -1860,11 +2029,14 @@ public class RemoteViews implements Parcelable, Filter { } // Inflate nested views and add as children - target.addView(mNestedViews.apply(context, target, handler), mIndex); + target.addView( + mNestedViews.apply(context, target, handler, null /* size */, colorResources), + mIndex); } @Override - public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) { + public Action initActionAsync(ViewTree root, ViewGroup rootParent, + InteractionHandler handler, ColorResources colorResources) { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the current view. root.createTree(); @@ -1876,8 +2048,8 @@ public class RemoteViews implements Parcelable, Filter { // Inflate nested views and perform all the async tasks for the child remoteView. final Context context = root.mRoot.getContext(); - final AsyncApplyTask task = mNestedViews.getAsyncApplyTask( - context, targetVg, null, handler); + final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(context, targetVg, + null /* listener */, handler, null /* size */, colorResources); final ViewTree tree = task.doInBackground(); if (tree == null) { @@ -1890,8 +2062,8 @@ public class RemoteViews implements Parcelable, Filter { return new RuntimeAction() { @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { task.onPostExecute(tree); targetVg.addView(task.mResult, mIndex); } @@ -1952,7 +2124,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final ViewGroup target = root.findViewById(viewId); if (target == null) { @@ -1968,7 +2141,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) { + public Action initActionAsync(ViewTree root, ViewGroup rootParent, + InteractionHandler handler, ColorResources colorResources) { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the current view. root.createTree(); @@ -1992,8 +2166,8 @@ public class RemoteViews implements Parcelable, Filter { } return new RuntimeAction() { @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) { targetVg.removeAllViews(); return; @@ -2048,7 +2222,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null || target == root) { @@ -2062,7 +2237,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) { + public Action initActionAsync(ViewTree root, ViewGroup rootParent, + InteractionHandler handler, ColorResources colorResources) { // In the async implementation, update the view tree so that subsequent calls to // findViewById return the correct view. root.createTree(); @@ -2081,8 +2257,8 @@ public class RemoteViews implements Parcelable, Filter { parent.mChildren.remove(target); return new RuntimeAction() { @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { parentVg.removeView(target.mRoot); } }; @@ -2161,7 +2337,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final TextView target = root.findViewById(viewId); if (target == null) return; if (drawablesLoaded) { @@ -2191,7 +2368,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) { + public Action initActionAsync(ViewTree root, ViewGroup rootParent, + InteractionHandler handler, ColorResources colorResources) { final TextView target = root.findViewById(viewId); if (target == null) return ACTION_NOOP; @@ -2269,7 +2447,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final TextView target = root.findViewById(viewId); if (target == null) return; target.setTextSize(units, size); @@ -2314,7 +2493,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; target.setPadding(left, top, right, bottom); @@ -2387,7 +2567,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) { return; @@ -2500,7 +2681,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(viewId); if (target == null) return; @@ -2535,7 +2717,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { // Let's traverse the viewtree and override all textColors! Stack<View> viewsToProcess = new Stack<>(); viewsToProcess.add(root); @@ -2585,7 +2768,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) { final View target = root.findViewById(mViewId); if (target == null) return; @@ -2619,7 +2803,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { final View target = root.findViewById(viewId); if (target == null) return; @@ -2630,7 +2815,17 @@ public class RemoteViews implements Parcelable, Filter { return; } - ((CompoundButton) target).setChecked(mChecked); + CompoundButton button = (CompoundButton) target; + Object tag = button.getTag(R.id.remote_checked_change_listener_tag); + // Temporarily unset the checked change listener so calling setChecked doesn't launch + // the intent. + if (tag instanceof OnCheckedChangeListener) { + button.setOnCheckedChangeListener(null); + button.setChecked(mChecked); + button.setOnCheckedChangeListener((OnCheckedChangeListener) tag); + } else { + button.setChecked(mChecked); + } } @Override @@ -2660,8 +2855,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { final View target = root.findViewById(viewId); if (target == null) return; @@ -2670,7 +2865,32 @@ public class RemoteViews implements Parcelable, Filter { return; } - ((RadioGroup) target).check(mCheckedId); + RadioGroup group = (RadioGroup) target; + + // Temporarily unset all the checked change listeners while we check the group. + for (int i = 0; i < group.getChildCount(); i++) { + View child = group.getChildAt(i); + if (!(child instanceof CompoundButton)) continue; + + Object tag = child.getTag(R.id.remote_checked_change_listener_tag); + if (!(tag instanceof OnCheckedChangeListener)) continue; + + // Clear the checked change listener, we'll restore it after the check. + ((CompoundButton) child).setOnCheckedChangeListener(null); + } + + group.check(mCheckedId); + + // Loop through the children again and restore the checked change listeners. + for (int i = 0; i < group.getChildCount(); i++) { + View child = group.getChildAt(i); + if (!(child instanceof CompoundButton)) continue; + + Object tag = child.getTag(R.id.remote_checked_change_listener_tag); + if (!(tag instanceof OnCheckedChangeListener)) continue; + + ((CompoundButton) child).setOnCheckedChangeListener((OnCheckedChangeListener) tag); + } } @Override @@ -2712,8 +2932,8 @@ public class RemoteViews implements Parcelable, Filter { } @Override - public void apply(View root, ViewGroup rootParent, OnClickHandler handler) - throws ActionException { + public void apply(View root, ViewGroup rootParent, InteractionHandler handler, + ColorResources colorResources) throws ActionException { final View target = root.findViewById(viewId); if (target == null) return; @@ -3110,6 +3330,10 @@ public class RemoteViews implements Parcelable, Filter { return new SetRadioGroupCheckedAction(parcel); case SET_VIEW_OUTLINE_RADIUS_TAG: return new SetViewOutlinePreferredRadiusAction(parcel); + case SET_ON_CHECKED_CHANGE_RESPONSE_TAG: + return new SetOnCheckedChangeResponse(parcel); + case NIGHT_MODE_REFLECTION_ACTION_TAG: + return new NightModeReflectionAction(parcel); default: throw new ActionException("Tag " + tag + " not found"); } @@ -3567,6 +3791,41 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to calling + * {@link android.widget.CompoundButton#setOnCheckedChangeListener( + * android.widget.CompoundButton.OnCheckedChangeListener)} + * to launch the provided {@link RemoteResponse}. + * + * The intent will be filled with the current checked state of the view at the key + * {@link #EXTRA_CHECKED}. + * + * The {@link RemoteResponse} will not be launched in response to check changes arising from + * {@link #setCompoundButtonChecked(int, boolean)} or {@link #setRadioGroupChecked(int, int)} + * usages. + * + * The {@link RemoteResponse} must be created using + * {@link RemoteResponse#fromFillInIntent(Intent)} in conjunction with + * {@link RemoteViews#setPendingIntentTemplate(int, PendingIntent)} for items inside + * collections (eg. {@link ListView}, {@link StackView} etc.). + * + * Otherwise, create the {@link RemoteResponse} using + * {@link RemoteResponse#fromPendingIntent(PendingIntent)}. + * + * @param viewId The id of the view that will trigger the {@link PendingIntent} when checked + * state changes. + * @param response The {@link RemoteResponse} to send when the checked state changes. + */ + public void setOnCheckedChangeResponse( + @IdRes int viewId, + @NonNull RemoteResponse response) { + addAction( + new SetOnCheckedChangeResponse( + viewId, + response.setInteractionType( + RemoteResponse.INTERACTION_TYPE_CHECKED_CHANGE))); + } + + /** * @hide * Equivalent to calling * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}, @@ -3764,7 +4023,6 @@ public class RemoteViews implements Parcelable, Filter { * @param viewId The id of the view to change * @param type The margin being set e.g. {@link #MARGIN_END} * @param dimen a dimension resource to apply to the margin, or 0 to clear the margin. - * @hide */ public void setViewLayoutMarginDimen(@IdRes int viewId, @MarginType int type, @DimenRes int dimen) { @@ -3783,7 +4041,6 @@ public class RemoteViews implements Parcelable, Filter { * @param type The margin being set e.g. {@link #MARGIN_END} * @param value a value for the margin the given units. * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP} - * @hide */ public void setViewLayoutMargin(@IdRes int viewId, @MarginType int type, float value, @ComplexDimensionUnit int units) { @@ -3801,7 +4058,6 @@ public class RemoteViews implements Parcelable, Filter { * * @param width Width of the view in the given units * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP} - * @hide */ public void setViewLayoutWidth(@IdRes int viewId, float width, @ComplexDimensionUnit int units) { @@ -3813,7 +4069,6 @@ public class RemoteViews implements Parcelable, Filter { * the result of {@link Resources#getDimensionPixelSize(int)}. * * @param widthDimen the dimension resource for the view's width - * @hide */ public void setViewLayoutWidthDimen(@IdRes int viewId, @DimenRes int widthDimen) { addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, widthDimen)); @@ -3830,7 +4085,6 @@ public class RemoteViews implements Parcelable, Filter { * * @param height height of the view in the given units * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP} - * @hide */ public void setViewLayoutHeight(@IdRes int viewId, float height, @ComplexDimensionUnit int units) { @@ -3842,7 +4096,6 @@ public class RemoteViews implements Parcelable, Filter { * the result of {@link Resources#getDimensionPixelSize(int)}. * * @param heightDimen a dimen resource to read the height from. - * @hide */ public void setViewLayoutHeightDimen(@IdRes int viewId, @DimenRes int heightDimen) { addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, heightDimen)); @@ -3962,6 +4215,30 @@ public class RemoteViews implements Parcelable, Filter { ResourceReflectionAction.COLOR_RESOURCE, colorResource)); } + /** + * Call a method taking one int, a color, on a view in the layout for this RemoteViews. + * + * @param viewId The id of the view on which to call the method. + * @param methodName The name of the method to call. + * @param notNight The value to pass to the method when the view's configuration is set to + * {@link Configuration#UI_MODE_NIGHT_NO} + * @param night The value to pass to the method when the view's configuration is set to + * {@link Configuration#UI_MODE_NIGHT_YES} + */ + public void setColorInt( + @IdRes int viewId, + @NonNull String methodName, + @ColorInt int notNight, + @ColorInt int night) { + addAction( + new NightModeReflectionAction( + viewId, + methodName, + BaseReflectionAction.INT, + notNight, + night)); + } + /** * Call a method taking one ColorStateList on a view in the layout for this RemoteViews. @@ -3969,10 +4246,9 @@ public class RemoteViews implements Parcelable, Filter { * @param viewId The id of the view on which to call the method. * @param methodName The name of the method to call. * @param value The value to pass to the method. - * - * @hide */ - public void setColorStateList(@IdRes int viewId, String methodName, ColorStateList value) { + public void setColorStateList(@IdRes int viewId, @NonNull String methodName, + @Nullable ColorStateList value) { addAction(new ReflectionAction(viewId, methodName, BaseReflectionAction.COLOR_STATE_LIST, value)); } @@ -3980,6 +4256,30 @@ public class RemoteViews implements Parcelable, Filter { /** * Call a method taking one ColorStateList on a view in the layout for this RemoteViews. * + * @param viewId The id of the view on which to call the method. + * @param methodName The name of the method to call. + * @param notNight The value to pass to the method when the view's configuration is set to + * {@link Configuration#UI_MODE_NIGHT_NO} + * @param night The value to pass to the method when the view's configuration is set to + * {@link Configuration#UI_MODE_NIGHT_YES} + */ + public void setColorStateList( + @IdRes int viewId, + @NonNull String methodName, + @Nullable ColorStateList notNight, + @Nullable ColorStateList night) { + addAction( + new NightModeReflectionAction( + viewId, + methodName, + BaseReflectionAction.COLOR_STATE_LIST, + notNight, + night)); + } + + /** + * Call a method taking one ColorStateList on a view in the layout for this RemoteViews. + * * The ColorStateList will be resolved from the resources at the time of inflation. * * @param viewId The id of the view on which to call the method. @@ -4188,6 +4488,30 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Call a method taking one Icon on a view in the layout for this RemoteViews. + * + * @param viewId The id of the view on which to call the method. + * @param methodName The name of the method to call. + * @param notNight The value to pass to the method when the view's configuration is set to + * {@link Configuration#UI_MODE_NIGHT_NO} + * @param night The value to pass to the method when the view's configuration is set to + * {@link Configuration#UI_MODE_NIGHT_YES} + */ + public void setIcon( + @IdRes int viewId, + @NonNull String methodName, + @Nullable Icon notNight, + @Nullable Icon night) { + addAction( + new NightModeReflectionAction( + viewId, + methodName, + BaseReflectionAction.ICON, + notNight, + night)); + } + + /** * Equivalent to calling View.setContentDescription(CharSequence). * * @param viewId The id of the view whose content description should change. @@ -4367,49 +4691,61 @@ public class RemoteViews implements Parcelable, Filter { } /** @hide */ - public View apply(Context context, ViewGroup parent, OnClickHandler handler) { + public View apply(Context context, ViewGroup parent, InteractionHandler handler) { return apply(context, parent, handler, null); } /** @hide */ public View apply(@NonNull Context context, @NonNull ViewGroup parent, - @Nullable OnClickHandler handler, @Nullable PointF size) { + @Nullable InteractionHandler handler, @Nullable PointF size) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); View result = inflateView(context, rvToApply, parent); - rvToApply.performApply(result, parent, handler); + rvToApply.performApply(result, parent, handler, null); return result; } /** @hide */ public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent, - @Nullable OnClickHandler handler, - @StyleRes int applyThemeResId) { + @Nullable InteractionHandler handler, @StyleRes int applyThemeResId) { return applyWithTheme(context, parent, handler, applyThemeResId, null); } /** @hide */ public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent, - @Nullable OnClickHandler handler, - @StyleRes int applyThemeResId, @Nullable PointF size) { + @Nullable InteractionHandler handler, @StyleRes int applyThemeResId, + @Nullable PointF size) { + RemoteViews rvToApply = getRemoteViewsToApply(context, size); + + View result = inflateView(context, rvToApply, parent, applyThemeResId, null); + rvToApply.performApply(result, parent, handler, null); + return result; + } + + /** @hide */ + public View apply(Context context, ViewGroup parent, InteractionHandler handler, + @NonNull PointF size, @Nullable ColorResources colorResources) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); - View result = inflateView(context, rvToApply, parent, applyThemeResId); - rvToApply.performApply(result, parent, handler); + View result = inflateView(context, rvToApply, parent, 0, colorResources); + rvToApply.performApply(result, parent, handler, colorResources); return result; } private View inflateView(Context context, RemoteViews rv, ViewGroup parent) { - return inflateView(context, rv, parent, 0); + return inflateView(context, rv, parent, 0, null); } private View inflateView(Context context, RemoteViews rv, ViewGroup parent, - @StyleRes int applyThemeResId) { + @StyleRes int applyThemeResId, @Nullable ColorResources colorResources) { // RemoteViews may be built by an application installed in another // user. So build a context that loads resources from that user but // still returns the current users userId so settings like data / time formats // are loaded without requiring cross user persmissions. final Context contextForResources = getContextForResources(context); + if (colorResources != null) { + colorResources.apply(contextForResources); + } Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources); // If mApplyThemeResId is not given, Theme.DeviceDefault will be used. @@ -4471,33 +4807,37 @@ public class RemoteViews implements Parcelable, Filter { */ public CancellationSignal applyAsync( Context context, ViewGroup parent, Executor executor, OnViewAppliedListener listener) { - return applyAsync(context, parent, executor, listener, null); + return applyAsync(context, parent, executor, listener, null /* handler */); } + /** @hide */ public CancellationSignal applyAsync(Context context, ViewGroup parent, - Executor executor, OnViewAppliedListener listener, OnClickHandler handler) { - return applyAsync(context, parent, executor, listener, handler, null); + Executor executor, OnViewAppliedListener listener, InteractionHandler handler) { + return applyAsync(context, parent, executor, listener, handler, null /* size */); } /** @hide */ public CancellationSignal applyAsync(Context context, ViewGroup parent, - Executor executor, OnViewAppliedListener listener, OnClickHandler handler, + Executor executor, OnViewAppliedListener listener, InteractionHandler handler, PointF size) { - return getAsyncApplyTask(context, parent, listener, handler, size).startTaskOnExecutor( - executor); + return getAsyncApplyTask(context, parent, listener, handler, size, null /* themeColors */) + .startTaskOnExecutor(executor); } - private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent, - OnViewAppliedListener listener, OnClickHandler handler) { - return getAsyncApplyTask(context, parent, listener, handler, null); + /** @hide */ + public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor, + OnViewAppliedListener listener, InteractionHandler handler, PointF size, + ColorResources colorResources) { + return getAsyncApplyTask(context, parent, listener, handler, size, colorResources) + .startTaskOnExecutor(executor); } private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent, - OnViewAppliedListener listener, OnClickHandler handler, PointF size) { - return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, - listener, - handler, null); + OnViewAppliedListener listener, InteractionHandler handler, PointF size, + ColorResources colorResources) { + return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener, + handler, colorResources, null /* result */); } private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree> @@ -4507,7 +4847,8 @@ public class RemoteViews implements Parcelable, Filter { final ViewGroup mParent; final Context mContext; final OnViewAppliedListener mListener; - final OnClickHandler mHandler; + final InteractionHandler mHandler; + final ColorResources mColorResources; private View mResult; private ViewTree mTree; @@ -4516,11 +4857,12 @@ public class RemoteViews implements Parcelable, Filter { private AsyncApplyTask( RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener, - OnClickHandler handler, View result) { + InteractionHandler handler, ColorResources colorResources, View result) { mRV = rv; mParent = parent; mContext = context; mListener = listener; + mColorResources = colorResources; mHandler = handler; mResult = result; @@ -4530,7 +4872,7 @@ public class RemoteViews implements Parcelable, Filter { protected ViewTree doInBackground(Void... params) { try { if (mResult == null) { - mResult = inflateView(mContext, mRV, mParent); + mResult = inflateView(mContext, mRV, mParent, 0, mColorResources); } mTree = new ViewTree(mResult); @@ -4539,7 +4881,8 @@ public class RemoteViews implements Parcelable, Filter { mActions = new Action[count]; for (int i = 0; i < count && !isCancelled(); i++) { // TODO: check if isCancelled in nested views. - mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler); + mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler, + mColorResources); } } else { mActions = null; @@ -4561,10 +4904,10 @@ public class RemoteViews implements Parcelable, Filter { try { if (mActions != null) { - OnClickHandler handler = mHandler == null - ? DEFAULT_ON_CLICK_HANDLER : mHandler; + InteractionHandler handler = mHandler == null + ? DEFAULT_INTERACTION_HANDLER : mHandler; for (Action a : mActions) { - a.apply(viewTree.mRoot, mParent, handler); + a.apply(viewTree.mRoot, mParent, handler, mColorResources); } } } catch (Exception e) { @@ -4608,16 +4951,17 @@ public class RemoteViews implements Parcelable, Filter { * the {@link #apply(Context,ViewGroup)} call. */ public void reapply(Context context, View v) { - reapply(context, v, null); + reapply(context, v, null /* handler */); } /** @hide */ - public void reapply(Context context, View v, OnClickHandler handler) { - reapply(context, v, handler, null); + public void reapply(Context context, View v, InteractionHandler handler) { + reapply(context, v, handler, null /* size */, null /* colorResources */); } /** @hide */ - public void reapply(Context context, View v, OnClickHandler handler, PointF size) { + public void reapply(Context context, View v, InteractionHandler handler, PointF size, + ColorResources colorResources) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); // In the case that a view has this RemoteViews applied in one orientation or size, is @@ -4631,7 +4975,7 @@ public class RemoteViews implements Parcelable, Filter { } } - rvToApply.performApply(v, (ViewGroup) v.getParent(), handler); + rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources); } /** @@ -4647,20 +4991,21 @@ public class RemoteViews implements Parcelable, Filter { * @return CancellationSignal * @hide */ - public CancellationSignal reapplyAsync( - Context context, View v, Executor executor, OnViewAppliedListener listener) { + public CancellationSignal reapplyAsync(Context context, View v, Executor executor, + OnViewAppliedListener listener) { return reapplyAsync(context, v, executor, listener, null); } /** @hide */ public CancellationSignal reapplyAsync(Context context, View v, Executor executor, - OnViewAppliedListener listener, OnClickHandler handler) { - return reapplyAsync(context, v, executor, listener, handler, null); + OnViewAppliedListener listener, InteractionHandler handler) { + return reapplyAsync(context, v, executor, listener, handler, null, null); } /** @hide */ public CancellationSignal reapplyAsync(Context context, View v, Executor executor, - OnViewAppliedListener listener, OnClickHandler handler, PointF size) { + OnViewAppliedListener listener, InteractionHandler handler, PointF size, + ColorResources colorResources) { RemoteViews rvToApply = getRemoteViewsToApply(context, size); // In the case that a view has this RemoteViews applied in one orientation, is persisted @@ -4674,16 +5019,18 @@ public class RemoteViews implements Parcelable, Filter { } return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(), - context, listener, handler, v).startTaskOnExecutor(executor); + context, listener, handler, colorResources, v).startTaskOnExecutor( + executor); } - private void performApply(View v, ViewGroup parent, OnClickHandler handler) { + private void performApply(View v, ViewGroup parent, InteractionHandler handler, + ColorResources colorResources) { if (mActions != null) { - handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler; + handler = handler == null ? DEFAULT_INTERACTION_HANDLER : handler; final int count = mActions.size(); for (int i = 0; i < count; i++) { Action a = mActions.get(i); - a.apply(v, parent, handler); + a.apply(v, parent, handler, colorResources); } } } @@ -4724,6 +5071,122 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Object allowing the modification of a context to overload the system's dynamic colors. + * + * Only colors from {@link android.R.color#system_primary_0} to + * {@link android.R.color#system_neutral_1000} can be overloaded. + * @hide + */ + public static final class ColorResources { + // Set of valid colors resources. + private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_primary_0; + private static final int LAST_RESOURCE_COLOR_ID = android.R.color.system_neutral_1000; + // Size, in bytes, of an entry in the array of colors in an ARSC file. + private static final int ARSC_ENTRY_SIZE = 16; + + private ResourcesLoader mLoader; + + private ColorResources(ResourcesLoader loader) { + mLoader = loader; + } + + /** + * Apply the color resources to the given context. + * + * No resource resolution must have be done on the context given to that method. + */ + public void apply(Context context) { + context.getResources().addLoaders(mLoader); + } + + private static ByteArrayOutputStream readFileContent(InputStream input) throws IOException { + ByteArrayOutputStream content = new ByteArrayOutputStream(2048); + byte[] buffer = new byte[4096]; + while (input.available() > 0) { + int read = input.read(buffer); + content.write(buffer, 0, read); + } + return content; + } + + /** + * Creates the compiled resources content from the asset stored in the APK. + * + * The asset is a compiled resource with the correct resources name and correct ids, only + * the values are incorrect. The last value is at the very end of the file. The resources + * are in an array, the array's entries are 16 bytes each. We use this to work out the + * location of all the positions of the various resources. + */ + private static byte[] createCompiledResourcesContent(Context context, + SparseIntArray colorResources) throws IOException { + byte[] content; + try (InputStream input = context.getResources().openRawResource( + com.android.internal.R.raw.remote_views_color_resources)) { + ByteArrayOutputStream rawContent = readFileContent(input); + content = rawContent.toByteArray(); + } + int valuesOffset = + content.length - (LAST_RESOURCE_COLOR_ID & 0xffff) * ARSC_ENTRY_SIZE - 4; + if (valuesOffset < 0) { + Log.e(LOG_TAG, "ARSC file for theme colors is invalid."); + return null; + } + for (int colorRes = FIRST_RESOURCE_COLOR_ID; colorRes <= LAST_RESOURCE_COLOR_ID; + colorRes++) { + // The last 2 bytes are the index in the color array. + int index = colorRes & 0xffff; + int offset = valuesOffset + index * ARSC_ENTRY_SIZE; + int value = colorResources.get(colorRes, context.getColor(colorRes)); + // Write the 32 bit integer in little endian + for (int b = 0; b < 4; b++) { + content[offset + b] = (byte) (value & 0xff); + value >>= 8; + } + } + return content; + } + + /** + * Adds a resource loader for theme colors to the given context. + * + * @param context Context of the view hosting the widget. + * @param colorMapping Mapping of resources to color values. + * + * @hide + */ + public static ColorResources create(Context context, SparseIntArray colorMapping) { + try { + byte[] contentBytes = createCompiledResourcesContent(context, colorMapping); + if (contentBytes == null) { + return null; + } + FileDescriptor arscFile = null; + try { + arscFile = Os.memfd_create("remote_views_theme_colors.arsc", 0 /* flags */); + // Note: This must not be closed through the OutputStream. + try (OutputStream pipeWriter = new FileOutputStream(arscFile)) { + pipeWriter.write(contentBytes); + + try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(arscFile)) { + ResourcesLoader colorsLoader = new ResourcesLoader(); + colorsLoader.addProvider(ResourcesProvider + .loadFromTable(pfd, null /* assetsProvider */)); + return new ColorResources(colorsLoader); + } + } + } finally { + if (arscFile != null) { + Os.close(arscFile); + } + } + } catch (Exception ex) { + Log.e(LOG_TAG, "Failed to setup the context for theme colors", ex); + } + return null; + } + } + + /** * Returns the number of actions in this RemoteViews. Can be used as a sequence number. * * @hide @@ -4995,9 +5458,22 @@ public class RemoteViews implements Parcelable, Filter { */ public static class RemoteResponse { + /** @hide **/ + @IntDef(prefix = "INTERACTION_TYPE_", value = { + INTERACTION_TYPE_CLICK, + INTERACTION_TYPE_CHECKED_CHANGE, + }) + @Retention(RetentionPolicy.SOURCE) + @interface InteractionType {} + /** @hide */ + public static final int INTERACTION_TYPE_CLICK = 0; + /** @hide */ + public static final int INTERACTION_TYPE_CHECKED_CHANGE = 1; + private PendingIntent mPendingIntent; private Intent mFillIntent; + private int mInteractionType = INTERACTION_TYPE_CLICK; private IntArray mViewIds; private ArrayList<String> mElementNames; @@ -5069,12 +5545,27 @@ public class RemoteViews implements Parcelable, Filter { return this; } + /** + * Sets the interaction type for which this RemoteResponse responds. + * + * @param type the type of interaction for which this is a response, such as clicking or + * checked state changing + * + * @hide + */ + @NonNull + public RemoteResponse setInteractionType(@InteractionType int type) { + mInteractionType = type; + return this; + } + private void writeToParcel(Parcel dest, int flags) { PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest); if (mPendingIntent == null) { // Only write the intent if pending intent is null dest.writeTypedObject(mFillIntent, flags); } + dest.writeInt(mInteractionType); dest.writeIntArray(mViewIds == null ? null : mViewIds.toArray()); dest.writeStringList(mElementNames); } @@ -5084,50 +5575,62 @@ public class RemoteViews implements Parcelable, Filter { if (mPendingIntent == null) { mFillIntent = parcel.readTypedObject(Intent.CREATOR); } + mInteractionType = parcel.readInt(); int[] viewIds = parcel.createIntArray(); mViewIds = viewIds == null ? null : IntArray.wrap(viewIds); mElementNames = parcel.createStringArrayList(); } - private void handleViewClick(View v, OnClickHandler handler) { + private void handleViewInteraction( + View v, + InteractionHandler handler) { final PendingIntent pi; if (mPendingIntent != null) { pi = mPendingIntent; } else if (mFillIntent != null) { - // Insure that this view is a child of an AdapterView - View parent = (View) v.getParent(); - // Break the for loop on the first encounter of: - // 1) an AdapterView, - // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or - // 3) a null parent. - // 2) and 3) are unexpected and catch the case where a child is not - // correctly parented in an AdapterView. - while (parent != null && !(parent instanceof AdapterView<?>) - && !((parent instanceof AppWidgetHostView) - && !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) { - parent = (View) parent.getParent(); - } - - if (!(parent instanceof AdapterView<?>)) { - // Somehow they've managed to get this far without having - // and AdapterView as a parent. + AdapterView<?> ancestor = getAdapterViewAncestor(v); + if (ancestor == null) { Log.e(LOG_TAG, "Collection item doesn't have AdapterView parent"); return; } - // Insure that a template pending intent has been set on an ancestor - if (!(parent.getTag() instanceof PendingIntent)) { - Log.e(LOG_TAG, "Attempting setOnClickFillInIntent without" - + " calling setPendingIntentTemplate on parent."); + + // Ensure that a template pending intent has been set on the ancestor + if (!(ancestor.getTag() instanceof PendingIntent)) { + Log.e(LOG_TAG, "Attempting setOnClickFillInIntent or " + + "setOnCheckedChangeFillInIntent without calling " + + "setPendingIntentTemplate on parent."); return; } - pi = (PendingIntent) parent.getTag(); + pi = (PendingIntent) ancestor.getTag(); } else { Log.e(LOG_TAG, "Response has neither pendingIntent nor fillInIntent"); return; } - handler.onClickHandler(v, pi, this); + handler.onInteraction(v, pi, this); + } + + /** + * Returns the closest ancestor of the view that is an AdapterView or null if none could be + * found. + */ + @Nullable + private static AdapterView<?> getAdapterViewAncestor(@Nullable View view) { + View parent = (View) view.getParent(); + // Break the for loop on the first encounter of: + // 1) an AdapterView, + // 2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or + // 3) a null parent. + // 2) and 3) are unexpected and catch the case where a child is not + // correctly parented in an AdapterView. + while (parent != null && !(parent instanceof AdapterView<?>) + && !((parent instanceof AppWidgetHostView) + && !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) { + parent = (View) parent.getParent(); + } + + return parent instanceof AdapterView<?> ? (AdapterView<?>) parent : null; } /** @hide */ @@ -5135,6 +5638,11 @@ public class RemoteViews implements Parcelable, Filter { Intent intent = mPendingIntent != null ? new Intent() : new Intent(mFillIntent); intent.setSourceBounds(getSourceBounds(view)); + if (view instanceof CompoundButton + && mInteractionType == INTERACTION_TYPE_CHECKED_CHANGE) { + intent.putExtra(EXTRA_CHECKED, ((CompoundButton) view).isChecked()); + } + ActivityOptions opts = null; Context context = view.getContext(); diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index c98ed6a2f5d0..9749a688a97f 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -47,7 +47,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; -import android.widget.RemoteViews.OnClickHandler; +import android.widget.RemoteViews.InteractionHandler; import com.android.internal.widget.IRemoteViewsFactory; @@ -107,7 +107,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback private final boolean mOnLightBackground; private final Executor mAsyncViewLoadExecutor; - private OnClickHandler mRemoteViewsOnClickHandler; + private InteractionHandler mRemoteViewsInteractionHandler; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private final FixedSizeRemoteViewsCache mCache; private int mVisibleWindowLowerBound; @@ -386,9 +386,9 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback * asynchronously (for eg, when we are already showing the loading * view) */ - public void onRemoteViewsLoaded(RemoteViews view, OnClickHandler handler, + public void onRemoteViewsLoaded(RemoteViews view, InteractionHandler handler, boolean forceApplyAsync) { - setOnClickHandler(handler); + setInteractionHandler(handler); applyRemoteViews(view, forceApplyAsync || ((view != null) && view.prefersAsyncApply())); } @@ -455,7 +455,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback if (refs != null) { // Notify all the references for that position of the newly loaded RemoteViews for (final RemoteViewsFrameLayout ref : refs) { - ref.onRemoteViewsLoaded(view, mRemoteViewsOnClickHandler, true); + ref.onRemoteViewsLoaded(view, mRemoteViewsInteractionHandler, true); } } } @@ -902,9 +902,9 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback return mDataReady; } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public void setRemoteViewsOnClickHandler(OnClickHandler handler) { - mRemoteViewsOnClickHandler = handler; + /** @hide */ + public void setRemoteViewsInteractionHandler(InteractionHandler handler) { + mRemoteViewsInteractionHandler = handler; } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -1137,7 +1137,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback if (isInCache) { // Apply the view synchronously if possible, to avoid flickering - layout.onRemoteViewsLoaded(rv, mRemoteViewsOnClickHandler, false); + layout.onRemoteViewsLoaded(rv, mRemoteViewsInteractionHandler, false); if (hasNewItems) { mServiceHandler.sendEmptyMessage(MSG_LOAD_NEXT_ITEM); } @@ -1146,7 +1146,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback // exist, the layout will create a default view based on the firstView height. layout.onRemoteViewsLoaded( mCache.getMetaData().getLoadingTemplate(mContext).remoteViews, - mRemoteViewsOnClickHandler, + mRemoteViewsInteractionHandler, false); mRequestedViews.add(position, layout); mCache.queueRequestedPositionToLoad(position); diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java index b80fe4871616..827d03317d6a 100644 --- a/core/java/android/widget/RemoteViewsListAdapter.java +++ b/core/java/android/widget/RemoteViewsListAdapter.java @@ -31,12 +31,14 @@ public class RemoteViewsListAdapter extends BaseAdapter { private ArrayList<RemoteViews> mRemoteViewsList; private ArrayList<Integer> mViewTypes = new ArrayList<Integer>(); private int mViewTypeCount; + private RemoteViews.ColorResources mColorResources; public RemoteViewsListAdapter(Context context, ArrayList<RemoteViews> remoteViews, - int viewTypeCount) { + int viewTypeCount, RemoteViews.ColorResources colorResources) { mContext = context; mRemoteViewsList = remoteViews; mViewTypeCount = viewTypeCount; + mColorResources = colorResources; init(); } @@ -90,9 +92,10 @@ public class RemoteViewsListAdapter extends BaseAdapter { if (convertView != null && rv != null && convertView.getId() == rv.getLayoutId()) { v = convertView; - rv.reapply(mContext, v); + rv.reapply(mContext, v, null /* handler */, null /* size */, mColorResources); } else { - v = rv.apply(mContext, parent); + v = rv.apply(mContext, parent, null /* handler */, null /* size */, + mColorResources); } return v; } else { diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 64d09de49f2d..65f3da79afe0 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -335,6 +335,27 @@ public class ScrollView extends FrameLayout { } /** + * Returns the {@link EdgeEffect#getType()} for the edge effects. + * @return the {@link EdgeEffect#getType()} for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + @EdgeEffect.EdgeEffectType + public int getEdgeEffectType() { + return mEdgeGlowTop.getType(); + } + + /** + * Sets the {@link EdgeEffect#setType(int)} for the edge effects. + * @param type The edge effect type to use for the edge effects. + * @attr ref android.R.styleable#EdgeEffect_edgeEffectType + */ + public void setEdgeEffectType(@EdgeEffect.EdgeEffectType int type) { + mEdgeGlowTop.setType(type); + mEdgeGlowBottom.setType(type); + invalidate(); + } + + /** * @return The maximum amount this scroll view will scroll in response to * an arrow event. */ diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 0f2089a5463f..ca0747fadf14 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4756,6 +4756,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see #getJustificationMode() */ @Layout.JustificationMode + @android.view.RemotableViewMethod public void setJustificationMode(@Layout.JustificationMode int justificationMode) { mJustificationMode = justificationMode; if (mLayout != null) { @@ -5232,6 +5233,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see android.view.Gravity * @attr ref android.R.styleable#TextView_gravity */ + @android.view.RemotableViewMethod public void setGravity(int gravity) { if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) { gravity |= Gravity.START; @@ -5826,6 +5828,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @attr ref android.R.styleable#TextView_lineHeight */ + @android.view.RemotableViewMethod public void setLineHeight(@Px @IntRange(from = 0) int lineHeight) { Preconditions.checkArgumentNonnegative(lineHeight); @@ -10277,6 +10280,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see #setTransformationMethod(TransformationMethod) * @attr ref android.R.styleable#TextView_textAllCaps */ + @android.view.RemotableViewMethod public void setAllCaps(boolean allCaps) { if (allCaps) { setTransformationMethod(new AllCapsTransformationMethod(getContext())); diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java index 2904a8c889a2..0f2a3ca6936b 100644 --- a/core/java/android/widget/ToastPresenter.java +++ b/core/java/android/widget/ToastPresenter.java @@ -184,7 +184,7 @@ public class ToastPresenter { mParams.y = yOffset; mParams.horizontalMargin = horizontalMargin; mParams.verticalMargin = verticalMargin; - addToastView(); + mView.setLayoutParams(mParams); } /** diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java index e22a5eb9fe7b..acf9882e6658 100644 --- a/core/java/android/window/ClientWindowFrames.java +++ b/core/java/android/window/ClientWindowFrames.java @@ -58,9 +58,9 @@ public class ClientWindowFrames implements Parcelable { /** Needed for AIDL out parameters. */ public void readFromParcel(Parcel in) { - frame.set(Rect.CREATOR.createFromParcel(in)); - displayFrame.set(Rect.CREATOR.createFromParcel(in)); - backdropFrame.set(Rect.CREATOR.createFromParcel(in)); + frame.readFromParcel(in); + displayFrame.readFromParcel(in); + backdropFrame.readFromParcel(in); } @Override diff --git a/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl index cb280cd14180..f3759e091ed6 100644 --- a/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl +++ b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl @@ -18,5 +18,5 @@ package com.android.internal.app; // Iterface to observe op note/checks of ops oneway interface IAppOpsNotedCallback { - void opNoted(int op, int uid, String packageName, int flags, int mode); + void opNoted(int op, int uid, String packageName, String attributionTag, int flags, int mode); } diff --git a/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl b/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl index b0cb2a8ceb64..3a108e7e1d94 100644 --- a/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl +++ b/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl @@ -18,5 +18,5 @@ package com.android.internal.app; // Iterface to observe op starts oneway interface IAppOpsStartedCallback { - void opStarted(int op, int uid, String packageName, int flags, int mode); + void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode); } diff --git a/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java b/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java index 96dac565eb3d..402d7fed90c5 100644 --- a/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java +++ b/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java @@ -19,6 +19,7 @@ package com.android.internal.graphics.drawable; import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UiThread; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; @@ -31,12 +32,14 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RenderNode; import android.graphics.drawable.Drawable; -import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; -import android.view.SurfaceControl; +import android.util.LongSparseArray; import android.view.ViewRootImpl; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; /** * A drawable that keeps track of a blur region, pokes a hole under it, and propagates its state @@ -52,26 +55,40 @@ public final class BackgroundBlurDrawable extends Drawable { private final Paint mPaint = new Paint(); private final Path mRectPath = new Path(); private final float[] mTmpRadii = new float[8]; - private final SurfaceControl.BlurRegion mBlurRegion = new SurfaceControl.BlurRegion(); - // This will be called from a thread pool. - private final RenderNode.PositionUpdateListener mPositionUpdateListener = + private boolean mVisible = true; + + // Confined to UiThread. The values are copied into a BlurRegion, which lives on + // RenderThread to avoid interference with UiThread updates. + private int mBlurRadius; + private float mCornerRadiusTL; + private float mCornerRadiusTR; + private float mCornerRadiusBL; + private float mCornerRadiusBR; + private float mAlpha = 1; + + // Do not update from UiThread. This holds the latest position for this drawable. It is used + // by the Aggregator from RenderThread to get the final position of the blur region sent to SF + private final Rect mRect = new Rect(); + // This is called from a thread pool. The callbacks might come out of order w.r.t. the frame + // number, so we send a Runnable holding the actual update to the Aggregator. The Aggregator + // can apply the update on RenderThread when processing that same frame. + @VisibleForTesting + public final RenderNode.PositionUpdateListener mPositionUpdateListener = new RenderNode.PositionUpdateListener() { @Override public void positionChanged(long frameNumber, int left, int top, int right, int bottom) { - synchronized (mAggregator) { - mBlurRegion.rect.set(left, top, right, bottom); - mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion); - } + mAggregator.onRenderNodePositionChanged(frameNumber, () -> { + mRect.set(left, top, right, bottom); + }); } @Override public void positionLost(long frameNumber) { - synchronized (mAggregator) { - mBlurRegion.rect.setEmpty(); - mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion); - } + mAggregator.onRenderNodePositionChanged(frameNumber, () -> { + mRect.setEmpty(); + }); } }; @@ -79,6 +96,7 @@ public final class BackgroundBlurDrawable extends Drawable { mAggregator = aggregator; mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); mPaint.setColor(Color.TRANSPARENT); + mPaint.setAntiAlias(true); mRenderNode = new RenderNode("BackgroundBlurDrawable"); mRenderNode.addPositionUpdateListener(mPositionUpdateListener); } @@ -104,23 +122,30 @@ public final class BackgroundBlurDrawable extends Drawable { public boolean setVisible(boolean visible, boolean restart) { boolean changed = super.setVisible(visible, restart); if (changed) { - mBlurRegion.visible = visible; + mVisible = visible; + mAggregator.onBlurDrawableUpdated(this); } return changed; } @Override public void setAlpha(int alpha) { - mBlurRegion.alpha = alpha / 255f; - invalidateSelf(); + if (mAlpha != alpha / 255f) { + mAlpha = alpha / 255f; + invalidateSelf(); + mAggregator.onBlurDrawableUpdated(this); + } } /** * Blur radius in pixels. */ public void setBlurRadius(int blurRadius) { - mBlurRegion.blurRadius = blurRadius; - invalidateSelf(); + if (mBlurRadius != blurRadius) { + mBlurRadius = blurRadius; + invalidateSelf(); + mAggregator.onBlurDrawableUpdated(this); + } } /** @@ -139,14 +164,18 @@ public final class BackgroundBlurDrawable extends Drawable { */ public void setCornerRadius(float cornerRadiusTL, float cornerRadiusTR, float cornerRadiusBL, float cornerRadiusBR) { - synchronized (mAggregator) { - mBlurRegion.cornerRadiusTL = cornerRadiusTL; - mBlurRegion.cornerRadiusTR = cornerRadiusTR; - mBlurRegion.cornerRadiusBL = cornerRadiusBL; - mBlurRegion.cornerRadiusBR = cornerRadiusBR; + if (mCornerRadiusTL != cornerRadiusTL + || mCornerRadiusTR != cornerRadiusTR + || mCornerRadiusBL != cornerRadiusBL + || mCornerRadiusBR != cornerRadiusBR) { + mCornerRadiusTL = cornerRadiusTL; + mCornerRadiusTR = cornerRadiusTR; + mCornerRadiusBL = cornerRadiusBL; + mCornerRadiusBR = cornerRadiusBR; + updatePath(); + invalidateSelf(); + mAggregator.onBlurDrawableUpdated(this); } - updatePath(); - invalidateSelf(); } @Override @@ -157,12 +186,10 @@ public final class BackgroundBlurDrawable extends Drawable { } private void updatePath() { - synchronized (mAggregator) { - mTmpRadii[0] = mTmpRadii[1] = mBlurRegion.cornerRadiusTL; - mTmpRadii[2] = mTmpRadii[3] = mBlurRegion.cornerRadiusTR; - mTmpRadii[4] = mTmpRadii[5] = mBlurRegion.cornerRadiusBL; - mTmpRadii[6] = mTmpRadii[7] = mBlurRegion.cornerRadiusBR; - } + mTmpRadii[0] = mTmpRadii[1] = mCornerRadiusTL; + mTmpRadii[2] = mTmpRadii[3] = mCornerRadiusTR; + mTmpRadii[4] = mTmpRadii[5] = mCornerRadiusBL; + mTmpRadii[6] = mTmpRadii[7] = mCornerRadiusBR; mRectPath.reset(); if (getAlpha() == 0 || !isVisible()) { return; @@ -182,17 +209,32 @@ public final class BackgroundBlurDrawable extends Drawable { return PixelFormat.TRANSLUCENT; } + @Override + public String toString() { + return "BackgroundBlurDrawable{" + + "blurRadius=" + mBlurRadius + + ", corners={" + mCornerRadiusTL + + "," + mCornerRadiusTR + + "," + mCornerRadiusBL + + "," + mCornerRadiusBR + + "}, alpha=" + mAlpha + + ", visible=" + mVisible + + "}"; + } + /** * Responsible for keeping track of all blur regions of a {@link ViewRootImpl} and posting a * message when it's time to propagate them. */ public static final class Aggregator { - - private final ArrayMap<BackgroundBlurDrawable, SurfaceControl.BlurRegion> mBlurRegions = - new ArrayMap<>(); + private final Object mRtLock = new Object(); + // Maintains a list of all *visible* blur drawables. Confined to UI thread + private final ArraySet<BackgroundBlurDrawable> mDrawables = new ArraySet(); + @GuardedBy("mRtLock") + private final LongSparseArray<ArraySet<Runnable>> mFrameRtUpdates = new LongSparseArray(); private final ViewRootImpl mViewRoot; - private float[][] mTmpBlurRegionsArray; - private boolean mNeedsUpdate; + private BlurRegion[] mTmpBlurRegionsForFrame = new BlurRegion[0]; + private boolean mHasUiUpdates; public Aggregator(ViewRootImpl viewRoot) { mViewRoot = viewRoot; @@ -209,60 +251,191 @@ public final class BackgroundBlurDrawable extends Drawable { } /** - * Called from RenderThread only, already locked. - * @param drawable - * @param blurRegion + * Called when a BackgroundBlurDrawable has been updated */ - void onBlurRegionUpdated(BackgroundBlurDrawable drawable, - SurfaceControl.BlurRegion blurRegion) { - if (blurRegion.rect.isEmpty() || blurRegion.alpha == 0 || blurRegion.blurRadius == 0 - || !blurRegion.visible) { - mBlurRegions.remove(drawable); - mNeedsUpdate = true; - if (DEBUG) { - Log.d(TAG, "Remove " + blurRegion); + @UiThread + void onBlurDrawableUpdated(BackgroundBlurDrawable drawable) { + final boolean shouldBeDrawn = + drawable.mAlpha != 0 && drawable.mBlurRadius > 0 && drawable.mVisible; + final boolean isDrawn = mDrawables.contains(drawable); + if (shouldBeDrawn) { + mHasUiUpdates = true; + if (!isDrawn) { + mDrawables.add(drawable); + if (DEBUG) { + Log.d(TAG, "Add " + drawable); + } + } else { + if (DEBUG) { + Log.d(TAG, "Update " + drawable); + } } - } else { - mBlurRegions.put(drawable, blurRegion); - mNeedsUpdate = true; + } else if (!shouldBeDrawn && isDrawn) { + mHasUiUpdates = true; + mDrawables.remove(drawable); if (DEBUG) { - Log.d(TAG, "Update " + blurRegion); + Log.d(TAG, "Remove " + drawable); } } } + // Called from a thread pool + void onRenderNodePositionChanged(long frameNumber, Runnable update) { + // One of the blur region's position has changed, so we have to send an updated list + // of blur regions to SurfaceFlinger for this frame. + synchronized (mRtLock) { + ArraySet<Runnable> frameRtUpdates = mFrameRtUpdates.get(frameNumber); + if (frameRtUpdates == null) { + frameRtUpdates = new ArraySet<>(); + mFrameRtUpdates.put(frameNumber, frameRtUpdates); + } + frameRtUpdates.add(update); + } + } + + /** + * @return true if there are any updates that need to be sent to SF + */ + @UiThread + public boolean hasUpdates() { + return mHasUiUpdates; + } + /** - * If there are any blur regions visible on the screen at the moment. + * @return true if there are any visible blur regions */ + @UiThread public boolean hasRegions() { - return mBlurRegions.size() > 0; + return mDrawables.size() > 0; + } + + /** + * @return an array of BlurRegions, which are holding a copy of the information in + * all the currently visible BackgroundBlurDrawables + */ + @UiThread + public BlurRegion[] getBlurRegionsCopyForRT() { + if (mHasUiUpdates) { + mTmpBlurRegionsForFrame = new BlurRegion[mDrawables.size()]; + for (int i = 0; i < mDrawables.size(); i++) { + mTmpBlurRegionsForFrame[i] = new BlurRegion(mDrawables.valueAt(i)); + } + mHasUiUpdates = false; + } + + return mTmpBlurRegionsForFrame; } /** - * Dispatch blur updates, if there were any. - * @param frameNumber Frame where the update should happen. + * Called on RenderThread. + * + * @return all blur regions if there are any ui or position updates for this frame, + * null otherwise */ - public void dispatchBlurTransactionIfNeeded(long frameNumber) { - synchronized (this) { - if (!mNeedsUpdate) { - return; + @VisibleForTesting + public float[][] getBlurRegionsToDispatchToSf(long frameNumber, + BlurRegion[] blurRegionsForFrame, boolean hasUiUpdatesForFrame) { + synchronized (mRtLock) { + if (!hasUiUpdatesForFrame && (mFrameRtUpdates.size() == 0 + || mFrameRtUpdates.keyAt(0) > frameNumber)) { + return null; } - mNeedsUpdate = false; - if (mTmpBlurRegionsArray == null - || mTmpBlurRegionsArray.length != mBlurRegions.size()) { - mTmpBlurRegionsArray = new float[mBlurRegions.size()][]; + // mFrameRtUpdates holds position updates coming from a thread pool span from + // RenderThread. At this point, all position updates for frame frameNumber should + // have been added to mFrameRtUpdates. + // Here, we apply all updates for frames <= frameNumber in case some previous update + // has been missed. This also protects mFrameRtUpdates from memory leaks. + while (mFrameRtUpdates.size() != 0 && mFrameRtUpdates.keyAt(0) <= frameNumber) { + final ArraySet<Runnable> frameUpdates = mFrameRtUpdates.valueAt(0); + mFrameRtUpdates.removeAt(0); + for (int i = 0; i < frameUpdates.size(); i++) { + frameUpdates.valueAt(i).run(); + } } + } + + if (DEBUG) { + Log.d(TAG, "Dispatching " + blurRegionsForFrame.length + " blur regions:"); + } + + final float[][] blurRegionsArray = new float[blurRegionsForFrame.length][]; + for (int i = 0; i < blurRegionsArray.length; i++) { + blurRegionsArray[i] = blurRegionsForFrame[i].toFloatArray(); if (DEBUG) { - Log.d(TAG, "onBlurRegionUpdated will dispatch " + mTmpBlurRegionsArray.length - + " regions for frame " + frameNumber); - } - for (int i = 0; i < mTmpBlurRegionsArray.length; i++) { - mTmpBlurRegionsArray[i] = mBlurRegions.valueAt(i).toFloatArray(); + Log.d(TAG, blurRegionsForFrame[i].toString()); } + } + return blurRegionsArray; + } - mViewRoot.dispatchBlurRegions(mTmpBlurRegionsArray, frameNumber); + /** + * Called on RenderThread in FrameDrawingCallback. + * Dispatch all blur regions if there are any ui or position updates. + */ + public void dispatchBlurTransactionIfNeeded(long frameNumber, + BlurRegion[] blurRegionsForFrame, boolean hasUiUpdatesForFrame) { + final float[][] blurRegionsArray = getBlurRegionsToDispatchToSf(frameNumber, + blurRegionsForFrame, hasUiUpdatesForFrame); + if (blurRegionsArray != null) { + mViewRoot.dispatchBlurRegions(blurRegionsArray, frameNumber); } } + + } + + /** + * Wrapper for sending blur data to SurfaceFlinger + * Confined to RenderThread. + */ + public static final class BlurRegion { + public final int blurRadius; + public final float cornerRadiusTL; + public final float cornerRadiusTR; + public final float cornerRadiusBL; + public final float cornerRadiusBR; + public final float alpha; + public final Rect rect; + + BlurRegion(BackgroundBlurDrawable drawable) { + alpha = drawable.mAlpha; + blurRadius = drawable.mBlurRadius; + cornerRadiusTL = drawable.mCornerRadiusTL; + cornerRadiusTR = drawable.mCornerRadiusTR; + cornerRadiusBL = drawable.mCornerRadiusBL; + cornerRadiusBR = drawable.mCornerRadiusBR; + rect = drawable.mRect; + } + + /** + * Serializes this class into a float array that's more JNI friendly. + */ + float[] toFloatArray() { + final float[] floatArray = new float[10]; + floatArray[0] = blurRadius; + floatArray[1] = alpha; + floatArray[2] = rect.left; + floatArray[3] = rect.top; + floatArray[4] = rect.right; + floatArray[5] = rect.bottom; + floatArray[6] = cornerRadiusTL; + floatArray[7] = cornerRadiusTR; + floatArray[8] = cornerRadiusBL; + floatArray[9] = cornerRadiusBR; + return floatArray; + } + + @Override + public String toString() { + return "BlurRegion{" + + "blurRadius=" + blurRadius + + ", corners={" + cornerRadiusTL + + "," + cornerRadiusTR + + "," + cornerRadiusBL + + "," + cornerRadiusBR + + "}, alpha=" + alpha + + ", rect=" + rect + + "}"; + } } } diff --git a/core/java/com/android/internal/graphics/palette/CelebiQuantizer.java b/core/java/com/android/internal/graphics/palette/CelebiQuantizer.java new file mode 100644 index 000000000000..de6bf2044dbc --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/CelebiQuantizer.java @@ -0,0 +1,50 @@ +/* + * 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.internal.graphics.palette; + +import java.util.List; + +/** + * An implementation of Celebi's WSM quantizer, or, a Kmeans quantizer that starts with centroids + * from a Wu quantizer to ensure 100% reproducible and quality results, and has some optimizations + * to the Kmeans algorithm. + * + * See Celebi 2011, “Improving the Performance of K-Means for Color Quantization” + */ +public class CelebiQuantizer implements Quantizer { + private List<Palette.Swatch> mSwatches; + + public CelebiQuantizer() { } + + @Override + public void quantize(int[] pixels, int maxColors) { + WuQuantizer wu = new WuQuantizer(pixels, maxColors); + wu.quantize(pixels, maxColors); + List<Palette.Swatch> wuSwatches = wu.getQuantizedColors(); + LABCentroid labCentroidProvider = new LABCentroid(); + WSMeansQuantizer kmeans = + new WSMeansQuantizer(WSMeansQuantizer.createStartingCentroids(labCentroidProvider, + wuSwatches), labCentroidProvider, pixels, maxColors); + kmeans.quantize(pixels, maxColors); + mSwatches = kmeans.getQuantizedColors(); + } + + @Override + public List<Palette.Swatch> getQuantizedColors() { + return mSwatches; + } +} diff --git a/core/java/com/android/internal/graphics/palette/CentroidProvider.java b/core/java/com/android/internal/graphics/palette/CentroidProvider.java new file mode 100644 index 000000000000..5fcfcbab3159 --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/CentroidProvider.java @@ -0,0 +1,38 @@ +/* + * 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.internal.graphics.palette; + +import android.annotation.ColorInt; + +interface CentroidProvider { + /** + * @return 3 dimensions representing the color + */ + float[] getCentroid(@ColorInt int color); + + /** + * @param centroid 3 dimensions representing the color + * @return 32-bit ARGB representation + */ + @ColorInt + int getColor(float[] centroid); + + /** + * Distance between two centroids. + */ + float distance(float[] a, float[] b); +} diff --git a/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java index 9ac753b6d6ce..777949434c36 100644 --- a/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java +++ b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java @@ -35,6 +35,8 @@ package com.android.internal.graphics.palette; import android.graphics.Color; import android.util.TimingLogger; +import com.android.internal.graphics.palette.Palette.Swatch; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -42,9 +44,6 @@ import java.util.Comparator; import java.util.List; import java.util.PriorityQueue; -import com.android.internal.graphics.ColorUtils; -import com.android.internal.graphics.palette.Palette.Swatch; - /** * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/ * graphics/ColorCutQuantizer.java @@ -77,20 +76,17 @@ final class ColorCutQuantizer implements Quantizer { int[] mHistogram; List<Swatch> mQuantizedColors; TimingLogger mTimingLogger; - Palette.Filter[] mFilters; private final float[] mTempHsl = new float[3]; /** * Execute color quantization. * - * @param pixels histogram representing an image's pixel data + * @param pixels histogram representing an image's pixel data * @param maxColors The maximum number of colors that should be in the result palette. - * @param filters Set of filters to use in the quantization stage */ - public void quantize(final int[] pixels, final int maxColors, final Palette.Filter[] filters) { + public void quantize(final int[] pixels, final int maxColors) { mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null; - mFilters = filters; final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)]; for (int i = 0; i < pixels.length; i++) { @@ -108,10 +104,6 @@ final class ColorCutQuantizer implements Quantizer { // Now let's count the number of distinct colors int distinctColorCount = 0; for (int color = 0; color < hist.length; color++) { - if (hist[color] > 0 && shouldIgnoreColor(color)) { - // If we should ignore the color, set the population to 0 - hist[color] = 0; - } if (hist[color] > 0) { // If the color has population, increase the distinct color count distinctColorCount++; @@ -186,7 +178,7 @@ final class ColorCutQuantizer implements Quantizer { * and splitting them. Once split, the new box and the remaining box are offered back to the * queue. * - * @param queue {@link java.util.PriorityQueue} to poll for boxes + * @param queue {@link java.util.PriorityQueue} to poll for boxes * @param maxSize Maximum amount of boxes to split */ private void splitBoxes(final PriorityQueue<Vbox> queue, final int maxSize) { @@ -216,11 +208,7 @@ final class ColorCutQuantizer implements Quantizer { ArrayList<Swatch> colors = new ArrayList<>(vboxes.size()); for (Vbox vbox : vboxes) { Swatch swatch = vbox.getAverageColor(); - if (!shouldIgnoreColor(swatch)) { - // As we're averaging a color box, we can still get colors which we do not want, so - // we check again here - colors.add(swatch); - } + colors.add(swatch); } return colors; } @@ -230,7 +218,7 @@ final class ColorCutQuantizer implements Quantizer { */ private class Vbox { // lower and upper index are inclusive - private int mLowerIndex; + private final int mLowerIndex; private int mUpperIndex; // Population of colors within this box private int mPopulation; @@ -373,7 +361,7 @@ final class ColorCutQuantizer implements Quantizer { modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex); final int midPoint = mPopulation / 2; - for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++) { + for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++) { count += hist[colors[i]]; if (count >= midPoint) { // we never want to split on the upperIndex, as this will result in the same @@ -447,27 +435,6 @@ final class ColorCutQuantizer implements Quantizer { } } - private boolean shouldIgnoreColor(int color565) { - final int rgb = approximateToRgb888(color565); - ColorUtils.colorToHSL(rgb, mTempHsl); - return shouldIgnoreColor(rgb, mTempHsl); - } - - private boolean shouldIgnoreColor(Swatch color) { - return shouldIgnoreColor(color.getRgb(), color.getHsl()); - } - - private boolean shouldIgnoreColor(int rgb, float[] hsl) { - if (mFilters != null && mFilters.length > 0) { - for (int i = 0, count = mFilters.length; i < count; i++) { - if (!mFilters[i].isAllowed(rgb, hsl)) { - return true; - } - } - } - return false; - } - /** * Comparator which sorts {@link Vbox} instances based on their volume, in descending order */ @@ -498,7 +465,8 @@ final class ColorCutQuantizer implements Quantizer { } private static int approximateToRgb888(int color) { - return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color)); + return approximateToRgb888(quantizedRed(color), quantizedGreen(color), + quantizedBlue(color)); } /** diff --git a/core/java/com/android/internal/graphics/palette/Contrast.java b/core/java/com/android/internal/graphics/palette/Contrast.java new file mode 100644 index 000000000000..3dd1b8d8117c --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/Contrast.java @@ -0,0 +1,103 @@ +/* + * 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.internal.graphics.palette; + +/** + * Helper methods for determining contrast between two colors, either via the colors themselves + * or components in different color spaces. + */ +public class Contrast { + /** + * + * @param y Y in XYZ that contrasts with the returned Y value + * @param contrast contrast ratio between color argument and returned Y value. Must be >= 1 + * or an exception will be thrown + * @return the lower Y coordinate in XYZ space that contrasts with color, or -1 if reaching + * no Y coordinate reaches contrast with color. + */ + public static float lighterY(float y, float contrast) { + assert (contrast >= 1); + float answer = -5 + contrast * (5 + y); + if (answer > 100.0) { + return -1; + } + return answer; + } + + + /** + * @param y Y in XYZ that contrasts with the returned Y value + * @param contrast contrast ratio between color argument and returned Y value. Must be >= 1 + * or an exception will be thrown + * @return the lower Y coordinate in XYZ space that contrasts with color, or -1 if reaching + * no Y coordinate reaches contrast with color. + */ + public static float darkerY(float y, float contrast) { + assert (contrast >= 1); + float answer = (5 - 5 * contrast + y) / contrast; + if (answer < 0.0) { + return -1; + } + return answer; + } + + /** + * Convert L* in L*a*b* to Y in XYZ. + * + * @param lstar L* in L*a*b* + * @return Y in XYZ + */ + public static float lstarToY(float lstar) { + // http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html + float ke = 8.0f; + if (lstar > ke) { + return (float) (Math.pow(((lstar + 16.0) / 116.0), 3) * 100.0); + } else { + return (float) (lstar / (24389 / 27) * 100.0); + } + } + + /** + * Convert Y in XYZ to L* in L*a*b*. + * + * @param y Y in XYZ + * @return L* in L*a*b* + */ + public static float yToLstar(float y) { + y = y / 100.0f; + float e = 216.0f / 24389.0f; + float y_intermediate; + if (y <= e) { + y_intermediate = (24389.f / 27.f) * y; + // If y < e, can skip consecutive steps of / 116 + 16 followed by * 116 - 16. + return y_intermediate; + } else { + y_intermediate = (float) Math.cbrt(y); + } + return 116.f * y_intermediate - 16.f; + } + + + /** + * @return Contrast ratio between two Y values in XYZ space. + */ + public static float contrastYs(float y1, float y2) { + final float lighter = Math.max(y1, y2); + final float darker = (lighter == y1) ? y2 : y1; + return (lighter + 5) / (darker + 5); + } +} diff --git a/core/java/com/android/internal/graphics/palette/LABCentroid.java b/core/java/com/android/internal/graphics/palette/LABCentroid.java new file mode 100644 index 000000000000..98d5d2684857 --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/LABCentroid.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.internal.graphics.palette; + +import android.graphics.Color; +import android.graphics.ColorSpace; + +/** + * Allows quantizers to operate in the L*a*b* colorspace. + * L*a*b* is a good choice for measuring distance between colors. + * Better spaces, and better distance calculations even in L*a*b* exist, but measuring distance + * in L*a*b* space, also known as deltaE, is a universally accepted standard across industries + * and worldwide. + */ +public class LABCentroid implements CentroidProvider { + final ColorSpace.Connector mRgbToLab; + final ColorSpace.Connector mLabToRgb; + + public LABCentroid() { + mRgbToLab = ColorSpace.connect( + ColorSpace.get(ColorSpace.Named.SRGB), + ColorSpace.get(ColorSpace.Named.CIE_LAB)); + mLabToRgb = ColorSpace.connect(ColorSpace.get(ColorSpace.Named.CIE_LAB), + ColorSpace.get(ColorSpace.Named.SRGB)); + } + + @Override + public float[] getCentroid(int color) { + float r = Color.red(color) / 255.f; + float g = Color.green(color) / 255.f; + float b = Color.blue(color) / 255.f; + + float[] transform = mRgbToLab.transform(r, g, b); + return transform; + } + + @Override + public int getColor(float[] centroid) { + float[] rgb = mLabToRgb.transform(centroid); + int color = Color.rgb(rgb[0], rgb[1], rgb[2]); + return color; + } + + @Override + public float distance(float[] a, float[] b) { + // Standard v1 CIELAB deltaE formula, 1976 - easily improved upon, however, + // improvements do not significantly impact the Palette algorithm's results. + double dL = a[0] - b[0]; + double dA = a[1] - b[1]; + double dB = a[2] - b[2]; + return (float) (Math.pow(dL, 2) + Math.pow(dA, 2) + Math.pow(dB, 2)); + } +} diff --git a/core/java/com/android/internal/graphics/palette/Mean.java b/core/java/com/android/internal/graphics/palette/Mean.java new file mode 100644 index 000000000000..894f91b6261c --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/Mean.java @@ -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 com.android.internal.graphics.palette; + +import java.util.Random; + +/** + * Represents a centroid in Kmeans algorithms. + */ +public class Mean { + private static final Random RANDOM = new Random(0); + + public float[] center; + + /** + * Constructor. + * + * @param upperBound maximum value of a dimension in the space Kmeans is optimizing in + */ + Mean(int upperBound) { + center = + new float[]{ + RANDOM.nextInt(upperBound + 1), RANDOM.nextInt(upperBound + 1), + RANDOM.nextInt(upperBound + 1) + }; + } + + Mean(float[] center) { + this.center = center; + } +} diff --git a/core/java/com/android/internal/graphics/palette/MeanBucket.java b/core/java/com/android/internal/graphics/palette/MeanBucket.java new file mode 100644 index 000000000000..ae8858a8107c --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/MeanBucket.java @@ -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. + */ + +package com.android.internal.graphics.palette; + +import java.util.HashSet; +import java.util.Set; + +class MeanBucket { + float[] mTotal = {0.f, 0.f, 0.f}; + int mCount = 0; + Set<Integer> mColors = new HashSet<>(); + + void add(float[] colorAsDoubles, int color, int colorCount) { + assert (colorAsDoubles.length == 3); + mColors.add(color); + mTotal[0] += (colorAsDoubles[0] * colorCount); + mTotal[1] += (colorAsDoubles[1] * colorCount); + mTotal[2] += (colorAsDoubles[2] * colorCount); + mCount += colorCount; + } + + float[] getCentroid() { + if (mCount == 0) { + return null; + } + return new float[]{mTotal[0] / mCount, mTotal[1] / mCount, mTotal[2] / mCount}; + } +} diff --git a/core/java/com/android/internal/graphics/palette/OWNERS b/core/java/com/android/internal/graphics/palette/OWNERS new file mode 100644 index 000000000000..731dca9b128f --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 484670
+dupin@google.com
+jamesoleary@google.com
\ No newline at end of file diff --git a/core/java/com/android/internal/graphics/palette/Palette.java b/core/java/com/android/internal/graphics/palette/Palette.java index a4f9a596050c..8b1137d7de7c 100644 --- a/core/java/com/android/internal/graphics/palette/Palette.java +++ b/core/java/com/android/internal/graphics/palette/Palette.java @@ -19,48 +19,24 @@ package com.android.internal.graphics.palette; import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.Px; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Rect; -import android.os.AsyncTask; -import android.util.ArrayMap; import android.util.Log; -import android.util.SparseBooleanArray; -import android.util.TimingLogger; -import com.android.internal.graphics.ColorUtils; - -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; /** - * Copied from: /frameworks/support/v7/palette/src/main/java/android/support/v7/ - * graphics/Palette.java - * * A helper class to extract prominent colors from an image. - * <p> - * A number of colors with different profiles are extracted from the image: - * <ul> - * <li>Vibrant</li> - * <li>Vibrant Dark</li> - * <li>Vibrant Light</li> - * <li>Muted</li> - * <li>Muted Dark</li> - * <li>Muted Light</li> - * </ul> - * These can be retrieved from the appropriate getter method. * - * <p> - * Instances are created with a {@link Palette.Builder} which supports several options to tweak the + * <p>Instances are created with a {@link Builder} which supports several options to tweak the * generated Palette. See that class' documentation for more information. - * <p> - * Generation should always be completed on a background thread, ideally the one in - * which you load your image on. {@link Palette.Builder} supports both synchronous and asynchronous - * generation: + * + * <p>Generation should always be completed on a background thread, ideally the one in which you + * load your image on. {@link Builder} supports both synchronous and asynchronous generation: * * <pre> * // Synchronous @@ -85,346 +61,59 @@ public final class Palette { /** * Called when the {@link Palette} has been generated. */ - void onGenerated(Palette palette); + void onGenerated(@Nullable Palette palette); } static final int DEFAULT_RESIZE_BITMAP_AREA = 112 * 112; static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16; - - static final float MIN_CONTRAST_TITLE_TEXT = 3.0f; - static final float MIN_CONTRAST_BODY_TEXT = 4.5f; - static final String LOG_TAG = "Palette"; - static final boolean LOG_TIMINGS = false; - /** - * Start generating a {@link Palette} with the returned {@link Palette.Builder} instance. - */ - public static Palette.Builder from(Bitmap bitmap) { - return new Palette.Builder(bitmap); + /** Start generating a {@link Palette} with the returned {@link Builder} instance. */ + @NonNull + public static Builder from(@NonNull Bitmap bitmap, @NonNull Quantizer quantizer) { + return new Builder(bitmap, quantizer); } /** * Generate a {@link Palette} from the pre-generated list of {@link Palette.Swatch} swatches. - * This is useful for testing, or if you want to resurrect a {@link Palette} instance from a - * list of swatches. Will return null if the {@code swatches} is null. - */ - public static Palette from(List<Palette.Swatch> swatches) { - return new Palette.Builder(swatches).generate(); - } - - /** - * @deprecated Use {@link Palette.Builder} to generate the Palette. - */ - @Deprecated - public static Palette generate(Bitmap bitmap) { - return from(bitmap).generate(); - } - - /** - * @deprecated Use {@link Palette.Builder} to generate the Palette. - */ - @Deprecated - public static Palette generate(Bitmap bitmap, int numColors) { - return from(bitmap).maximumColorCount(numColors).generate(); - } - - /** - * @deprecated Use {@link Palette.Builder} to generate the Palette. + * This + * is useful for testing, or if you want to resurrect a {@link Palette} instance from a list of + * swatches. Will return null if the {@code swatches} is null. */ - @Deprecated - public static AsyncTask<Bitmap, Void, Palette> generateAsync( - Bitmap bitmap, Palette.PaletteAsyncListener listener) { - return from(bitmap).generate(listener); - } - - /** - * @deprecated Use {@link Palette.Builder} to generate the Palette. - */ - @Deprecated - public static AsyncTask<Bitmap, Void, Palette> generateAsync( - final Bitmap bitmap, final int numColors, final Palette.PaletteAsyncListener listener) { - return from(bitmap).maximumColorCount(numColors).generate(listener); + @NonNull + public static Palette from(@NonNull List<Swatch> swatches) { + return new Builder(swatches).generate(); } - private final List<Palette.Swatch> mSwatches; - private final List<Target> mTargets; + private final List<Swatch> mSwatches; - private final Map<Target, Palette.Swatch> mSelectedSwatches; - private final SparseBooleanArray mUsedColors; - private final Palette.Swatch mDominantSwatch; + @Nullable + private final Swatch mDominantSwatch; - Palette(List<Palette.Swatch> swatches, List<Target> targets) { + Palette(List<Swatch> swatches) { mSwatches = swatches; - mTargets = targets; - - mUsedColors = new SparseBooleanArray(); - mSelectedSwatches = new ArrayMap<>(); - mDominantSwatch = findDominantSwatch(); } - /** - * Returns all of the swatches which make up the palette. - */ + /** Returns all of the swatches which make up the palette. */ @NonNull - public List<Palette.Swatch> getSwatches() { + public List<Swatch> getSwatches() { return Collections.unmodifiableList(mSwatches); } - /** - * Returns the targets used to generate this palette. - */ - @NonNull - public List<Target> getTargets() { - return Collections.unmodifiableList(mTargets); - } - - /** - * Returns the most vibrant swatch in the palette. Might be null. - * - * @see Target#VIBRANT - */ - @Nullable - public Palette.Swatch getVibrantSwatch() { - return getSwatchForTarget(Target.VIBRANT); - } - - /** - * Returns a light and vibrant swatch from the palette. Might be null. - * - * @see Target#LIGHT_VIBRANT - */ - @Nullable - public Palette.Swatch getLightVibrantSwatch() { - return getSwatchForTarget(Target.LIGHT_VIBRANT); - } - - /** - * Returns a dark and vibrant swatch from the palette. Might be null. - * - * @see Target#DARK_VIBRANT - */ - @Nullable - public Palette.Swatch getDarkVibrantSwatch() { - return getSwatchForTarget(Target.DARK_VIBRANT); - } - - /** - * Returns a muted swatch from the palette. Might be null. - * - * @see Target#MUTED - */ - @Nullable - public Palette.Swatch getMutedSwatch() { - return getSwatchForTarget(Target.MUTED); - } - - /** - * Returns a muted and light swatch from the palette. Might be null. - * - * @see Target#LIGHT_MUTED - */ - @Nullable - public Palette.Swatch getLightMutedSwatch() { - return getSwatchForTarget(Target.LIGHT_MUTED); - } - - /** - * Returns a muted and dark swatch from the palette. Might be null. - * - * @see Target#DARK_MUTED - */ + /** Returns the swatch with the highest population, or null if there are no swatches. */ @Nullable - public Palette.Swatch getDarkMutedSwatch() { - return getSwatchForTarget(Target.DARK_MUTED); - } - - /** - * Returns the most vibrant color in the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getVibrantSwatch() - */ - @ColorInt - public int getVibrantColor(@ColorInt final int defaultColor) { - return getColorForTarget(Target.VIBRANT, defaultColor); - } - - /** - * Returns a light and vibrant color from the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getLightVibrantSwatch() - */ - @ColorInt - public int getLightVibrantColor(@ColorInt final int defaultColor) { - return getColorForTarget(Target.LIGHT_VIBRANT, defaultColor); - } - - /** - * Returns a dark and vibrant color from the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getDarkVibrantSwatch() - */ - @ColorInt - public int getDarkVibrantColor(@ColorInt final int defaultColor) { - return getColorForTarget(Target.DARK_VIBRANT, defaultColor); - } - - /** - * Returns a muted color from the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getMutedSwatch() - */ - @ColorInt - public int getMutedColor(@ColorInt final int defaultColor) { - return getColorForTarget(Target.MUTED, defaultColor); - } - - /** - * Returns a muted and light color from the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getLightMutedSwatch() - */ - @ColorInt - public int getLightMutedColor(@ColorInt final int defaultColor) { - return getColorForTarget(Target.LIGHT_MUTED, defaultColor); - } - - /** - * Returns a muted and dark color from the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getDarkMutedSwatch() - */ - @ColorInt - public int getDarkMutedColor(@ColorInt final int defaultColor) { - return getColorForTarget(Target.DARK_MUTED, defaultColor); - } - - /** - * Returns the selected swatch for the given target from the palette, or {@code null} if one - * could not be found. - */ - @Nullable - public Palette.Swatch getSwatchForTarget(@NonNull final Target target) { - return mSelectedSwatches.get(target); - } - - /** - * Returns the selected color for the given target from the palette as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - */ - @ColorInt - public int getColorForTarget(@NonNull final Target target, @ColorInt final int defaultColor) { - Palette.Swatch swatch = getSwatchForTarget(target); - return swatch != null ? swatch.getRgb() : defaultColor; - } - - /** - * Returns the dominant swatch from the palette. - * - * <p>The dominant swatch is defined as the swatch with the greatest population (frequency) - * within the palette.</p> - */ - @Nullable - public Palette.Swatch getDominantSwatch() { + public Swatch getDominantSwatch() { return mDominantSwatch; } - /** - * Returns the color of the dominant swatch from the palette, as an RGB packed int. - * - * @param defaultColor value to return if the swatch isn't available - * @see #getDominantSwatch() - */ - @ColorInt - public int getDominantColor(@ColorInt int defaultColor) { - return mDominantSwatch != null ? mDominantSwatch.getRgb() : defaultColor; - } - - void generate() { - // We need to make sure that the scored targets are generated first. This is so that - // inherited targets have something to inherit from - for (int i = 0, count = mTargets.size(); i < count; i++) { - final Target target = mTargets.get(i); - target.normalizeWeights(); - mSelectedSwatches.put(target, generateScoredTarget(target)); - } - // We now clear out the used colors - mUsedColors.clear(); - } - - private Palette.Swatch generateScoredTarget(final Target target) { - final Palette.Swatch maxScoreSwatch = getMaxScoredSwatchForTarget(target); - if (maxScoreSwatch != null && target.isExclusive()) { - // If we have a swatch, and the target is exclusive, add the color to the used list - mUsedColors.append(maxScoreSwatch.getRgb(), true); - } - return maxScoreSwatch; - } - - private Palette.Swatch getMaxScoredSwatchForTarget(final Target target) { - float maxScore = 0; - Palette.Swatch maxScoreSwatch = null; - for (int i = 0, count = mSwatches.size(); i < count; i++) { - final Palette.Swatch swatch = mSwatches.get(i); - if (shouldBeScoredForTarget(swatch, target)) { - final float score = generateScore(swatch, target); - if (maxScoreSwatch == null || score > maxScore) { - maxScoreSwatch = swatch; - maxScore = score; - } - } - } - return maxScoreSwatch; - } - - private boolean shouldBeScoredForTarget(final Palette.Swatch swatch, final Target target) { - // Check whether the HSL values are within the correct ranges, and this color hasn't - // been used yet. - final float hsl[] = swatch.getHsl(); - return hsl[1] >= target.getMinimumSaturation() && hsl[1] <= target.getMaximumSaturation() - && hsl[2] >= target.getMinimumLightness() && hsl[2] <= target.getMaximumLightness() - && !mUsedColors.get(swatch.getRgb()); - } - - private float generateScore(Palette.Swatch swatch, Target target) { - final float[] hsl = swatch.getHsl(); - - float saturationScore = 0; - float luminanceScore = 0; - float populationScore = 0; - - final int maxPopulation = mDominantSwatch != null ? mDominantSwatch.getPopulation() : 1; - - if (target.getSaturationWeight() > 0) { - saturationScore = target.getSaturationWeight() - * (1f - Math.abs(hsl[1] - target.getTargetSaturation())); - } - if (target.getLightnessWeight() > 0) { - luminanceScore = target.getLightnessWeight() - * (1f - Math.abs(hsl[2] - target.getTargetLightness())); - } - if (target.getPopulationWeight() > 0) { - populationScore = target.getPopulationWeight() - * (swatch.getPopulation() / (float) maxPopulation); - } - - return saturationScore + luminanceScore + populationScore; - } - - private Palette.Swatch findDominantSwatch() { + @Nullable + private Swatch findDominantSwatch() { int maxPop = Integer.MIN_VALUE; - Palette.Swatch maxSwatch = null; + Swatch maxSwatch = null; for (int i = 0, count = mSwatches.size(); i < count; i++) { - Palette.Swatch swatch = mSwatches.get(i); + Swatch swatch = mSwatches.get(i); if (swatch.getPopulation() > maxPop) { maxSwatch = swatch; maxPop = swatch.getPopulation(); @@ -433,148 +122,42 @@ public final class Palette { return maxSwatch; } - private static float[] copyHslValues(Palette.Swatch color) { - final float[] newHsl = new float[3]; - System.arraycopy(color.getHsl(), 0, newHsl, 0, 3); - return newHsl; - } - /** * Represents a color swatch generated from an image's palette. The RGB color can be retrieved - * by calling {@link #getRgb()}. + * by + * calling {@link #getInt()}. */ - public static final class Swatch { - private final int mRed, mGreen, mBlue; - private final int mRgb; + public static class Swatch { + private final Color mColor; private final int mPopulation; - private boolean mGeneratedTextColors; - private int mTitleTextColor; - private int mBodyTextColor; - - private float[] mHsl; - - public Swatch(@ColorInt int color, int population) { - mRed = Color.red(color); - mGreen = Color.green(color); - mBlue = Color.blue(color); - mRgb = color; - mPopulation = population; - } - Swatch(int red, int green, int blue, int population) { - mRed = red; - mGreen = green; - mBlue = blue; - mRgb = Color.rgb(red, green, blue); + public Swatch(@ColorInt int colorInt, int population) { + mColor = Color.valueOf(colorInt); mPopulation = population; } - Swatch(float[] hsl, int population) { - this(ColorUtils.HSLToColor(hsl), population); - mHsl = hsl; - } - - /** - * @return this swatch's RGB color value - */ + /** @return this swatch's RGB color value */ @ColorInt - public int getRgb() { - return mRgb; + public int getInt() { + return mColor.toArgb(); } - /** - * Return this swatch's HSL values. - * hsv[0] is Hue [0 .. 360) - * hsv[1] is Saturation [0...1] - * hsv[2] is Lightness [0...1] - */ - public float[] getHsl() { - if (mHsl == null) { - mHsl = new float[3]; - } - ColorUtils.RGBToHSL(mRed, mGreen, mBlue, mHsl); - return mHsl; - } - - /** - * @return the number of pixels represented by this swatch - */ + /** @return the number of pixels represented by this swatch */ public int getPopulation() { return mPopulation; } - /** - * Returns an appropriate color to use for any 'title' text which is displayed over this - * {@link Palette.Swatch}'s color. This color is guaranteed to have sufficient contrast. - */ - @ColorInt - public int getTitleTextColor() { - ensureTextColorsGenerated(); - return mTitleTextColor; - } - - /** - * Returns an appropriate color to use for any 'body' text which is displayed over this - * {@link Palette.Swatch}'s color. This color is guaranteed to have sufficient contrast. - */ - @ColorInt - public int getBodyTextColor() { - ensureTextColorsGenerated(); - return mBodyTextColor; - } - - private void ensureTextColorsGenerated() { - if (!mGeneratedTextColors) { - // First check white, as most colors will be dark - final int lightBodyAlpha = ColorUtils.calculateMinimumAlpha( - Color.WHITE, mRgb, MIN_CONTRAST_BODY_TEXT); - final int lightTitleAlpha = ColorUtils.calculateMinimumAlpha( - Color.WHITE, mRgb, MIN_CONTRAST_TITLE_TEXT); - - if (lightBodyAlpha != -1 && lightTitleAlpha != -1) { - // If we found valid light values, use them and return - mBodyTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha); - mTitleTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha); - mGeneratedTextColors = true; - return; - } - - final int darkBodyAlpha = ColorUtils.calculateMinimumAlpha( - Color.BLACK, mRgb, MIN_CONTRAST_BODY_TEXT); - final int darkTitleAlpha = ColorUtils.calculateMinimumAlpha( - Color.BLACK, mRgb, MIN_CONTRAST_TITLE_TEXT); - - if (darkBodyAlpha != -1 && darkTitleAlpha != -1) { - // If we found valid dark values, use them and return - mBodyTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha); - mTitleTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha); - mGeneratedTextColors = true; - return; - } - - // If we reach here then we can not find title and body values which use the same - // lightness, we need to use mismatched values - mBodyTextColor = lightBodyAlpha != -1 - ? ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha) - : ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha); - mTitleTextColor = lightTitleAlpha != -1 - ? ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha) - : ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha); - mGeneratedTextColors = true; - } - } - @Override public String toString() { return new StringBuilder(getClass().getSimpleName()) - .append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']') - .append(" [HSL: ").append(Arrays.toString(getHsl())).append(']') - .append(" [Population: ").append(mPopulation).append(']') - .append(" [Title Text: #").append(Integer.toHexString(getTitleTextColor())) + .append(" [") + .append(mColor) + .append(']') + .append(" [Population: ") + .append(mPopulation) .append(']') - .append(" [Body Text: #").append(Integer.toHexString(getBodyTextColor())) - .append(']').toString(); + .toString(); } @Override @@ -586,243 +169,168 @@ public final class Palette { return false; } - Palette.Swatch - swatch = (Palette.Swatch) o; - return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb; + Swatch swatch = (Swatch) o; + return mPopulation == swatch.mPopulation && mColor.toArgb() == swatch.mColor.toArgb(); } @Override public int hashCode() { - return 31 * mRgb + mPopulation; + return 31 * mColor.toArgb() + mPopulation; } } - /** - * Builder class for generating {@link Palette} instances. - */ - public static final class Builder { - private final List<Palette.Swatch> mSwatches; + /** Builder class for generating {@link Palette} instances. */ + public static class Builder { + @Nullable + private final List<Swatch> mSwatches; + @Nullable private final Bitmap mBitmap; + @Nullable + private Quantizer mQuantizer = new ColorCutQuantizer(); - private final List<Target> mTargets = new ArrayList<>(); private int mMaxColors = DEFAULT_CALCULATE_NUMBER_COLORS; private int mResizeArea = DEFAULT_RESIZE_BITMAP_AREA; private int mResizeMaxDimension = -1; - private final List<Palette.Filter> mFilters = new ArrayList<>(); + @Nullable private Rect mRegion; - private Quantizer mQuantizer; - - /** - * Construct a new {@link Palette.Builder} using a source {@link Bitmap} - */ - public Builder(Bitmap bitmap) { + /** Construct a new {@link Builder} using a source {@link Bitmap} */ + public Builder(@NonNull Bitmap bitmap, @NonNull Quantizer quantizer) { if (bitmap == null || bitmap.isRecycled()) { throw new IllegalArgumentException("Bitmap is not valid"); } - mFilters.add(DEFAULT_FILTER); - mBitmap = bitmap; mSwatches = null; - - // Add the default targets - mTargets.add(Target.LIGHT_VIBRANT); - mTargets.add(Target.VIBRANT); - mTargets.add(Target.DARK_VIBRANT); - mTargets.add(Target.LIGHT_MUTED); - mTargets.add(Target.MUTED); - mTargets.add(Target.DARK_MUTED); + mBitmap = bitmap; + mQuantizer = quantizer == null ? new ColorCutQuantizer() : quantizer; } /** - * Construct a new {@link Palette.Builder} using a list of {@link Palette.Swatch} instances. - * Typically only used for testing. + * Construct a new {@link Builder} using a list of {@link Swatch} instances. Typically only + * used + * for testing. */ - public Builder(List<Palette.Swatch> swatches) { + public Builder(@NonNull List<Swatch> swatches) { if (swatches == null || swatches.isEmpty()) { throw new IllegalArgumentException("List of Swatches is not valid"); } - mFilters.add(DEFAULT_FILTER); mSwatches = swatches; mBitmap = null; + mQuantizer = null; } /** - * Set the maximum number of colors to use in the quantization step when using a - * {@link android.graphics.Bitmap} as the source. - * <p> - * Good values for depend on the source image type. For landscapes, good values are in - * the range 10-16. For images which are largely made up of people's faces then this - * value should be increased to ~24. + * Set the maximum number of colors to use in the quantization step when using a {@link + * android.graphics.Bitmap} as the source. + * + * <p>Good values for depend on the source image type. For landscapes, good values are in + * the + * range 10-16. For images which are largely made up of people's faces then this value + * should be + * increased to ~24. */ @NonNull - public Palette.Builder maximumColorCount(int colors) { + public Builder maximumColorCount(int colors) { mMaxColors = colors; return this; } /** - * Set the resize value when using a {@link android.graphics.Bitmap} as the source. - * If the bitmap's largest dimension is greater than the value specified, then the bitmap - * will be resized so that its largest dimension matches {@code maxDimension}. If the - * bitmap is smaller or equal, the original is used as-is. - * - * @deprecated Using {@link #resizeBitmapArea(int)} is preferred since it can handle - * abnormal aspect ratios more gracefully. + * Set the resize value when using a {@link android.graphics.Bitmap} as the source. If the + * bitmap's largest dimension is greater than the value specified, then the bitmap will be + * resized so that its largest dimension matches {@code maxDimension}. If the bitmap is + * smaller + * or equal, the original is used as-is. * * @param maxDimension the number of pixels that the max dimension should be scaled down to, - * or any value <= 0 to disable resizing. + * or + * any value <= 0 to disable resizing. + * @deprecated Using {@link #resizeBitmapArea(int)} is preferred since it can handle + * abnormal + * aspect ratios more gracefully. */ @NonNull @Deprecated - public Palette.Builder resizeBitmapSize(final int maxDimension) { + public Builder resizeBitmapSize(int maxDimension) { mResizeMaxDimension = maxDimension; mResizeArea = -1; return this; } /** - * Set the resize value when using a {@link android.graphics.Bitmap} as the source. - * If the bitmap's area is greater than the value specified, then the bitmap - * will be resized so that its area matches {@code area}. If the - * bitmap is smaller or equal, the original is used as-is. - * <p> - * This value has a large effect on the processing time. The larger the resized image is, - * the greater time it will take to generate the palette. The smaller the image is, the - * more detail is lost in the resulting image and thus less precision for color selection. + * Set the resize value when using a {@link android.graphics.Bitmap} as the source. If the + * bitmap's area is greater than the value specified, then the bitmap will be resized so + * that + * its area matches {@code area}. If the bitmap is smaller or equal, the original is used + * as-is. + * + * <p>This value has a large effect on the processing time. The larger the resized image is, + * the + * greater time it will take to generate the palette. The smaller the image is, the more + * detail + * is lost in the resulting image and thus less precision for color selection. * * @param area the number of pixels that the intermediary scaled down Bitmap should cover, - * or any value <= 0 to disable resizing. + * or + * any value <= 0 to disable resizing. */ @NonNull - public Palette.Builder resizeBitmapArea(final int area) { + public Builder resizeBitmapArea(int area) { mResizeArea = area; mResizeMaxDimension = -1; return this; } /** - * Clear all added filters. This includes any default filters added automatically by - * {@link Palette}. - */ - @NonNull - public Palette.Builder clearFilters() { - mFilters.clear(); - return this; - } - - /** - * Add a filter to be able to have fine grained control over which colors are - * allowed in the resulting palette. - * - * @param filter filter to add. - */ - @NonNull - public Palette.Builder addFilter( - Palette.Filter filter) { - if (filter != null) { - mFilters.add(filter); - } - return this; - } - - /** - * Set a specific quantization algorithm. {@link ColorCutQuantizer} will - * be used if unspecified. - * - * @param quantizer Quantizer implementation. - */ - @NonNull - public Palette.Builder setQuantizer(Quantizer quantizer) { - mQuantizer = quantizer; - return this; - } - - /** * Set a region of the bitmap to be used exclusively when calculating the palette. - * <p>This only works when the original input is a {@link Bitmap}.</p> * - * @param left The left side of the rectangle used for the region. - * @param top The top of the rectangle used for the region. - * @param right The right side of the rectangle used for the region. + * <p>This only works when the original input is a {@link Bitmap}. + * + * @param left The left side of the rectangle used for the region. + * @param top The top of the rectangle used for the region. + * @param right The right side of the rectangle used for the region. * @param bottom The bottom of the rectangle used for the region. */ @NonNull - public Palette.Builder setRegion(int left, int top, int right, int bottom) { + public Builder setRegion(@Px int left, @Px int top, @Px int right, @Px int bottom) { if (mBitmap != null) { if (mRegion == null) mRegion = new Rect(); // Set the Rect to be initially the whole Bitmap mRegion.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); // Now just get the intersection with the region if (!mRegion.intersect(left, top, right, bottom)) { - throw new IllegalArgumentException("The given region must intersect with " - + "the Bitmap's dimensions."); + throw new IllegalArgumentException( + "The given region must intersect with " + "the Bitmap's dimensions."); } } return this; } - /** - * Clear any previously region set via {@link #setRegion(int, int, int, int)}. - */ + /** Clear any previously region set via {@link #setRegion(int, int, int, int)}. */ @NonNull - public Palette.Builder clearRegion() { + public Builder clearRegion() { mRegion = null; return this; } - /** - * Add a target profile to be generated in the palette. - * - * <p>You can retrieve the result via {@link Palette#getSwatchForTarget(Target)}.</p> - */ - @NonNull - public Palette.Builder addTarget(@NonNull final Target target) { - if (!mTargets.contains(target)) { - mTargets.add(target); - } - return this; - } - /** - * Clear all added targets. This includes any default targets added automatically by - * {@link Palette}. - */ - @NonNull - public Palette.Builder clearTargets() { - if (mTargets != null) { - mTargets.clear(); - } - return this; - } - - /** - * Generate and return the {@link Palette} synchronously. - */ + /** Generate and return the {@link Palette} synchronously. */ @NonNull public Palette generate() { - final TimingLogger logger = LOG_TIMINGS - ? new TimingLogger(LOG_TAG, "Generation") - : null; - - List<Palette.Swatch> swatches; + List<Swatch> swatches; if (mBitmap != null) { // We have a Bitmap so we need to use quantization to reduce the number of colors // First we'll scale down the bitmap if needed - final Bitmap bitmap = scaleBitmapDown(mBitmap); - - if (logger != null) { - logger.addSplit("Processed Bitmap"); - } + Bitmap bitmap = scaleBitmapDown(mBitmap); - final Rect region = mRegion; + Rect region = mRegion; if (bitmap != mBitmap && region != null) { // If we have a scaled bitmap and a selected region, we need to scale down the // region to match the new scale - final double scale = bitmap.getWidth() / (double) mBitmap.getWidth(); + double scale = bitmap.getWidth() / (double) mBitmap.getWidth(); region.left = (int) Math.floor(region.left * scale); region.top = (int) Math.floor(region.top * scale); region.right = Math.min((int) Math.ceil(region.right * scale), @@ -832,54 +340,47 @@ public final class Palette { } // Now generate a quantizer from the Bitmap - if (mQuantizer == null) { - mQuantizer = new ColorCutQuantizer(); - } - mQuantizer.quantize(getPixelsFromBitmap(bitmap), - mMaxColors, mFilters.isEmpty() ? null : - mFilters.toArray(new Palette.Filter[mFilters.size()])); + mQuantizer.quantize( + getPixelsFromBitmap(bitmap), + mMaxColors); // If created a new bitmap, recycle it if (bitmap != mBitmap) { bitmap.recycle(); } - swatches = mQuantizer.getQuantizedColors(); - - if (logger != null) { - logger.addSplit("Color quantization completed"); - } - } else { + } else if (mSwatches != null) { // Else we're using the provided swatches swatches = mSwatches; + } else { + // The constructors enforce either a bitmap or swatches are present. + throw new AssertionError(); } // Now create a Palette instance - final Palette p = new Palette(swatches, mTargets); + Palette p = new Palette(swatches); // And make it generate itself - p.generate(); - - if (logger != null) { - logger.addSplit("Created Palette"); - logger.dumpToLog(); - } return p; } /** - * Generate the {@link Palette} asynchronously. The provided listener's - * {@link Palette.PaletteAsyncListener#onGenerated} method will be called with the palette when - * generated. + * Generate the {@link Palette} asynchronously. The provided listener's {@link + * PaletteAsyncListener#onGenerated} method will be called with the palette when generated. + * + * @deprecated Use the standard <code>java.util.concurrent</code> or <a + * href="https://developer.android.com/topic/libraries/architecture/coroutines">Kotlin + * concurrency utilities</a> to call {@link #generate()} instead. */ @NonNull - public AsyncTask<Bitmap, Void, Palette> generate(final Palette.PaletteAsyncListener listener) { - if (listener == null) { - throw new IllegalArgumentException("listener can not be null"); - } + @Deprecated + public android.os.AsyncTask<Bitmap, Void, Palette> generate( + @NonNull PaletteAsyncListener listener) { + assert (listener != null); - return new AsyncTask<Bitmap, Void, Palette>() { + return new android.os.AsyncTask<Bitmap, Void, Palette>() { @Override + @Nullable protected Palette doInBackground(Bitmap... params) { try { return generate(); @@ -890,16 +391,16 @@ public final class Palette { } @Override - protected void onPostExecute(Palette colorExtractor) { + protected void onPostExecute(@Nullable Palette colorExtractor) { listener.onGenerated(colorExtractor); } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mBitmap); + }.executeOnExecutor(android.os.AsyncTask.THREAD_POOL_EXECUTOR, mBitmap); } private int[] getPixelsFromBitmap(Bitmap bitmap) { - final int bitmapWidth = bitmap.getWidth(); - final int bitmapHeight = bitmap.getHeight(); - final int[] pixels = new int[bitmapWidth * bitmapHeight]; + int bitmapWidth = bitmap.getWidth(); + int bitmapHeight = bitmap.getHeight(); + int[] pixels = new int[bitmapWidth * bitmapHeight]; bitmap.getPixels(pixels, 0, bitmapWidth, 0, 0, bitmapWidth, bitmapHeight); if (mRegion == null) { @@ -908,32 +409,34 @@ public final class Palette { } else { // If we do have a region, lets create a subset array containing only the region's // pixels - final int regionWidth = mRegion.width(); - final int regionHeight = mRegion.height(); + int regionWidth = mRegion.width(); + int regionHeight = mRegion.height(); // pixels contains all of the pixels, so we need to iterate through each row and // copy the regions pixels into a new smaller array - final int[] subsetPixels = new int[regionWidth * regionHeight]; + int[] subsetPixels = new int[regionWidth * regionHeight]; for (int row = 0; row < regionHeight; row++) { - System.arraycopy(pixels, ((row + mRegion.top) * bitmapWidth) + mRegion.left, - subsetPixels, row * regionWidth, regionWidth); + System.arraycopy( + pixels, + ((row + mRegion.top) * bitmapWidth) + mRegion.left, + subsetPixels, + row * regionWidth, + regionWidth); } return subsetPixels; } } - /** - * Scale the bitmap down as needed. - */ - private Bitmap scaleBitmapDown(final Bitmap bitmap) { + /** Scale the bitmap down as needed. */ + private Bitmap scaleBitmapDown(Bitmap bitmap) { double scaleRatio = -1; if (mResizeArea > 0) { - final int bitmapArea = bitmap.getWidth() * bitmap.getHeight(); + int bitmapArea = bitmap.getWidth() * bitmap.getHeight(); if (bitmapArea > mResizeArea) { scaleRatio = Math.sqrt(mResizeArea / (double) bitmapArea); } } else if (mResizeMaxDimension > 0) { - final int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight()); + int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight()); if (maxDimension > mResizeMaxDimension) { scaleRatio = mResizeMaxDimension / (double) maxDimension; } @@ -944,11 +447,13 @@ public final class Palette { return bitmap; } - return Bitmap.createScaledBitmap(bitmap, + return Bitmap.createScaledBitmap( + bitmap, (int) Math.ceil(bitmap.getWidth() * scaleRatio), (int) Math.ceil(bitmap.getHeight() * scaleRatio), false); } + } /** @@ -961,9 +466,7 @@ public final class Palette { * * @param rgb the color in RGB888. * @param hsl HSL representation of the color. - * * @return true if the color is allowed, false if not. - * * @see Palette.Builder#addFilter(Palette.Filter) */ boolean isAllowed(int rgb, float[] hsl); @@ -1004,3 +507,4 @@ public final class Palette { } }; } + diff --git a/core/java/com/android/internal/graphics/palette/Quantizer.java b/core/java/com/android/internal/graphics/palette/Quantizer.java index db60f2e9dc69..a219ea3aa7d0 100644 --- a/core/java/com/android/internal/graphics/palette/Quantizer.java +++ b/core/java/com/android/internal/graphics/palette/Quantizer.java @@ -22,6 +22,15 @@ import java.util.List; * Definition of an algorithm that receives pixels and outputs a list of colors. */ public interface Quantizer { - void quantize(final int[] pixels, final int maxColors, final Palette.Filter[] filters); + /** + * Create colors representative of the colors present in pixels. + * @param pixels Set of ARGB representation of a color. + * @param maxColors number of colors to generate + */ + void quantize(int[] pixels, int maxColors); + + /** + * List of colors generated by previous call to quantize. + */ List<Palette.Swatch> getQuantizedColors(); } diff --git a/core/java/com/android/internal/graphics/palette/Target.java b/core/java/com/android/internal/graphics/palette/Target.java index 0540d80ef6f0..96e7faa81c3a 100644 --- a/core/java/com/android/internal/graphics/palette/Target.java +++ b/core/java/com/android/internal/graphics/palette/Target.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 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. @@ -16,368 +16,234 @@ package com.android.internal.graphics.palette; -/* - * Copyright 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; /** - * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/graphics/Target.java - * - * A class which allows custom selection of colors in a {@link Palette}'s generation. Instances - * can be created via the {@link android.support.v7.graphics.Target.Builder} class. - * - * <p>To use the target, use the {@link Palette.Builder#addTarget(Target)} API when building a - * Palette.</p> + * A class which allows custom selection of colors in a {@link Palette}'s generation. Instances can + * be created via the {@link Builder} class. */ -public final class Target { - - private static final float TARGET_DARK_LUMA = 0.26f; - private static final float MAX_DARK_LUMA = 0.45f; - - private static final float MIN_LIGHT_LUMA = 0.55f; - private static final float TARGET_LIGHT_LUMA = 0.74f; - - private static final float MIN_NORMAL_LUMA = 0.3f; - private static final float TARGET_NORMAL_LUMA = 0.5f; - private static final float MAX_NORMAL_LUMA = 0.7f; - - private static final float TARGET_MUTED_SATURATION = 0.3f; - private static final float MAX_MUTED_SATURATION = 0.4f; - - private static final float TARGET_VIBRANT_SATURATION = 1f; - private static final float MIN_VIBRANT_SATURATION = 0.35f; - - private static final float WEIGHT_SATURATION = 0.24f; - private static final float WEIGHT_LUMA = 0.52f; - private static final float WEIGHT_POPULATION = 0.24f; - - static final int INDEX_MIN = 0; - static final int INDEX_TARGET = 1; - static final int INDEX_MAX = 2; - - static final int INDEX_WEIGHT_SAT = 0; - static final int INDEX_WEIGHT_LUMA = 1; - static final int INDEX_WEIGHT_POP = 2; - - /** - * A target which has the characteristics of a vibrant color which is light in luminance. - */ - public static final Target LIGHT_VIBRANT; - - /** - * A target which has the characteristics of a vibrant color which is neither light or dark. - */ - public static final Target VIBRANT; - - /** - * A target which has the characteristics of a vibrant color which is dark in luminance. - */ - public static final Target DARK_VIBRANT; - - /** - * A target which has the characteristics of a muted color which is light in luminance. - */ - public static final Target LIGHT_MUTED; - - /** - * A target which has the characteristics of a muted color which is neither light or dark. - */ - public static final Target MUTED; - - /** - * A target which has the characteristics of a muted color which is dark in luminance. - */ - public static final Target DARK_MUTED; - static { - LIGHT_VIBRANT = new Target(); - setDefaultLightLightnessValues(LIGHT_VIBRANT); - setDefaultVibrantSaturationValues(LIGHT_VIBRANT); - - VIBRANT = new Target(); - setDefaultNormalLightnessValues(VIBRANT); - setDefaultVibrantSaturationValues(VIBRANT); - - DARK_VIBRANT = new Target(); - setDefaultDarkLightnessValues(DARK_VIBRANT); - setDefaultVibrantSaturationValues(DARK_VIBRANT); - - LIGHT_MUTED = new Target(); - setDefaultLightLightnessValues(LIGHT_MUTED); - setDefaultMutedSaturationValues(LIGHT_MUTED); - - MUTED = new Target(); - setDefaultNormalLightnessValues(MUTED); - setDefaultMutedSaturationValues(MUTED); - - DARK_MUTED = new Target(); - setDefaultDarkLightnessValues(DARK_MUTED); - setDefaultMutedSaturationValues(DARK_MUTED); - } - - final float[] mSaturationTargets = new float[3]; - final float[] mLightnessTargets = new float[3]; - final float[] mWeights = new float[3]; - boolean mIsExclusive = true; // default to true +public final class Target { + private static final float WEIGHT_CHROMA = 0.5f; + private static final float WEIGHT_RELATIVE_LUMINANCE = 0.5f; + private static final float WEIGHT_POPULATION = 0.3f; + private static final float WEIGHT_HUE = 0.2f; + + // Arbitrarily chosen, except max - CAM16 chroma has a ceiling of 130, based on unit testing. + private static final float DEFAULT_CHROMA_MIN = 0.f; + private static final float DEFAULT_CHROMA_MAX = 130.f; + private static final float DEFAULT_CHROMA_TARGET = 30.f; + + private float mTargetRelativeLuminance = -1.0f; + private float mChromaWeight; + private float mChromaTarget; + private float mChromaMin; + private float mChromaMax; + private float mRelativeLuminanceWeight; + private float mPopulationWeight; + private float mHueWeight; + private float mTargetHue; Target() { - setTargetDefaultValues(mSaturationTargets); - setTargetDefaultValues(mLightnessTargets); - setDefaultWeights(); + mChromaMax = DEFAULT_CHROMA_MAX; + mChromaMin = DEFAULT_CHROMA_MIN; + mChromaTarget = DEFAULT_CHROMA_TARGET; + mChromaWeight = WEIGHT_CHROMA; + mRelativeLuminanceWeight = WEIGHT_RELATIVE_LUMINANCE; + mPopulationWeight = WEIGHT_POPULATION; + mHueWeight = WEIGHT_HUE; } - Target(Target from) { - System.arraycopy(from.mSaturationTargets, 0, mSaturationTargets, 0, - mSaturationTargets.length); - System.arraycopy(from.mLightnessTargets, 0, mLightnessTargets, 0, - mLightnessTargets.length); - System.arraycopy(from.mWeights, 0, mWeights, 0, mWeights.length); + Target(@NonNull Target from) { + mTargetRelativeLuminance = from.mTargetRelativeLuminance; + mChromaWeight = from.mChromaWeight; + mRelativeLuminanceWeight = from.mRelativeLuminanceWeight; + mPopulationWeight = from.mPopulationWeight; + mHueWeight = from.mHueWeight; + mChromaTarget = from.mChromaTarget; + mChromaMin = from.mChromaMin; + mChromaMax = from.mChromaMax; } - /** - * The minimum saturation value for this target. - */ - @FloatRange(from = 0, to = 1) - public float getMinimumSaturation() { - return mSaturationTargets[INDEX_MIN]; + /** The relative luminance value for this target. */ + @FloatRange(from = 0, to = 100) + public float getTargetRelativeLuminance() { + return mTargetRelativeLuminance; } - /** - * The target saturation value for this target. - */ - @FloatRange(from = 0, to = 1) - public float getTargetSaturation() { - return mSaturationTargets[INDEX_TARGET]; + /** The relative luminance value for this target. */ + @FloatRange(from = 0, to = 100) + public float getTargetPerceptualLuminance() { + return Contrast.yToLstar(mTargetRelativeLuminance); } - /** - * The maximum saturation value for this target. - */ - @FloatRange(from = 0, to = 1) - public float getMaximumSaturation() { - return mSaturationTargets[INDEX_MAX]; + /** The minimum chroma value for this target. */ + @FloatRange(from = 0, to = 100) + public float getMinimumChroma() { + return mChromaMin; } - /** - * The minimum lightness value for this target. - */ - @FloatRange(from = 0, to = 1) - public float getMinimumLightness() { - return mLightnessTargets[INDEX_MIN]; + /** The target chroma value for this target. */ + @FloatRange(from = 0, to = 100) + public float getTargetChroma() { + return mChromaTarget; } - /** - * The target lightness value for this target. - */ - @FloatRange(from = 0, to = 1) - public float getTargetLightness() { - return mLightnessTargets[INDEX_TARGET]; + /** The maximum chroma value for this target. */ + @FloatRange(from = 0, to = 130) + public float getMaximumChroma() { + return mChromaMax; } - /** - * The maximum lightness value for this target. - */ - @FloatRange(from = 0, to = 1) - public float getMaximumLightness() { - return mLightnessTargets[INDEX_MAX]; + /** The target hue value for this target. */ + @FloatRange(from = 0, to = 100) + public float getTargetHue() { + return mTargetHue; } /** - * Returns the weight of importance that this target places on a color's saturation within - * the image. + * Returns the weight of importance that this target places on a color's chroma within the + * image. * * <p>The larger the weight, relative to the other weights, the more important that a color - * being close to the target value has on selection.</p> + * being + * close to the target value has on selection. * - * @see #getTargetSaturation() + * @see #getTargetChroma() */ - public float getSaturationWeight() { - return mWeights[INDEX_WEIGHT_SAT]; + public float getChromaWeight() { + return mChromaWeight; } /** - * Returns the weight of importance that this target places on a color's lightness within - * the image. + * Returns the weight of importance that this target places on a color's lightness within the + * image. * * <p>The larger the weight, relative to the other weights, the more important that a color - * being close to the target value has on selection.</p> + * being + * close to the target value has on selection. * - * @see #getTargetLightness() + * @see #getTargetRelativeLuminance() */ public float getLightnessWeight() { - return mWeights[INDEX_WEIGHT_LUMA]; + return mRelativeLuminanceWeight; } /** - * Returns the weight of importance that this target places on a color's population within - * the image. + * Returns the weight of importance that this target places on a color's population within the + * image. * - * <p>The larger the weight, relative to the other weights, the more important that a - * color's population being close to the most populous has on selection.</p> + * <p>The larger the weight, relative to the other weights, the more important that a color's + * population being close to the most populous has on selection. */ public float getPopulationWeight() { - return mWeights[INDEX_WEIGHT_POP]; + return mPopulationWeight; } /** - * Returns whether any color selected for this target is exclusive for this target only. + * Returns the weight of importance that this target places on a color's hue. * - * <p>If false, then the color can be selected for other targets.</p> + * <p>The larger the weight, relative to the other weights, the more important that a color's + * hue being close to the desired hue has on selection. */ - public boolean isExclusive() { - return mIsExclusive; + public float getHueWeight() { + return mHueWeight; } - private static void setTargetDefaultValues(final float[] values) { - values[INDEX_MIN] = 0f; - values[INDEX_TARGET] = 0.5f; - values[INDEX_MAX] = 1f; - } - - private void setDefaultWeights() { - mWeights[INDEX_WEIGHT_SAT] = WEIGHT_SATURATION; - mWeights[INDEX_WEIGHT_LUMA] = WEIGHT_LUMA; - mWeights[INDEX_WEIGHT_POP] = WEIGHT_POPULATION; - } - void normalizeWeights() { - float sum = 0; - for (int i = 0, z = mWeights.length; i < z; i++) { - float weight = mWeights[i]; - if (weight > 0) { - sum += weight; - } - } - if (sum != 0) { - for (int i = 0, z = mWeights.length; i < z; i++) { - if (mWeights[i] > 0) { - mWeights[i] /= sum; - } - } - } - } - - private static void setDefaultDarkLightnessValues(Target target) { - target.mLightnessTargets[INDEX_TARGET] = TARGET_DARK_LUMA; - target.mLightnessTargets[INDEX_MAX] = MAX_DARK_LUMA; - } - - private static void setDefaultNormalLightnessValues(Target target) { - target.mLightnessTargets[INDEX_MIN] = MIN_NORMAL_LUMA; - target.mLightnessTargets[INDEX_TARGET] = TARGET_NORMAL_LUMA; - target.mLightnessTargets[INDEX_MAX] = MAX_NORMAL_LUMA; - } - - private static void setDefaultLightLightnessValues(Target target) { - target.mLightnessTargets[INDEX_MIN] = MIN_LIGHT_LUMA; - target.mLightnessTargets[INDEX_TARGET] = TARGET_LIGHT_LUMA; - } - - private static void setDefaultVibrantSaturationValues(Target target) { - target.mSaturationTargets[INDEX_MIN] = MIN_VIBRANT_SATURATION; - target.mSaturationTargets[INDEX_TARGET] = TARGET_VIBRANT_SATURATION; - } - - private static void setDefaultMutedSaturationValues(Target target) { - target.mSaturationTargets[INDEX_TARGET] = TARGET_MUTED_SATURATION; - target.mSaturationTargets[INDEX_MAX] = MAX_MUTED_SATURATION; - } - - /** - * Builder class for generating custom {@link Target} instances. - */ - public final static class Builder { + /** Builder class for generating custom {@link Target} instances. */ + public static class Builder { private final Target mTarget; - /** - * Create a new {@link Target} builder from scratch. - */ + /** Create a new {@link Target} builder from scratch. */ public Builder() { mTarget = new Target(); } - /** - * Create a new builder based on an existing {@link Target}. - */ - public Builder(Target target) { + /** Create a new builder based on an existing {@link Target}. */ + public Builder(@NonNull Target target) { mTarget = new Target(target); } - /** - * Set the minimum saturation value for this target. - */ - public Target.Builder setMinimumSaturation(@FloatRange(from = 0, to = 1) float value) { - mTarget.mSaturationTargets[INDEX_MIN] = value; + /** Set the minimum chroma value for this target. */ + @NonNull + public Builder setMinimumChroma(@FloatRange(from = 0, to = 100) float value) { + mTarget.mChromaMin = value; return this; } - /** - * Set the target/ideal saturation value for this target. - */ - public Target.Builder setTargetSaturation(@FloatRange(from = 0, to = 1) float value) { - mTarget.mSaturationTargets[INDEX_TARGET] = value; + /** Set the target/ideal chroma value for this target. */ + @NonNull + public Builder setTargetChroma(@FloatRange(from = 0, to = 100) float value) { + mTarget.mChromaTarget = value; return this; } - /** - * Set the maximum saturation value for this target. - */ - public Target.Builder setMaximumSaturation(@FloatRange(from = 0, to = 1) float value) { - mTarget.mSaturationTargets[INDEX_MAX] = value; + /** Set the maximum chroma value for this target. */ + @NonNull + public Builder setMaximumChroma(@FloatRange(from = 0, to = 100) float value) { + mTarget.mChromaMax = value; return this; } - /** - * Set the minimum lightness value for this target. - */ - public Target.Builder setMinimumLightness(@FloatRange(from = 0, to = 1) float value) { - mTarget.mLightnessTargets[INDEX_MIN] = value; + /** Set the minimum lightness value for this target, using Y in XYZ color space. */ + @NonNull + public Builder setTargetRelativeLuminance(@FloatRange(from = 0, to = 100) float value) { + mTarget.mTargetRelativeLuminance = value; return this; } - /** - * Set the target/ideal lightness value for this target. - */ - public Target.Builder setTargetLightness(@FloatRange(from = 0, to = 1) float value) { - mTarget.mLightnessTargets[INDEX_TARGET] = value; + /** Set the minimum lightness value for this target, using L* in LAB color space. */ + @NonNull + public Builder setTargetPerceptualLuminance(@FloatRange(from = 0, to = 100) float value) { + mTarget.mTargetRelativeLuminance = Contrast.lstarToY(value); return this; } /** - * Set the maximum lightness value for this target. + * Set the hue desired from the target. This hue is not enforced, the only consequence + * is points will be awarded to seed colors the closer they are to this hue. */ - public Target.Builder setMaximumLightness(@FloatRange(from = 0, to = 1) float value) { - mTarget.mLightnessTargets[INDEX_MAX] = value; + @NonNull + public Builder setTargetHue(@IntRange(from = 0, to = 360) int hue) { + mTarget.mTargetHue = hue; + return this; + } + + /** Sets lightness value for this target. */ + @NonNull + public Builder setContrastRatio( + @FloatRange(from = 1, to = 21) float value, + @FloatRange(from = 0, to = 100) float relativeLuminance) { + float counterpartY = relativeLuminance; + float lstar = Contrast.yToLstar(counterpartY); + + float targetY; + if (lstar < 50) { + targetY = Contrast.lighterY(counterpartY, value); + } else { + targetY = Contrast.darkerY(counterpartY, value); + } + mTarget.mTargetRelativeLuminance = targetY; return this; } /** - * Set the weight of importance that this target will place on saturation values. + * Set the weight of importance that this target will place on chroma values. * * <p>The larger the weight, relative to the other weights, the more important that a color - * being close to the target value has on selection.</p> + * being close to the target value has on selection. * - * <p>A weight of 0 means that it has no weight, and thus has no - * bearing on the selection.</p> + * <p>A weight of 0 means that it has no weight, and thus has no bearing on the selection. * - * @see #setTargetSaturation(float) + * @see #setTargetChroma(float) */ - public Target.Builder setSaturationWeight(@FloatRange(from = 0) float weight) { - mTarget.mWeights[INDEX_WEIGHT_SAT] = weight; + @NonNull + public Builder setChromaWeight(@FloatRange(from = 0) float weight) { + mTarget.mChromaWeight = weight; return this; } @@ -385,51 +251,40 @@ public final class Target { * Set the weight of importance that this target will place on lightness values. * * <p>The larger the weight, relative to the other weights, the more important that a color - * being close to the target value has on selection.</p> + * being close to the target value has on selection. * - * <p>A weight of 0 means that it has no weight, and thus has no - * bearing on the selection.</p> + * <p>A weight of 0 means that it has no weight, and thus has no bearing on the selection. * - * @see #setTargetLightness(float) + * @see #setTargetRelativeLuminance(float) */ - public Target.Builder setLightnessWeight(@FloatRange(from = 0) float weight) { - mTarget.mWeights[INDEX_WEIGHT_LUMA] = weight; + @NonNull + public Builder setLightnessWeight(@FloatRange(from = 0) float weight) { + mTarget.mRelativeLuminanceWeight = weight; return this; } /** * Set the weight of importance that this target will place on a color's population within - * the image. + * the + * image. * * <p>The larger the weight, relative to the other weights, the more important that a - * color's population being close to the most populous has on selection.</p> + * color's + * population being close to the most populous has on selection. * - * <p>A weight of 0 means that it has no weight, and thus has no - * bearing on the selection.</p> + * <p>A weight of 0 means that it has no weight, and thus has no bearing on the selection. */ - public Target.Builder setPopulationWeight(@FloatRange(from = 0) float weight) { - mTarget.mWeights[INDEX_WEIGHT_POP] = weight; + @NonNull + public Builder setPopulationWeight(@FloatRange(from = 0) float weight) { + mTarget.mPopulationWeight = weight; return this; } - /** - * Set whether any color selected for this target is exclusive to this target only. - * Defaults to true. - * - * @param exclusive true if any the color is exclusive to this target, or false is the - * color can be selected for other targets. - */ - public Target.Builder setExclusive(boolean exclusive) { - mTarget.mIsExclusive = exclusive; - return this; - } - /** - * Builds and returns the resulting {@link Target}. - */ + /** Builds and returns the resulting {@link Target}. */ + @NonNull public Target build() { return mTarget; } } - -}
\ No newline at end of file +} diff --git a/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java index b0355350dc15..d791f7b3e6be 100644 --- a/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java +++ b/core/java/com/android/internal/graphics/palette/VariationalKMeansQuantizer.java @@ -70,10 +70,9 @@ public class VariationalKMeansQuantizer implements Quantizer { * * @param pixels Pixels to quantize. * @param maxColors Maximum number of clusters to extract. - * @param filters Colors that should be ignored */ @Override - public void quantize(int[] pixels, int maxColors, Palette.Filter[] filters) { + public void quantize(int[] pixels, int maxColors) { // Start by converting all colors to HSL. // HLS is way more meaningful for clustering than RGB. final float[] hsl = {0, 0, 0}; @@ -111,16 +110,18 @@ public class VariationalKMeansQuantizer implements Quantizer { // Convert data to final format, de-normalizing the hue. mQuantizedColors = new ArrayList<>(); + float[] mHsl = new float[3]; for (KMeans.Mean mean : optimalMeans) { if (mean.getItems().size() == 0) { continue; } float[] centroid = mean.getCentroid(); - mQuantizedColors.add(new Palette.Swatch(new float[]{ - centroid[0] * 360f, - centroid[1], - centroid[2] - }, mean.getItems().size())); + + mHsl[0] = centroid[0] * 360f; + mHsl[1] = centroid[1]; + mHsl[2] = centroid[2]; + int color = ColorUtils.HSLToColor(mHsl); + mQuantizedColors.add(new Palette.Swatch(color, mean.getItems().size())); } } diff --git a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java new file mode 100644 index 000000000000..a87a34f4ae11 --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java @@ -0,0 +1,269 @@ +/* + * 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.internal.graphics.palette; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * A color quantizer based on the Kmeans algorithm. + * + * This is an implementation of Kmeans based on Celebi's 2011 paper, + * "Improving the Performance of K-Means for Color Quantization". In the paper, this algorithm is + * referred to as "WSMeans", or, "Weighted Square Means" The main advantages of this Kmeans + * implementation are taking advantage of triangle properties to avoid distance calculations, as + * well as indexing colors by their count, thus minimizing the number of points to move around. + * + * Celebi's paper also stabilizes results and guarantees high quality by using starting centroids + * from Wu's quantization algorithm. See CelebiQuantizer for more info. + */ +public class WSMeansQuantizer implements Quantizer { + Mean[] mMeans; + private final Map<Integer, Integer> mCountByColor = new HashMap<>(); + private final Map<Integer, Integer> mMeanIndexByColor = new HashMap<>(); + private final Set<Integer> mUniqueColors = new HashSet<>(); + private final List<Palette.Swatch> mSwatches = new ArrayList<>(); + private final CentroidProvider mCentroidProvider; + + public WSMeansQuantizer( + float[][] means, CentroidProvider centroidProvider, int[] pixels, int maxColors) { + if (pixels == null) { + pixels = new int[]{}; + } + mCentroidProvider = centroidProvider; + mMeans = new Mean[maxColors]; + for (int i = 0; i < means.length; i++) { + mMeans[i] = new Mean(means[i]); + } + + if (maxColors > means.length) { + int randomMeansToCreate = maxColors - means.length; + for (int i = 0; i < randomMeansToCreate; i++) { + mMeans[means.length + i] = new Mean(100); + } + } + + for (int pixel : pixels) { + Integer currentCount = mCountByColor.get(pixel); + if (currentCount == null) { + currentCount = 0; + mUniqueColors.add(pixel); + } + mCountByColor.put(pixel, currentCount + 1); + } + for (int color : mUniqueColors) { + int closestMeanIndex = -1; + double closestMeanDistance = -1; + float[] centroid = mCentroidProvider.getCentroid(color); + for (int i = 0; i < mMeans.length; i++) { + double distance = mCentroidProvider.distance(centroid, mMeans[i].center); + if (closestMeanIndex == -1 || distance < closestMeanDistance) { + closestMeanIndex = i; + closestMeanDistance = distance; + } + } + mMeanIndexByColor.put(color, closestMeanIndex); + } + + if (pixels.length == 0) { + return; + } + + predict(maxColors, 0); + } + + /** Create starting centroids for K-means from a set of colors. */ + public static float[][] createStartingCentroids(CentroidProvider centroidProvider, + List<Palette.Swatch> swatches) { + float[][] startingCentroids = new float[swatches.size()][]; + for (int i = 0; i < swatches.size(); i++) { + startingCentroids[i] = centroidProvider.getCentroid(swatches.get(i).getInt()); + } + return startingCentroids; + } + + /** Create random starting centroids for K-means. */ + public static float[][] randomMeans(int maxColors, int upperBound) { + float[][] means = new float[maxColors][]; + for (int i = 0; i < maxColors; i++) { + means[i] = new Mean(upperBound).center; + } + return means; + } + + + @Override + public void quantize(int[] pixels, int maxColors) { + + } + + @Override + public List<Palette.Swatch> getQuantizedColors() { + return mSwatches; + } + + private void predict(int maxColors, int iterationsCompleted) { + double[][] centroidDistance = new double[maxColors][maxColors]; + for (int i = 0; i <= maxColors; i++) { + for (int j = i + 1; j < maxColors; j++) { + float[] meanI = mMeans[i].center; + float[] meanJ = mMeans[j].center; + double distance = mCentroidProvider.distance(meanI, meanJ); + centroidDistance[i][j] = distance; + centroidDistance[j][i] = distance; + } + } + + // Construct a K×K matrix M in which row i is a permutation of + // 1,2,…,K that represents the clusters in increasing order of + // distance of their centers from ci; + int[][] distanceMatrix = new int[maxColors][maxColors]; + for (int i = 0; i < maxColors; i++) { + double[] distancesFromIToAnotherMean = centroidDistance[i]; + double[] sortedByDistanceAscending = distancesFromIToAnotherMean.clone(); + Arrays.sort(sortedByDistanceAscending); + int[] outputRow = new int[maxColors]; + for (int j = 0; j < maxColors; j++) { + outputRow[j] = findIndex(distancesFromIToAnotherMean, sortedByDistanceAscending[j]); + } + distanceMatrix[i] = outputRow; + } + + // for (i=1;i≤N′;i=i+ 1) do + // Let Sp be the cluster that xi was assigned to in the previous + // iteration; + // p=m[i]; + // min_dist=prev_dist=jjxi−cpjj2; + boolean anyColorMoved = false; + for (int intColor : mUniqueColors) { + float[] color = mCentroidProvider.getCentroid(intColor); + int indexOfCurrentMean = mMeanIndexByColor.get(intColor); + Mean currentMean = mMeans[indexOfCurrentMean]; + double minDistance = mCentroidProvider.distance(color, currentMean.center); + for (int j = 1; j < maxColors; j++) { + int indexOfClusterFromCurrentToJ = distanceMatrix[indexOfCurrentMean][j]; + double distanceBetweenJAndCurrent = + centroidDistance[indexOfCurrentMean][indexOfClusterFromCurrentToJ]; + if (distanceBetweenJAndCurrent >= (4 * minDistance)) { + break; + } + double distanceBetweenJAndColor = mCentroidProvider.distance(mMeans[j].center, + color); + if (distanceBetweenJAndColor < minDistance) { + minDistance = distanceBetweenJAndColor; + mMeanIndexByColor.remove(intColor); + mMeanIndexByColor.put(intColor, j); + anyColorMoved = true; + } + } + } + + List<MeanBucket> buckets = new ArrayList<>(); + for (int i = 0; i < maxColors; i++) { + buckets.add(new MeanBucket()); + } + + for (int intColor : mUniqueColors) { + int meanIndex = mMeanIndexByColor.get(intColor); + MeanBucket meanBucket = buckets.get(meanIndex); + meanBucket.add(mCentroidProvider.getCentroid(intColor), intColor, + mCountByColor.get(intColor)); + } + + List<Palette.Swatch> swatches = new ArrayList<>(); + boolean done = !anyColorMoved && iterationsCompleted > 0 || iterationsCompleted >= 100; + if (done) { + for (int i = 0; i < buckets.size(); i++) { + MeanBucket a = buckets.get(i); + if (a.mCount <= 0) { + continue; + } + List<MeanBucket> bucketsToMerge = new ArrayList<>(); + for (int j = i + 1; j < buckets.size(); j++) { + MeanBucket b = buckets.get(j); + if (b.mCount == 0) { + continue; + } + float[] bCentroid = b.getCentroid(); + assert (a.mCount > 0); + assert (a.getCentroid() != null); + + assert (bCentroid != null); + if (mCentroidProvider.distance(a.getCentroid(), b.getCentroid()) < 5) { + bucketsToMerge.add(b); + } + } + + for (MeanBucket bucketToMerge : bucketsToMerge) { + float[] centroid = bucketToMerge.getCentroid(); + a.add(centroid, mCentroidProvider.getColor(centroid), bucketToMerge.mCount); + buckets.remove(bucketToMerge); + } + } + + for (MeanBucket bucket : buckets) { + float[] centroid = bucket.getCentroid(); + if (centroid == null) { + continue; + } + + int rgb = mCentroidProvider.getColor(centroid); + swatches.add(new Palette.Swatch(rgb, bucket.mCount)); + mSwatches.clear(); + mSwatches.addAll(swatches); + } + } else { + List<MeanBucket> emptyBuckets = new ArrayList<>(); + for (int i = 0; i < buckets.size(); i++) { + MeanBucket bucket = buckets.get(i); + if ((bucket.getCentroid() == null) || (bucket.mCount == 0)) { + emptyBuckets.add(bucket); + for (Integer color : mUniqueColors) { + int meanIndex = mMeanIndexByColor.get(color); + if (meanIndex > i) { + mMeanIndexByColor.put(color, meanIndex--); + } + } + } + } + + Mean[] newMeans = new Mean[buckets.size()]; + for (int i = 0; i < buckets.size(); i++) { + float[] centroid = buckets.get(i).getCentroid(); + newMeans[i] = new Mean(centroid); + } + + predict(buckets.size(), iterationsCompleted + 1); + } + + } + + private static int findIndex(double[] list, double element) { + for (int i = 0; i < list.length; i++) { + if (list[i] == element) { + return i; + } + } + throw new IllegalArgumentException("Element not in list"); + } +} diff --git a/core/java/com/android/internal/graphics/palette/WuQuantizer.java b/core/java/com/android/internal/graphics/palette/WuQuantizer.java new file mode 100644 index 000000000000..01e45f613986 --- /dev/null +++ b/core/java/com/android/internal/graphics/palette/WuQuantizer.java @@ -0,0 +1,442 @@ +/* + * 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.internal.graphics.palette; + + +import java.util.ArrayList; +import java.util.List; + +// All reference Wu implementations are based on the original C code by Wu. +// Comments on methods are the same as in the original implementation, and the comment below +// is the original class header. + +/** + * Wu's Color Quantizer (v. 2) (see Graphics Gems vol. II, pp. 126-133) Author: Xiaolin Wu + * + * <p>Algorithm: Greedy orthogonal bipartition of RGB space for variance minimization aided by + * inclusion-exclusion tricks. For speed no nearest neighbor search is done. Slightly better + * performance can be expected by more sophisticated but more expensive versions. + */ +public class WuQuantizer implements Quantizer { + private static final int MAX_COLORS = 256; + private static final int RED = 2; + private static final int GREEN = 1; + private static final int BLUE = 0; + + private static final int QUANT_SIZE = 33; + private final List<Palette.Swatch> mSwatches = new ArrayList<>(); + + @Override + public List<Palette.Swatch> getQuantizedColors() { + return mSwatches; + } + + private static final class Box { + int mR0; /* min value, exclusive */ + int mR1; /* max value, inclusive */ + int mG0; + int mG1; + int mB0; + int mB1; + int mVol; + } + + private final int mSize; /* image size, in bytes. */ + private int mMaxColors; + private int[] mQadd; + private final int[] mPixels; + + private final double[][][] mM2 = new double[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE]; + private final long[][][] mWt = new long[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE]; + private final long[][][] mMr = new long[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE]; + private final long[][][] mMg = new long[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE]; + private final long[][][] mMb = new long[QUANT_SIZE][QUANT_SIZE][QUANT_SIZE]; + + public WuQuantizer(int[] pixels, int maxColorCount) { + if (pixels == null) { + pixels = new int[]{}; + } + this.mPixels = pixels; + this.mSize = pixels.length; + } + + @Override + public void quantize(int[] colors, int maxColorCount) { + // All of the sample Wu implementations are reimplementations of a snippet of C code from + // the early 90s. They all cap the maximum # of colors at 256, and it is impossible to tell + // if this is a requirement, a consequence of QUANT_SIZE, or arbitrary. + this.mMaxColors = Math.min(MAX_COLORS, maxColorCount); + Box[] cube = new Box[mMaxColors]; + int red, green, blue; + + int next, i, k; + long weight; + double[] vv = new double[mMaxColors]; + double temp; + + compute3DHistogram(mWt, mMr, mMg, mMb, mM2); + computeMoments(mWt, mMr, mMg, mMb, mM2); + + for (i = 0; i < mMaxColors; i++) { + cube[i] = new Box(); + } + + cube[0].mR0 = cube[0].mG0 = cube[0].mB0 = 0; + cube[0].mR1 = cube[0].mG1 = cube[0].mB1 = QUANT_SIZE - 1; + next = 0; + + for (i = 1; i < mMaxColors; ++i) { + if (cut(cube[next], cube[i])) { + vv[next] = (cube[next].mVol > 1) ? getVariance(cube[next]) : 0.0f; + vv[i] = (cube[i].mVol > 1) ? getVariance(cube[i]) : 0.0f; + } else { + vv[next] = 0.0f; + i--; + } + next = 0; + temp = vv[0]; + for (k = 1; k <= i; ++k) { + if (vv[k] > temp) { + temp = vv[k]; + next = k; + } + } + if (temp <= 0.0f) { + break; + } + } + + for (k = 0; k < mMaxColors; ++k) { + weight = getVolume(cube[k], mWt); + if (weight > 0) { + red = (int) (getVolume(cube[k], mMr) / weight); + green = (int) (getVolume(cube[k], mMg) / weight); + blue = (int) (getVolume(cube[k], mMb) / weight); + colors[k] = ((red & 0x0ff) << 16) | ((green & 0x0ff) << 8) | (blue & 0x0ff); + } else { + colors[k] = 0; + } + } + + int bitsPerPixel = 0; + while ((1 << bitsPerPixel) < mMaxColors) { + bitsPerPixel++; + } + + List<Palette.Swatch> swatches = new ArrayList<>(); + for (int l = 0; l < k; l++) { + int pixel = colors[l]; + if (pixel == 0) { + continue; + } + swatches.add(new Palette.Swatch(pixel, 0)); + } + mSwatches.clear(); + mSwatches.addAll(swatches); + } + + /* Histogram is in elements 1..HISTSIZE along each axis, + * element 0 is for base or marginal value + * NB: these must start out 0! + */ + private void compute3DHistogram( + long[][][] vwt, long[][][] vmr, long[][][] vmg, long[][][] vmb, double[][][] m2) { + // build 3-D color histogram of counts, r/g/b, and c^2 + int r, g, b; + int i; + int inr; + int ing; + int inb; + int[] table = new int[256]; + + for (i = 0; i < 256; i++) { + table[i] = i * i; + } + + mQadd = new int[mSize]; + + for (i = 0; i < mSize; ++i) { + int rgb = mPixels[i]; + // Skip less than opaque pixels. They're not meaningful in the context of palette + // generation for UI schemes. + if ((rgb >>> 24) < 0xff) { + continue; + } + r = ((rgb >> 16) & 0xff); + g = ((rgb >> 8) & 0xff); + b = (rgb & 0xff); + inr = (r >> 3) + 1; + ing = (g >> 3) + 1; + inb = (b >> 3) + 1; + mQadd[i] = (inr << 10) + (inr << 6) + inr + (ing << 5) + ing + inb; + /*[inr][ing][inb]*/ + ++vwt[inr][ing][inb]; + vmr[inr][ing][inb] += r; + vmg[inr][ing][inb] += g; + vmb[inr][ing][inb] += b; + m2[inr][ing][inb] += table[r] + table[g] + table[b]; + } + } + + /* At conclusion of the histogram step, we can interpret + * wt[r][g][b] = sum over voxel of P(c) + * mr[r][g][b] = sum over voxel of r*P(c) , similarly for mg, mb + * m2[r][g][b] = sum over voxel of c^2*P(c) + * Actually each of these should be divided by 'size' to give the usual + * interpretation of P() as ranging from 0 to 1, but we needn't do that here. + * + * We now convert histogram into moments so that we can rapidly calculate + * the sums of the above quantities over any desired box. + */ + private void computeMoments( + long[][][] vwt, long[][][] vmr, long[][][] vmg, long[][][] vmb, double[][][] m2) { + /* compute cumulative moments. */ + int i, r, g, b; + int line, line_r, line_g, line_b; + int[] area = new int[QUANT_SIZE]; + int[] area_r = new int[QUANT_SIZE]; + int[] area_g = new int[QUANT_SIZE]; + int[] area_b = new int[QUANT_SIZE]; + double line2; + double[] area2 = new double[QUANT_SIZE]; + + for (r = 1; r < QUANT_SIZE; ++r) { + for (i = 0; i < QUANT_SIZE; ++i) { + area2[i] = area[i] = area_r[i] = area_g[i] = area_b[i] = 0; + } + for (g = 1; g < QUANT_SIZE; ++g) { + line2 = line = line_r = line_g = line_b = 0; + for (b = 1; b < QUANT_SIZE; ++b) { + line += vwt[r][g][b]; + line_r += vmr[r][g][b]; + line_g += vmg[r][g][b]; + line_b += vmb[r][g][b]; + line2 += m2[r][g][b]; + + area[b] += line; + area_r[b] += line_r; + area_g[b] += line_g; + area_b[b] += line_b; + area2[b] += line2; + + vwt[r][g][b] = vwt[r - 1][g][b] + area[b]; + vmr[r][g][b] = vmr[r - 1][g][b] + area_r[b]; + vmg[r][g][b] = vmg[r - 1][g][b] + area_g[b]; + vmb[r][g][b] = vmb[r - 1][g][b] + area_b[b]; + m2[r][g][b] = m2[r - 1][g][b] + area2[b]; + } + } + } + } + + private long getVolume(Box cube, long[][][] mmt) { + /* Compute sum over a box of any given statistic */ + return (mmt[cube.mR1][cube.mG1][cube.mB1] + - mmt[cube.mR1][cube.mG1][cube.mB0] + - mmt[cube.mR1][cube.mG0][cube.mB1] + + mmt[cube.mR1][cube.mG0][cube.mB0] + - mmt[cube.mR0][cube.mG1][cube.mB1] + + mmt[cube.mR0][cube.mG1][cube.mB0] + + mmt[cube.mR0][cube.mG0][cube.mB1] + - mmt[cube.mR0][cube.mG0][cube.mB0]); + } + + /* The next two routines allow a slightly more efficient calculation + * of Vol() for a proposed subbox of a given box. The sum of Top() + * and Bottom() is the Vol() of a subbox split in the given direction + * and with the specified new upper bound. + */ + private long getBottom(Box cube, int dir, long[][][] mmt) { + /* Compute part of Vol(cube, mmt) that doesn't depend on r1, g1, or b1 */ + /* (depending on dir) */ + switch (dir) { + case RED: + return (-mmt[cube.mR0][cube.mG1][cube.mB1] + + mmt[cube.mR0][cube.mG1][cube.mB0] + + mmt[cube.mR0][cube.mG0][cube.mB1] + - mmt[cube.mR0][cube.mG0][cube.mB0]); + case GREEN: + return (-mmt[cube.mR1][cube.mG0][cube.mB1] + + mmt[cube.mR1][cube.mG0][cube.mB0] + + mmt[cube.mR0][cube.mG0][cube.mB1] + - mmt[cube.mR0][cube.mG0][cube.mB0]); + case BLUE: + return (-mmt[cube.mR1][cube.mG1][cube.mB0] + + mmt[cube.mR1][cube.mG0][cube.mB0] + + mmt[cube.mR0][cube.mG1][cube.mB0] + - mmt[cube.mR0][cube.mG0][cube.mB0]); + default: + return 0; + } + } + + private long getTop(Box cube, int dir, int pos, long[][][] mmt) { + /* Compute remainder of Vol(cube, mmt), substituting pos for */ + /* r1, g1, or b1 (depending on dir) */ + switch (dir) { + case RED: + return (mmt[pos][cube.mG1][cube.mB1] + - mmt[pos][cube.mG1][cube.mB0] + - mmt[pos][cube.mG0][cube.mB1] + + mmt[pos][cube.mG0][cube.mB0]); + case GREEN: + return (mmt[cube.mR1][pos][cube.mB1] + - mmt[cube.mR1][pos][cube.mB0] + - mmt[cube.mR0][pos][cube.mB1] + + mmt[cube.mR0][pos][cube.mB0]); + case BLUE: + return (mmt[cube.mR1][cube.mG1][pos] + - mmt[cube.mR1][cube.mG0][pos] + - mmt[cube.mR0][cube.mG1][pos] + + mmt[cube.mR0][cube.mG0][pos]); + default: + return 0; + } + } + + private double getVariance(Box cube) { + /* Compute the weighted variance of a box */ + /* NB: as with the raw statistics, this is really the variance * size */ + double dr, dg, db, xx; + dr = getVolume(cube, mMr); + dg = getVolume(cube, mMg); + db = getVolume(cube, mMb); + xx = + mM2[cube.mR1][cube.mG1][cube.mB1] + - mM2[cube.mR1][cube.mG1][cube.mB0] + - mM2[cube.mR1][cube.mG0][cube.mB1] + + mM2[cube.mR1][cube.mG0][cube.mB0] + - mM2[cube.mR0][cube.mG1][cube.mB1] + + mM2[cube.mR0][cube.mG1][cube.mB0] + + mM2[cube.mR0][cube.mG0][cube.mB1] + - mM2[cube.mR0][cube.mG0][cube.mB0]; + return xx - (dr * dr + dg * dg + db * db) / getVolume(cube, mWt); + } + + /* We want to minimize the sum of the variances of two subboxes. + * The sum(c^2) terms can be ignored since their sum over both subboxes + * is the same (the sum for the whole box) no matter where we split. + * The remaining terms have a minus sign in the variance formula, + * so we drop the minus sign and MAXIMIZE the sum of the two terms. + */ + private double maximize( + Box cube, + int dir, + int first, + int last, + int[] cut, + long wholeR, + long wholeG, + long wholeB, + long wholeW) { + long half_r, half_g, half_b, half_w; + long base_r, base_g, base_b, base_w; + int i; + double temp, max; + + base_r = getBottom(cube, dir, mMr); + base_g = getBottom(cube, dir, mMg); + base_b = getBottom(cube, dir, mMb); + base_w = getBottom(cube, dir, mWt); + + max = 0.0f; + cut[0] = -1; + + for (i = first; i < last; ++i) { + half_r = base_r + getTop(cube, dir, i, mMr); + half_g = base_g + getTop(cube, dir, i, mMg); + half_b = base_b + getTop(cube, dir, i, mMb); + half_w = base_w + getTop(cube, dir, i, mWt); + /* now half_x is sum over lower half of box, if split at i */ + if (half_w == 0) /* subbox could be empty of pixels! */ { + continue; /* never split into an empty box */ + } + temp = (half_r * half_r + half_g * half_g + half_b * half_b) / (double) half_w; + half_r = wholeR - half_r; + half_g = wholeG - half_g; + half_b = wholeB - half_b; + half_w = wholeW - half_w; + if (half_w == 0) /* subbox could be empty of pixels! */ { + continue; /* never split into an empty box */ + } + temp += (half_r * half_r + half_g * half_g + half_b * half_b) / (double) half_w; + + if (temp > max) { + max = temp; + cut[0] = i; + } + } + + return max; + } + + private boolean cut(Box set1, Box set2) { + int dir; + int[] cutr = new int[1]; + int[] cutg = new int[1]; + int[] cutb = new int[1]; + double maxr, maxg, maxb; + long whole_r, whole_g, whole_b, whole_w; + + whole_r = getVolume(set1, mMr); + whole_g = getVolume(set1, mMg); + whole_b = getVolume(set1, mMb); + whole_w = getVolume(set1, mWt); + + maxr = maximize(set1, RED, set1.mR0 + 1, set1.mR1, cutr, whole_r, whole_g, whole_b, + whole_w); + maxg = maximize(set1, GREEN, set1.mG0 + 1, set1.mG1, cutg, whole_r, whole_g, whole_b, + whole_w); + maxb = maximize(set1, BLUE, set1.mB0 + 1, set1.mB1, cutb, whole_r, whole_g, whole_b, + whole_w); + + if (maxr >= maxg && maxr >= maxb) { + dir = RED; + if (cutr[0] < 0) return false; /* can't split the box */ + } else if (maxg >= maxr && maxg >= maxb) { + dir = GREEN; + } else { + dir = BLUE; + } + + set2.mR1 = set1.mR1; + set2.mG1 = set1.mG1; + set2.mB1 = set1.mB1; + + switch (dir) { + case RED: + set2.mR0 = set1.mR1 = cutr[0]; + set2.mG0 = set1.mG0; + set2.mB0 = set1.mB0; + break; + case GREEN: + set2.mG0 = set1.mG1 = cutg[0]; + set2.mR0 = set1.mR0; + set2.mB0 = set1.mB0; + break; + case BLUE: + set2.mB0 = set1.mB1 = cutb[0]; + set2.mR0 = set1.mR0; + set2.mG0 = set1.mG0; + break; + } + set1.mVol = (set1.mR1 - set1.mR0) * (set1.mG1 - set1.mG0) * (set1.mB1 - set1.mB0); + set2.mVol = (set2.mR1 - set2.mR0) * (set2.mG1 - set2.mG0) * (set2.mB1 - set2.mB0); + + return true; + } +} diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index e82cc737a395..342456a58091 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -25,6 +25,9 @@ import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_GPU_DEADL import static android.view.SurfaceControl.JankData.PREDICTION_ERROR; import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING; +import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED; +import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN; + import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.HardwareRendererObserver; @@ -72,6 +75,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener private long mEndVsyncId = INVALID_ID; private boolean mMetricsFinalized; private boolean mCancelled = false; + private FrameTrackerListener mListener; private static class JankInfo { long frameVsyncId; @@ -109,7 +113,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener @NonNull SurfaceControlWrapper surfaceControlWrapper, @NonNull ChoreographerWrapper choreographer, @NonNull FrameMetricsWrapper metrics, int traceThresholdMissedFrames, - int traceThresholdFrameTimeMillis) { + int traceThresholdFrameTimeMillis, @Nullable FrameTrackerListener listener) { mSession = session; mRendererWrapper = renderer; mMetricsWrapper = metrics; @@ -120,6 +124,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler); mTraceThresholdMissedFrames = traceThresholdMissedFrames; mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis; + mListener = listener; // If the surface isn't valid yet, wait until it's created. if (viewRootWrapper.getSurfaceControl().isValid()) { @@ -165,11 +170,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener */ public synchronized void begin() { mBeginVsyncId = mChoreographer.getVsyncId() + 1; + mSession.setTimeStamp(System.nanoTime()); Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId); mRendererWrapper.addObserver(mObserver); if (mSurfaceControl != null) { mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl); } + if (mListener != null) { + mListener.onNotifyCujEvents(mSession, ACTION_SESSION_BEGIN); + } } /** @@ -224,7 +233,6 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener } private boolean isInRange(long vsyncId) { - // It's possible that we may miss a callback for the frame with vsyncId == mEndVsyncId. // Because of that, we collect all frames even if they happen after the end so we eventually // have a frame after the end with both callbacks present. @@ -371,6 +379,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener missedAppFramesCount + missedSfFramesCounts, maxFrameTimeNanos, missedSfFramesCounts); + if (mListener != null) { + mListener.onNotifyCujEvents(mSession, ACTION_METRICS_LOGGED); + } } if (DEBUG) { Log.i(TAG, "FrameTracker: CUJ=" + mSession.getName() @@ -495,4 +506,17 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener return mChoreographer.getVsyncId(); } } + + /** + * A listener that notifies cuj events. + */ + public interface FrameTrackerListener { + /** + * Notify that the CUJ session was created. + * + * @param session the CUJ session + * @param action the specific action + */ + void onNotifyCujEvents(Session session, String action); + } } diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index cba6af98a980..0294ec398484 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -16,6 +16,8 @@ package com.android.internal.jank; +import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY; + import static com.android.internal.jank.FrameTracker.ChoreographerWrapper; import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL; @@ -48,9 +50,12 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import android.annotation.IntDef; import android.annotation.NonNull; +import android.content.Context; +import android.content.Intent; import android.os.Build; import android.os.HandlerExecutor; import android.os.HandlerThread; +import android.os.SystemProperties; import android.provider.DeviceConfig; import android.util.Log; import android.util.SparseArray; @@ -59,6 +64,7 @@ import android.view.View; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; +import com.android.internal.jank.FrameTracker.FrameTrackerListener; import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; import com.android.internal.jank.FrameTracker.ViewRootWrapper; import com.android.internal.util.PerfettoTrigger; @@ -74,6 +80,8 @@ import java.util.concurrent.TimeUnit; */ public class InteractionJankMonitor { private static final String TAG = InteractionJankMonitor.class.getSimpleName(); + private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName(); + private static final String DEFAULT_WORKER_NAME = TAG + "-Worker"; private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L); private static final String SETTINGS_ENABLED_KEY = "enabled"; @@ -90,6 +98,14 @@ public class InteractionJankMonitor { private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3; private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64; + public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN"; + public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END"; + public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED"; + public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME"; + public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP"; + @VisibleForTesting + public static final String PROP_NOTIFY_CUJ_EVENT = "debug.notify_cuj_events"; + // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE. public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0; public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1; @@ -256,15 +272,28 @@ public class InteractionJankMonitor { */ @VisibleForTesting public FrameTracker createFrameTracker(View v, Session session) { + final Context c = v.getContext().getApplicationContext(); synchronized (this) { + boolean needListener = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false); + FrameTrackerListener eventsListener = + !needListener ? null : (s, act) -> notifyEvents(c, act, s); + return new FrameTracker(session, mWorker.getThreadHandler(), new ThreadedRendererWrapper(v.getThreadedRenderer()), new ViewRootWrapper(v.getViewRootImpl()), new SurfaceControlWrapper(), new ChoreographerWrapper(Choreographer.getInstance()), mMetrics, - mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis); + mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, eventsListener); } } + private void notifyEvents(Context context, String action, Session session) { + Intent intent = new Intent(action); + intent.putExtra(BUNDLE_KEY_CUJ_NAME, getNameOfCuj(session.getCuj())); + intent.putExtra(BUNDLE_KEY_TIMESTAMP, session.getTimeStamp()); + intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY); + context.sendBroadcast(intent); + } + /** * Begin a trace session. * @@ -479,6 +508,7 @@ public class InteractionJankMonitor { public static class Session { @CujType private int mCujType; + private long mTimeStamp; public Session(@CujType int cujType) { mCujType = cujType; @@ -505,5 +535,13 @@ public class InteractionJankMonitor { public String getName() { return "J<" + getNameOfCuj(mCujType) + ">"; } + + public void setTimeStamp(long timeStamp) { + mTimeStamp = timeStamp; + } + + public long getTimeStamp() { + return mTimeStamp; + } } } diff --git a/core/java/com/android/internal/listeners/ListenerTransportManager.java b/core/java/com/android/internal/listeners/ListenerTransportManager.java index 0d5d1b7b53ff..d5b561939836 100644 --- a/core/java/com/android/internal/listeners/ListenerTransportManager.java +++ b/core/java/com/android/internal/listeners/ListenerTransportManager.java @@ -17,6 +17,7 @@ package com.android.internal.listeners; import android.os.RemoteException; +import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; @@ -36,13 +37,17 @@ public abstract class ListenerTransportManager<TTransport extends ListenerTransp @GuardedBy("mRegistrations") private final Map<Object, WeakReference<TTransport>> mRegistrations; - protected ListenerTransportManager() { + protected ListenerTransportManager(boolean allowServerSideTransportRemoval) { // using weakhashmap means that the transport may be GCed if the server drops its reference, // and thus the listener may be GCed as well if the client drops that reference. if the // server will never drop a reference without warning (ie, transport removal may only be // initiated from the client side), then arraymap or similar may be used without fear of // memory leaks. - mRegistrations = new WeakHashMap<>(); + if (allowServerSideTransportRemoval) { + mRegistrations = new WeakHashMap<>(); + } else { + mRegistrations = new ArrayMap<>(); + } } /** @@ -53,16 +58,21 @@ public abstract class ListenerTransportManager<TTransport extends ListenerTransp synchronized (mRegistrations) { // ordering of operations is important so that if an error occurs at any point we // are left in a reasonable state - registerTransport(transport); - WeakReference<TTransport> oldTransportRef = mRegistrations.put(key, - new WeakReference<>(transport)); + TTransport oldTransport; + WeakReference<TTransport> oldTransportRef = mRegistrations.get(key); if (oldTransportRef != null) { - TTransport oldTransport = oldTransportRef.get(); - if (oldTransport != null) { - oldTransport.unregister(); - unregisterTransport(oldTransport); - } + oldTransport = oldTransportRef.get(); + } else { + oldTransport = null; + } + + if (oldTransport == null) { + registerTransport(transport); + } else { + registerTransport(transport, oldTransport); + oldTransport.unregister(); } + mRegistrations.put(key, new WeakReference<>(transport)); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -91,7 +101,33 @@ public abstract class ListenerTransportManager<TTransport extends ListenerTransp } } + /** + * Registers a new transport. + */ protected abstract void registerTransport(TTransport transport) throws RemoteException; + /** + * Registers a new transport that is replacing the given old transport. Implementations must + * ensure that if they throw a remote exception, the call does not have any side effects. + */ + protected void registerTransport(TTransport transport, TTransport oldTransport) + throws RemoteException { + registerTransport(transport); + try { + unregisterTransport(oldTransport); + } catch (RemoteException e) { + try { + // best effort to ensure there are no side effects + unregisterTransport(transport); + } catch (RemoteException suppressed) { + e.addSuppressed(suppressed); + } + throw e; + } + } + + /** + * Unregisters an existing transport. + */ protected abstract void unregisterTransport(TTransport transport) throws RemoteException; } diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java index 13130902feb8..e153eb2f0933 100644 --- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java +++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java @@ -46,14 +46,12 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); - final double powerMah = mPowerEstimator.calculatePower(durationMs); - if (powerMah > 0) { - builder.getOrCreateSystemBatteryConsumerBuilder( - SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs); - } - // TODO(b/178140704): Attribute *measured* total usage for BatteryUsageStats. + final double powerMah = getMeasuredOrEstimatedPower(batteryStats.getScreenDozeEnergy(), + mPowerEstimator, durationMs, query.shouldForceUsePowerProfileModel()); + builder.getOrCreateSystemBatteryConsumerBuilder( + SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs); } /** @@ -66,8 +64,8 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { final long durationMs = calculateDuration(batteryStats, rawRealtimeUs, statsType); - final double powerMah = getMeasuredOrEstimatedPower( - batteryStats.getScreenDozeEnergy(), durationMs); + final double powerMah = getMeasuredOrEstimatedPower(batteryStats.getScreenDozeEnergy(), + mPowerEstimator, durationMs, false); if (powerMah > 0) { BatterySipper bs = new BatterySipper(BatterySipper.DrainType.AMBIENT_DISPLAY, null, 0); bs.usagePowerMah = powerMah; @@ -81,11 +79,4 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { return batteryStats.getScreenDozeTime(rawRealtimeUs, statsType) / 1000; } - private double getMeasuredOrEstimatedPower(long measuredEnergyUJ, long durationMs) { - if (measuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { - return uJtoMah(measuredEnergyUJ); - } else { - return mPowerEstimator.calculatePower(durationMs); - } - } } diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index bdb65f33ad1a..879e0a8cfe10 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -16,6 +16,8 @@ package com.android.internal.os; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.BatteryStats; import android.os.Parcel; import android.os.StatFs; @@ -61,6 +63,7 @@ public class BatteryStatsHistory { public static final String FILE_SUFFIX = ".bin"; private static final int MIN_FREE_SPACE = 100 * 1024 * 1024; + @Nullable private final BatteryStatsImpl mStats; private final Parcel mHistoryBuffer; private final File mHistoryDir; @@ -107,7 +110,8 @@ public class BatteryStatsHistory { * @param systemDir typically /data/system * @param historyBuffer The in-memory history buffer. */ - public BatteryStatsHistory(BatteryStatsImpl stats, File systemDir, Parcel historyBuffer) { + public BatteryStatsHistory(@NonNull BatteryStatsImpl stats, File systemDir, + Parcel historyBuffer) { mStats = stats; mHistoryBuffer = historyBuffer; mHistoryDir = new File(systemDir, HISTORY_DIR); @@ -149,11 +153,10 @@ public class BatteryStatsHistory { /** * Used when BatteryStatsImpl object is created from deserialization of a parcel, * such as Settings app or checkin file. - * @param stats BatteryStatsImpl object. - * @param historyBuffer the history buffer inside BatteryStatsImpl + * @param historyBuffer the history buffer */ - public BatteryStatsHistory(BatteryStatsImpl stats, Parcel historyBuffer) { - mStats = stats; + public BatteryStatsHistory(Parcel historyBuffer) { + mStats = null; mHistoryDir = null; mHistoryBuffer = historyBuffer; } @@ -184,10 +187,16 @@ public class BatteryStatsHistory { * create next history file. */ public void startNextFile() { + if (mStats == null) { + Slog.wtf(TAG, "mStats should not be null when writing history"); + return; + } + if (mFileNumbers.isEmpty()) { Slog.wtf(TAG, "mFileNumbers should never be empty"); return; } + // The last number in mFileNumbers is the highest number. The next file number is highest // number plus one. final int next = mFileNumbers.get(mFileNumbers.size() - 1) + 1; @@ -357,7 +366,7 @@ public class BatteryStatsHistory { private boolean skipHead(Parcel p) { p.setDataPosition(0); final int version = p.readInt(); - if (version != mStats.VERSION) { + if (version != BatteryStatsImpl.VERSION) { return false; } // skip historyBaseTime field. diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java new file mode 100644 index 000000000000..1871ac5074c9 --- /dev/null +++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java @@ -0,0 +1,264 @@ +/* + * 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.internal.os; + +import android.annotation.NonNull; +import android.os.BatteryManager; +import android.os.BatteryStats; +import android.os.Parcel; +import android.util.Slog; + +import java.util.List; + +/** + * An iterator for {@link BatteryStats.HistoryItem}'s. + */ +public class BatteryStatsHistoryIterator { + private static final boolean DEBUG = false; + private static final String TAG = "BatteryStatsHistoryItr"; + private final BatteryStatsHistory mBatteryStatsHistory; + private final BatteryStats.HistoryStepDetails mReadHistoryStepDetails = + new BatteryStats.HistoryStepDetails(); + private final String[] mReadHistoryStrings; + private final int[] mReadHistoryUids; + + public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, + @NonNull List<BatteryStats.HistoryTag> historyTagPool) { + mBatteryStatsHistory = history; + + mBatteryStatsHistory.startIteratingHistory(); + + mReadHistoryStrings = new String[historyTagPool.size()]; + mReadHistoryUids = new int[historyTagPool.size()]; + for (int i = historyTagPool.size() - 1; i >= 0; i--) { + BatteryStats.HistoryTag tag = historyTagPool.get(i); + final int idx = tag.poolIdx; + mReadHistoryStrings[idx] = tag.string; + mReadHistoryUids[idx] = tag.uid; + } + } + + /** + * Retrieves the next HistoryItem from battery history, if available. Returns false if there + * are no more items. + */ + public boolean next(BatteryStats.HistoryItem out) { + Parcel p = mBatteryStatsHistory.getNextParcel(out); + if (p == null) { + mBatteryStatsHistory.finishIteratingHistory(); + return false; + } + + final long lastRealtimeMs = out.time; + final long lastWalltimeMs = out.currentTime; + readHistoryDelta(p, out); + if (out.cmd != BatteryStats.HistoryItem.CMD_CURRENT_TIME + && out.cmd != BatteryStats.HistoryItem.CMD_RESET && lastWalltimeMs != 0) { + out.currentTime = lastWalltimeMs + (out.time - lastRealtimeMs); + } + return true; + } + + void readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur) { + int firstToken = src.readInt(); + int deltaTimeToken = firstToken & BatteryStatsImpl.DELTA_TIME_MASK; + cur.cmd = BatteryStats.HistoryItem.CMD_UPDATE; + cur.numReadInts = 1; + if (DEBUG) { + Slog.i(TAG, "READ DELTA: firstToken=0x" + Integer.toHexString(firstToken) + + " deltaTimeToken=" + deltaTimeToken); + } + + if (deltaTimeToken < BatteryStatsImpl.DELTA_TIME_ABS) { + cur.time += deltaTimeToken; + } else if (deltaTimeToken == BatteryStatsImpl.DELTA_TIME_ABS) { + cur.readFromParcel(src); + if (DEBUG) Slog.i(TAG, "READ DELTA: ABS time=" + cur.time); + return; + } else if (deltaTimeToken == BatteryStatsImpl.DELTA_TIME_INT) { + int delta = src.readInt(); + cur.time += delta; + cur.numReadInts += 1; + if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time); + } else { + long delta = src.readLong(); + if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time); + cur.time += delta; + cur.numReadInts += 2; + } + + final int batteryLevelInt; + if ((firstToken & BatteryStatsImpl.DELTA_BATTERY_LEVEL_FLAG) != 0) { + batteryLevelInt = src.readInt(); + readBatteryLevelInt(batteryLevelInt, cur); + cur.numReadInts += 1; + if (DEBUG) { + Slog.i(TAG, "READ DELTA: batteryToken=0x" + + Integer.toHexString(batteryLevelInt) + + " batteryLevel=" + cur.batteryLevel + + " batteryTemp=" + cur.batteryTemperature + + " batteryVolt=" + (int) cur.batteryVoltage); + } + } else { + batteryLevelInt = 0; + } + + if ((firstToken & BatteryStatsImpl.DELTA_STATE_FLAG) != 0) { + int stateInt = src.readInt(); + cur.states = (firstToken & BatteryStatsImpl.DELTA_STATE_MASK) | (stateInt + & (~BatteryStatsImpl.STATE_BATTERY_MASK)); + cur.batteryStatus = (byte) ((stateInt >> BatteryStatsImpl.STATE_BATTERY_STATUS_SHIFT) + & BatteryStatsImpl.STATE_BATTERY_STATUS_MASK); + cur.batteryHealth = (byte) ((stateInt >> BatteryStatsImpl.STATE_BATTERY_HEALTH_SHIFT) + & BatteryStatsImpl.STATE_BATTERY_HEALTH_MASK); + cur.batteryPlugType = (byte) ((stateInt >> BatteryStatsImpl.STATE_BATTERY_PLUG_SHIFT) + & BatteryStatsImpl.STATE_BATTERY_PLUG_MASK); + switch (cur.batteryPlugType) { + case 1: + cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_AC; + break; + case 2: + cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_USB; + break; + case 3: + cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS; + break; + } + cur.numReadInts += 1; + if (DEBUG) { + Slog.i(TAG, "READ DELTA: stateToken=0x" + + Integer.toHexString(stateInt) + + " batteryStatus=" + cur.batteryStatus + + " batteryHealth=" + cur.batteryHealth + + " batteryPlugType=" + cur.batteryPlugType + + " states=0x" + Integer.toHexString(cur.states)); + } + } else { + cur.states = (firstToken & BatteryStatsImpl.DELTA_STATE_MASK) | (cur.states + & (~BatteryStatsImpl.STATE_BATTERY_MASK)); + } + + if ((firstToken & BatteryStatsImpl.DELTA_STATE2_FLAG) != 0) { + cur.states2 = src.readInt(); + if (DEBUG) { + Slog.i(TAG, "READ DELTA: states2=0x" + + Integer.toHexString(cur.states2)); + } + } + + if ((firstToken & BatteryStatsImpl.DELTA_WAKELOCK_FLAG) != 0) { + int indexes = src.readInt(); + int wakeLockIndex = indexes & 0xffff; + int wakeReasonIndex = (indexes >> 16) & 0xffff; + if (wakeLockIndex != 0xffff) { + cur.wakelockTag = cur.localWakelockTag; + readHistoryTag(wakeLockIndex, cur.wakelockTag); + if (DEBUG) { + Slog.i(TAG, "READ DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx + + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string); + } + } else { + cur.wakelockTag = null; + } + if (wakeReasonIndex != 0xffff) { + cur.wakeReasonTag = cur.localWakeReasonTag; + readHistoryTag(wakeReasonIndex, cur.wakeReasonTag); + if (DEBUG) { + Slog.i(TAG, "READ DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx + + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string); + } + } else { + cur.wakeReasonTag = null; + } + cur.numReadInts += 1; + } else { + cur.wakelockTag = null; + cur.wakeReasonTag = null; + } + + if ((firstToken & BatteryStatsImpl.DELTA_EVENT_FLAG) != 0) { + cur.eventTag = cur.localEventTag; + final int codeAndIndex = src.readInt(); + cur.eventCode = (codeAndIndex & 0xffff); + final int index = ((codeAndIndex >> 16) & 0xffff); + readHistoryTag(index, cur.eventTag); + cur.numReadInts += 1; + if (DEBUG) { + Slog.i(TAG, "READ DELTA: event=" + cur.eventCode + " tag=#" + + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":" + + cur.eventTag.string); + } + } else { + cur.eventCode = BatteryStats.HistoryItem.EVENT_NONE; + } + + if ((batteryLevelInt & BatteryStatsImpl.BATTERY_DELTA_LEVEL_FLAG) != 0) { + cur.stepDetails = mReadHistoryStepDetails; + cur.stepDetails.readFromParcel(src); + } else { + cur.stepDetails = null; + } + + if ((firstToken & BatteryStatsImpl.DELTA_BATTERY_CHARGE_FLAG) != 0) { + cur.batteryChargeUah = src.readInt(); + } + cur.modemRailChargeMah = src.readDouble(); + cur.wifiRailChargeMah = src.readDouble(); + } + + int getHistoryStringPoolSize() { + return mReadHistoryStrings.length; + } + + int getHistoryStringPoolBytes() { + int totalChars = 0; + for (int i = mReadHistoryStrings.length - 1; i >= 0; i--) { + if (mReadHistoryStrings[i] != null) { + totalChars += mReadHistoryStrings[i].length() + 1; + } + } + + // Each entry is a fixed 12 bytes: 4 for index, 4 for uid, 4 for string size + // Each string character is 2 bytes. + return (mReadHistoryStrings.length * 12) + (totalChars * 2); + } + + String getHistoryTagPoolString(int index) { + return mReadHistoryStrings[index]; + } + + int getHistoryTagPoolUid(int index) { + return mReadHistoryUids[index]; + } + + private void readHistoryTag(int index, BatteryStats.HistoryTag tag) { + if (index < mReadHistoryStrings.length) { + tag.string = mReadHistoryStrings[index]; + tag.uid = mReadHistoryUids[index]; + } else { + tag.string = null; + tag.uid = 0; + } + tag.poolIdx = index; + } + + private static void readBatteryLevelInt(int batteryLevelInt, BatteryStats.HistoryItem out) { + out.batteryLevel = (byte) ((batteryLevelInt & 0xfe000000) >>> 25); + out.batteryTemperature = (short) ((batteryLevelInt & 0x01ff8000) >>> 15); + out.batteryVoltage = (char) ((batteryLevelInt & 0x00007ffe) >>> 1); + } +} diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index e599888c3bee..73527d4a80d8 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -81,7 +81,6 @@ import android.util.IndentingPrintWriter; import android.util.IntArray; import android.util.KeyValueListParser; import android.util.Log; -import android.util.LogWriter; import android.util.LongSparseArray; import android.util.LongSparseLongArray; import android.util.MutableInt; @@ -108,7 +107,6 @@ import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThrea import com.android.internal.power.MeasuredEnergyStats; import com.android.internal.power.MeasuredEnergyStats.StandardEnergyBucket; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; import com.android.net.module.util.NetworkCapabilitiesUtils; @@ -164,7 +162,6 @@ public class BatteryStatsImpl extends BatteryStats { private static final boolean DEBUG_BINDER_STATS = false; private static final boolean DEBUG_MEMORY = false; private static final boolean DEBUG_HISTORY = false; - private static final boolean USE_OLD_HISTORY = false; // for debugging. // TODO: remove "tcp" from network methods, since we measure total stats. @@ -172,7 +169,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 193 + (USE_OLD_HISTORY ? 1000 : 0); + static final int VERSION = 193; // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -740,15 +737,11 @@ public class BatteryStatsImpl extends BatteryStats { protected boolean mRecordingHistory = false; int mNumHistoryItems; + final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>(); final Parcel mHistoryBuffer = Parcel.obtain(); final HistoryItem mHistoryLastWritten = new HistoryItem(); final HistoryItem mHistoryLastLastWritten = new HistoryItem(); - final HistoryItem mHistoryReadTmp = new HistoryItem(); final HistoryItem mHistoryAddTmp = new HistoryItem(); - final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<>(); - String[] mReadHistoryStrings; - int[] mReadHistoryUids; - int mReadHistoryChars; int mNextHistoryTagIdx = 0; int mNumHistoryTagChars = 0; int mHistoryBufferLastPos = -1; @@ -767,7 +760,7 @@ public class BatteryStatsImpl extends BatteryStats { // Last recorded battery energy capacity. // This is used for computing foregrund power per application. // See: PowerForUid below - private long mLastBatteryEnergyCapacityNWh = 0; + private long mLastBatteryEnergyCapacityNwh = 0; private static final class PowerForUid { public long energyNwh = 0; @@ -789,6 +782,7 @@ public class BatteryStatsImpl extends BatteryStats { private final HashMap<Integer, PowerForUid> mUidToPower = ENABLE_FOREGROUND_STATS_COLLECTION ? new HashMap<>() : null; + @NonNull final BatteryStatsHistory mBatteryStatsHistory; final HistoryItem mHistoryCur = new HistoryItem(); @@ -831,9 +825,9 @@ public class BatteryStatsImpl extends BatteryStats { long mCurStepStatSoftIrqTimeMs; long mCurStepStatIdleTimeMs; + private BatteryStatsHistoryIterator mBatteryStatsHistoryIterator; private HistoryItem mHistoryIterator; private boolean mReadOverflow; - private boolean mIteratingHistory; int mStartCount; @@ -1089,12 +1083,12 @@ public class BatteryStatsImpl extends BatteryStats { private int mNumConnectivityChange; - private int mBatteryVolt = -1; - private int mBatteryCharge = -1; - private int mEstimatedBatteryCapacity = -1; + private int mBatteryVoltageMv = -1; + private int mBatteryChargeUah = -1; + private int mEstimatedBatteryCapacityMah = -1; - private int mMinLearnedBatteryCapacity = -1; - private int mMaxLearnedBatteryCapacity = -1; + private int mMinLearnedBatteryCapacityUah = -1; + private int mMaxLearnedBatteryCapacityUah = -1; private long mBatteryTimeToFullSeconds = -1; @@ -1175,17 +1169,17 @@ public class BatteryStatsImpl extends BatteryStats { @Override public int getEstimatedBatteryCapacity() { - return mEstimatedBatteryCapacity; + return mEstimatedBatteryCapacityMah; } @Override public int getMinLearnedBatteryCapacity() { - return mMinLearnedBatteryCapacity; + return mMinLearnedBatteryCapacityUah; } @Override public int getMaxLearnedBatteryCapacity() { - return mMaxLearnedBatteryCapacity; + return mMaxLearnedBatteryCapacityUah; } public BatteryStatsImpl() { @@ -1197,7 +1191,7 @@ public class BatteryStatsImpl extends BatteryStats { mStatsFile = null; mCheckinFile = null; mDailyFile = null; - mBatteryStatsHistory = null; + mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer); mHandler = null; mPlatformIdleStateCallback = null; mMeasuredEnergyRetriever = null; @@ -3244,17 +3238,6 @@ public class BatteryStatsImpl extends BatteryStats { return idx; } - private void readHistoryTag(int index, HistoryTag tag) { - if (index < mReadHistoryStrings.length) { - tag.string = mReadHistoryStrings[index]; - tag.uid = mReadHistoryUids[index]; - } else { - tag.string = null; - tag.uid = 0; - } - tag.poolIdx = index; - } - /* The history delta format uses flags to denote further data in subsequent ints in the parcel. @@ -3416,7 +3399,7 @@ public class BatteryStatsImpl extends BatteryStats { firstToken |= DELTA_EVENT_FLAG; } - final boolean batteryChargeChanged = cur.batteryChargeUAh != last.batteryChargeUAh; + final boolean batteryChargeChanged = cur.batteryChargeUah != last.batteryChargeUah; if (batteryChargeChanged) { firstToken |= DELTA_BATTERY_CHARGE_FLAG; } @@ -3505,8 +3488,8 @@ public class BatteryStatsImpl extends BatteryStats { mLastHistoryStepLevel = cur.batteryLevel; if (batteryChargeChanged) { - if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUAh=" + cur.batteryChargeUAh); - dest.writeInt(cur.batteryChargeUAh); + if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryChargeUah=" + cur.batteryChargeUah); + dest.writeInt(cur.batteryChargeUah); } dest.writeDouble(cur.modemRailChargeMah); dest.writeDouble(cur.wifiRailChargeMah); @@ -3631,137 +3614,6 @@ public class BatteryStatsImpl extends BatteryStats { mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs; } - public void readHistoryDelta(Parcel src, HistoryItem cur) { - int firstToken = src.readInt(); - int deltaTimeToken = firstToken&DELTA_TIME_MASK; - cur.cmd = HistoryItem.CMD_UPDATE; - cur.numReadInts = 1; - if (DEBUG) Slog.i(TAG, "READ DELTA: firstToken=0x" + Integer.toHexString(firstToken) - + " deltaTimeToken=" + deltaTimeToken); - - if (deltaTimeToken < DELTA_TIME_ABS) { - cur.time += deltaTimeToken; - } else if (deltaTimeToken == DELTA_TIME_ABS) { - cur.readFromParcel(src); - if (DEBUG) Slog.i(TAG, "READ DELTA: ABS time=" + cur.time); - return; - } else if (deltaTimeToken == DELTA_TIME_INT) { - int delta = src.readInt(); - cur.time += delta; - cur.numReadInts += 1; - if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time); - } else { - long delta = src.readLong(); - if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + cur.time); - cur.time += delta; - cur.numReadInts += 2; - } - - final int batteryLevelInt; - if ((firstToken&DELTA_BATTERY_LEVEL_FLAG) != 0) { - batteryLevelInt = src.readInt(); - readBatteryLevelInt(batteryLevelInt, cur); - cur.numReadInts += 1; - if (DEBUG) Slog.i(TAG, "READ DELTA: batteryToken=0x" - + Integer.toHexString(batteryLevelInt) - + " batteryLevel=" + cur.batteryLevel - + " batteryTemp=" + cur.batteryTemperature - + " batteryVolt=" + (int)cur.batteryVoltage); - } else { - batteryLevelInt = 0; - } - - if ((firstToken&DELTA_STATE_FLAG) != 0) { - int stateInt = src.readInt(); - cur.states = (firstToken&DELTA_STATE_MASK) | (stateInt&(~STATE_BATTERY_MASK)); - cur.batteryStatus = (byte)((stateInt>>STATE_BATTERY_STATUS_SHIFT) - & STATE_BATTERY_STATUS_MASK); - cur.batteryHealth = (byte)((stateInt>>STATE_BATTERY_HEALTH_SHIFT) - & STATE_BATTERY_HEALTH_MASK); - cur.batteryPlugType = (byte)((stateInt>>STATE_BATTERY_PLUG_SHIFT) - & STATE_BATTERY_PLUG_MASK); - switch (cur.batteryPlugType) { - case 1: - cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_AC; - break; - case 2: - cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_USB; - break; - case 3: - cur.batteryPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS; - break; - } - cur.numReadInts += 1; - if (DEBUG) Slog.i(TAG, "READ DELTA: stateToken=0x" - + Integer.toHexString(stateInt) - + " batteryStatus=" + cur.batteryStatus - + " batteryHealth=" + cur.batteryHealth - + " batteryPlugType=" + cur.batteryPlugType - + " states=0x" + Integer.toHexString(cur.states)); - } else { - cur.states = (firstToken&DELTA_STATE_MASK) | (cur.states&(~STATE_BATTERY_MASK)); - } - - if ((firstToken&DELTA_STATE2_FLAG) != 0) { - cur.states2 = src.readInt(); - if (DEBUG) Slog.i(TAG, "READ DELTA: states2=0x" - + Integer.toHexString(cur.states2)); - } - - if ((firstToken&DELTA_WAKELOCK_FLAG) != 0) { - int indexes = src.readInt(); - int wakeLockIndex = indexes&0xffff; - int wakeReasonIndex = (indexes>>16)&0xffff; - if (wakeLockIndex != 0xffff) { - cur.wakelockTag = cur.localWakelockTag; - readHistoryTag(wakeLockIndex, cur.wakelockTag); - if (DEBUG) Slog.i(TAG, "READ DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx - + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string); - } else { - cur.wakelockTag = null; - } - if (wakeReasonIndex != 0xffff) { - cur.wakeReasonTag = cur.localWakeReasonTag; - readHistoryTag(wakeReasonIndex, cur.wakeReasonTag); - if (DEBUG) Slog.i(TAG, "READ DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx - + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string); - } else { - cur.wakeReasonTag = null; - } - cur.numReadInts += 1; - } else { - cur.wakelockTag = null; - cur.wakeReasonTag = null; - } - - if ((firstToken&DELTA_EVENT_FLAG) != 0) { - cur.eventTag = cur.localEventTag; - final int codeAndIndex = src.readInt(); - cur.eventCode = (codeAndIndex&0xffff); - final int index = ((codeAndIndex>>16)&0xffff); - readHistoryTag(index, cur.eventTag); - cur.numReadInts += 1; - if (DEBUG) Slog.i(TAG, "READ DELTA: event=" + cur.eventCode + " tag=#" - + cur.eventTag.poolIdx + " " + cur.eventTag.uid + ":" - + cur.eventTag.string); - } else { - cur.eventCode = HistoryItem.EVENT_NONE; - } - - if ((batteryLevelInt&BATTERY_DELTA_LEVEL_FLAG) != 0) { - cur.stepDetails = mReadHistoryStepDetails; - cur.stepDetails.readFromParcel(src); - } else { - cur.stepDetails = null; - } - - if ((firstToken&DELTA_BATTERY_CHARGE_FLAG) != 0) { - cur.batteryChargeUAh = src.readInt(); - } - cur.modemRailChargeMah = src.readDouble(); - cur.wifiRailChargeMah = src.readDouble(); - } - @Override public void commitCurrentHistoryBatchLocked() { mHistoryLastWritten.cmd = HistoryItem.CMD_NULL; @@ -3873,7 +3725,7 @@ public class BatteryStatsImpl extends BatteryStats { } private void addHistoryBufferLocked(long elapsedRealtimeMs, byte cmd, HistoryItem cur) { - if (mIteratingHistory) { + if (mBatteryStatsHistoryIterator != null) { throw new IllegalStateException("Can't do this while iterating history!"); } mHistoryBufferLastPos = mHistoryBuffer.dataPosition(); @@ -3917,44 +3769,6 @@ public class BatteryStatsImpl extends BatteryStats { void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) { addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur); - - if (!USE_OLD_HISTORY) { - return; - } - - if (!mHaveBatteryLevel || !mRecordingHistory) { - return; - } - - // If the current time is basically the same as the last time, - // and no states have since the last recorded entry changed and - // are now resetting back to their original value, then just collapse - // into one record. - if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE - && (mHistoryBaseTimeMs + elapsedRealtimeMs) < (mHistoryEnd.time + 1000) - && ((mHistoryEnd.states^cur.states)&mChangedStates&mActiveHistoryStates) == 0 - && ((mHistoryEnd.states2^cur.states2)&mChangedStates2&mActiveHistoryStates2) == 0) { - // If the current is the same as the one before, then we no - // longer need the entry. - if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE - && (mHistoryBaseTimeMs + elapsedRealtimeMs) < (mHistoryEnd.time + 500) - && mHistoryLastEnd.sameNonEvent(cur)) { - mHistoryLastEnd.next = null; - mHistoryEnd.next = mHistoryCache; - mHistoryCache = mHistoryEnd; - mHistoryEnd = mHistoryLastEnd; - mHistoryLastEnd = null; - } else { - mChangedStates |= mHistoryEnd.states^(cur.states&mActiveHistoryStates); - mChangedStates2 |= mHistoryEnd.states^(cur.states2&mActiveHistoryStates2); - mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, cur); - } - return; - } - - mChangedStates = 0; - mChangedStates2 = 0; - addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur); } public void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code, @@ -3992,15 +3806,6 @@ public class BatteryStatsImpl extends BatteryStats { void clearHistoryLocked() { if (DEBUG_HISTORY) Slog.i(TAG, "********** CLEARING HISTORY!"); - if (USE_OLD_HISTORY) { - if (mHistory != null) { - mHistoryEnd.next = mHistoryCache; - mHistoryCache = mHistory; - mHistory = mHistoryLastEnd = mHistoryEnd = null; - } - mNumHistoryItems = 0; - } - mHistoryBaseTimeMs = 0; mLastHistoryElapsedRealtimeMs = 0; mTrackRunningHistoryElapsedRealtimeMs = 0; @@ -7222,6 +7027,10 @@ public class BatteryStatsImpl extends BatteryStats { return mOnBattery; } + @Override public long getStatsStartRealtime() { + return mRealtimeStartUs; + } + @UnsupportedAppUsage @Override public SparseArray<? extends BatteryStats.Uid> getUidStats() { return mUidStats; @@ -7961,16 +7770,14 @@ public class BatteryStatsImpl extends BatteryStats { /** Adds the given energy to the given standard energy bucket for this uid. */ private void addEnergyToStandardBucketLocked(long energyDeltaUJ, - @StandardEnergyBucket int energyBucket, boolean accumulate) { + @StandardEnergyBucket int energyBucket) { getOrCreateMeasuredEnergyStatsLocked() - .updateStandardBucket(energyBucket, energyDeltaUJ, accumulate); + .updateStandardBucket(energyBucket, energyDeltaUJ); } /** Adds the given energy to the given custom energy bucket for this uid. */ - private void addEnergyToCustomBucketLocked(long energyDeltaUJ, int energyBucket, - boolean accumulate) { - getOrCreateMeasuredEnergyStatsLocked() - .updateCustomBucket(energyBucket, energyDeltaUJ, accumulate); + private void addEnergyToCustomBucketLocked(long energyDeltaUJ, int energyBucket) { + getOrCreateMeasuredEnergyStatsLocked().updateCustomBucket(energyBucket, energyDeltaUJ); } /** @@ -10729,7 +10536,7 @@ public class BatteryStatsImpl extends BatteryStats { if (systemDir == null) { mStatsFile = null; - mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer); + mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer); } else { mStatsFile = new AtomicFile(new File(systemDir, "batterystats.bin")); mBatteryStatsHistory = new BatteryStatsHistory(this, systemDir, mHistoryBuffer); @@ -10745,11 +10552,6 @@ public class BatteryStatsImpl extends BatteryStats { long realtimeUs = mClocks.elapsedRealtime() * 1000; initTimes(uptimeUs, realtimeUs); mStartPlatformVersion = mEndPlatformVersion = Build.ID; - mDischargeStartLevel = 0; - mDischargeUnplugLevel = 0; - mDischargePlugLevel = -1; - mDischargeCurrentLevel = 0; - mCurrentBatteryLevel = 0; initDischarge(realtimeUs); clearHistoryLocked(); updateDailyDeadlineLocked(); @@ -10835,6 +10637,11 @@ public class BatteryStatsImpl extends BatteryStats { mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase); mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase); mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase); + mDischargeStartLevel = 0; + mDischargeUnplugLevel = 0; + mDischargePlugLevel = -1; + mDischargeCurrentLevel = 0; + mCurrentBatteryLevel = 0; } @UnsupportedAppUsage @@ -10851,7 +10658,7 @@ public class BatteryStatsImpl extends BatteryStats { mExternalSync = null; mConstants = new Constants(mHandler); clearHistoryLocked(); - mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer); + mBatteryStatsHistory = new BatteryStatsHistory(mHistoryBuffer); readFromParcel(p); mPlatformIdleStateCallback = null; mMeasuredEnergyRetriever = null; @@ -10873,9 +10680,9 @@ public class BatteryStatsImpl extends BatteryStats { firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i); } - if (mEstimatedBatteryCapacity == -1) { + if (mEstimatedBatteryCapacityMah == -1) { // Initialize the estimated battery capacity to a known preset one. - mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity(); + mEstimatedBatteryCapacityMah = (int) mPowerProfile.getBatteryCapacity(); } } @@ -11218,60 +11025,6 @@ public class BatteryStatsImpl extends BatteryStats { return mNextMaxDailyDeadlineMs; } - @Override - public boolean startIteratingOldHistoryLocked() { - if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize() - + " pos=" + mHistoryBuffer.dataPosition()); - if ((mHistoryIterator = mHistory) == null) { - return false; - } - mHistoryBuffer.setDataPosition(0); - mHistoryReadTmp.clear(); - mReadOverflow = false; - mIteratingHistory = true; - return true; - } - - @Override - public boolean getNextOldHistoryLocked(HistoryItem out) { - boolean end = mHistoryBuffer.dataPosition() >= mHistoryBuffer.dataSize(); - if (!end) { - readHistoryDelta(mHistoryBuffer, mHistoryReadTmp); - mReadOverflow |= mHistoryReadTmp.cmd == HistoryItem.CMD_OVERFLOW; - } - HistoryItem cur = mHistoryIterator; - if (cur == null) { - if (!mReadOverflow && !end) { - Slog.w(TAG, "Old history ends before new history!"); - } - return false; - } - out.setTo(cur); - mHistoryIterator = cur.next; - if (!mReadOverflow) { - if (end) { - Slog.w(TAG, "New history ends before old history!"); - } else if (!out.same(mHistoryReadTmp)) { - PrintWriter pw = new FastPrintWriter(new LogWriter(android.util.Log.WARN, TAG)); - pw.println("Histories differ!"); - pw.println("Old history:"); - (new HistoryPrinter()).printNextItem(pw, out, 0, false, true); - pw.println("New history:"); - (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, 0, false, - true); - pw.flush(); - } - } - return true; - } - - @Override - public void finishIteratingOldHistoryLocked() { - mIteratingHistory = false; - mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize()); - mHistoryIterator = null; - } - public int getHistoryTotalSize() { return mConstants.MAX_HISTORY_BUFFER * mConstants.MAX_HISTORY_FILES; } @@ -11283,67 +11036,55 @@ public class BatteryStatsImpl extends BatteryStats { @Override @UnsupportedAppUsage public boolean startIteratingHistoryLocked() { - mBatteryStatsHistory.startIteratingHistory(); mReadOverflow = false; - mIteratingHistory = true; - mReadHistoryStrings = new String[mHistoryTagPool.size()]; - mReadHistoryUids = new int[mHistoryTagPool.size()]; - mReadHistoryChars = 0; - for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) { - final HistoryTag tag = ent.getKey(); - final int idx = ent.getValue(); - mReadHistoryStrings[idx] = tag.string; - mReadHistoryUids[idx] = tag.uid; - mReadHistoryChars += tag.string.length() + 1; - } + mBatteryStatsHistoryIterator = createBatteryStatsHistoryIterator(); return true; } + /** + * Creates an iterator for battery stats history. + */ + @VisibleForTesting + public BatteryStatsHistoryIterator createBatteryStatsHistoryIterator() { + ArrayList<HistoryTag> tags = new ArrayList<>(mHistoryTagPool.size()); + for (Map.Entry<HistoryTag, Integer> entry: mHistoryTagPool.entrySet()) { + final HistoryTag tag = entry.getKey(); + tag.poolIdx = entry.getValue(); + tags.add(tag); + } + + return new BatteryStatsHistoryIterator(mBatteryStatsHistory, tags); + } + @Override public int getHistoryStringPoolSize() { - return mReadHistoryStrings.length; + return mBatteryStatsHistoryIterator.getHistoryStringPoolSize(); } @Override public int getHistoryStringPoolBytes() { - // Each entry is a fixed 12 bytes: 4 for index, 4 for uid, 4 for string size - // Each string character is 2 bytes. - return (mReadHistoryStrings.length * 12) + (mReadHistoryChars * 2); + return mBatteryStatsHistoryIterator.getHistoryStringPoolBytes(); } @Override public String getHistoryTagPoolString(int index) { - return mReadHistoryStrings[index]; + return mBatteryStatsHistoryIterator.getHistoryTagPoolString(index); } @Override public int getHistoryTagPoolUid(int index) { - return mReadHistoryUids[index]; + return mBatteryStatsHistoryIterator.getHistoryTagPoolUid(index); } @Override @UnsupportedAppUsage public boolean getNextHistoryLocked(HistoryItem out) { - Parcel p = mBatteryStatsHistory.getNextParcel(out); - if (p == null) { - return false; - } - final long lastRealtimeMs = out.time; - final long lastWalltimeMs = out.currentTime; - readHistoryDelta(p, out); - if (out.cmd != HistoryItem.CMD_CURRENT_TIME - && out.cmd != HistoryItem.CMD_RESET && lastWalltimeMs != 0) { - out.currentTime = lastWalltimeMs + (out.time - lastRealtimeMs); - } - return true; + return mBatteryStatsHistoryIterator.next(out); } @Override public void finishIteratingHistoryLocked() { - mBatteryStatsHistory.finishIteratingHistory(); - mIteratingHistory = false; - mReadHistoryStrings = null; - mReadHistoryUids = null; + mBatteryStatsHistoryIterator = null; } @Override @@ -11439,12 +11180,12 @@ public class BatteryStatsImpl extends BatteryStats { } if (mPowerProfile != null) { - mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity(); + mEstimatedBatteryCapacityMah = (int) mPowerProfile.getBatteryCapacity(); } else { - mEstimatedBatteryCapacity = -1; + mEstimatedBatteryCapacityMah = -1; } - mMinLearnedBatteryCapacity = -1; - mMaxLearnedBatteryCapacity = -1; + mMinLearnedBatteryCapacityUah = -1; + mMaxLearnedBatteryCapacityUah = -1; mInteractiveTimer.reset(false, elapsedRealtimeUs); mPowerSaveModeEnabledTimer.reset(false, elapsedRealtimeUs); mLastIdleTimeStartMs = elapsedRealtimeMillis; @@ -11563,7 +11304,9 @@ public class BatteryStatsImpl extends BatteryStats { initDischarge(elapsedRealtimeUs); clearHistoryLocked(); - mBatteryStatsHistory.resetAllFiles(); + if (mBatteryStatsHistory != null) { + mBatteryStatsHistory.resetAllFiles(); + } // Flush external data, gathering snapshots, but don't process it since it is pre-reset data mIgnoreNextExternalStats = true; @@ -12462,7 +12205,7 @@ public class BatteryStatsImpl extends BatteryStats { return; } - mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ, true); + mGlobalMeasuredEnergyStats.updateStandardBucket(energyBucket, energyUJ); // Now we blame individual apps, but only if the display was ON. if (energyBucket != MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON) { @@ -12500,7 +12243,7 @@ public class BatteryStatsImpl extends BatteryStats { final long appDisplayEnergyMJ = (totalDisplayEnergyMJ * fgTimeMs + (totalFgTimeMs / 2)) / totalFgTimeMs; - uid.addEnergyToStandardBucketLocked(appDisplayEnergyMJ * 1000, energyBucket, true); + uid.addEnergyToStandardBucketLocked(appDisplayEnergyMJ * 1000, energyBucket); // To mitigate round-off errors, remove this app from numerator & denominator totals totalDisplayEnergyMJ -= appDisplayEnergyMJ; @@ -12514,6 +12257,7 @@ public class BatteryStatsImpl extends BatteryStats { * @param totalEnergyUJ energy (microjoules) used for this bucket since this was last called. * @param uidEnergies map of uid->energy (microjoules) for this bucket since last called. * Data inside uidEnergies will not be modified (treated immutable). + * Uids not already known to BatteryStats will be ignored. */ public void updateCustomMeasuredEnergyDataLocked(int customEnergyBucket, long totalEnergyUJ, @Nullable SparseLongArray uidEnergies) { @@ -12526,7 +12270,7 @@ public class BatteryStatsImpl extends BatteryStats { if (mGlobalMeasuredEnergyStats == null) return; if (!mOnBatteryInternal || mIgnoreNextExternalStats || totalEnergyUJ <= 0) return; - mGlobalMeasuredEnergyStats.updateCustomBucket(customEnergyBucket, totalEnergyUJ, true); + mGlobalMeasuredEnergyStats.updateCustomBucket(customEnergyBucket, totalEnergyUJ); if (uidEnergies == null) return; final int numUids = uidEnergies.size(); @@ -12534,10 +12278,20 @@ public class BatteryStatsImpl extends BatteryStats { final int uidInt = mapUid(uidEnergies.keyAt(i)); final long uidEnergyUJ = uidEnergies.valueAt(i); if (uidEnergyUJ == 0) continue; - // TODO(b/180030409): Worry about dead Uids (no longer in BSI) being revived by this, - // or converse problem of not creating a new Uid if its first blame is recorded here. - final Uid uidObj = getUidStatsLocked(uidInt); - uidObj.addEnergyToCustomBucketLocked(uidEnergyUJ, customEnergyBucket, true); + final Uid uidObj = getAvailableUidStatsLocked(uidInt); + if (uidObj != null) { + uidObj.addEnergyToCustomBucketLocked(uidEnergyUJ, customEnergyBucket); + } else { + // Ignore any uid not already known to BatteryStats, rather than creating a new Uid. + // Otherwise we could end up reviving dead Uids. Note that the CPU data is updated + // first, so any uid that has used any CPU should already be known to BatteryStats. + // Recently removed uids (especially common for isolated uids) can reach this path + // and are ignored. + if (!Process.isIsolated(uidInt)) { + Slog.w(TAG, "Received measured energy " + totalEnergyUJ + " for custom bucket " + + customEnergyBucket + " for non-existent uid " + uidInt); + } + } } } @@ -13212,7 +12966,7 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, - final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) { + final boolean onBattery, final int oldStatus, final int level, final int chargeUah) { boolean doWrite = false; Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE); m.arg1 = onBattery ? 1 : 0; @@ -13268,10 +13022,10 @@ public class BatteryStatsImpl extends BatteryStats { } doWrite = true; resetAllStatsLocked(mSecUptime, mSecRealtime); - if (chargeUAh > 0 && level > 0) { - mBatteryCharge = chargeUAh; + if (chargeUah > 0 && level > 0) { + mBatteryChargeUah = chargeUah; // Only use the reported coulomb charge value if it is supported and reported. - mEstimatedBatteryCapacity = (int) ((chargeUAh / 1000) / (level / 100.0)); + mEstimatedBatteryCapacityMah = (int) ((chargeUah / 1000) / (level / 100.0)); } mDischargeStartLevel = level; reset = true; @@ -13386,16 +13140,16 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") public void setBatteryStateLocked(final int status, final int health, final int plugType, - final int level, /* not final */ int temp, final int volt, final int chargeUAh, - final int chargeFullUAh, final long chargeTimeToFullSeconds) { - setBatteryStateLocked(status, health, plugType, level, temp, volt, chargeUAh, - chargeFullUAh, chargeTimeToFullSeconds, + final int level, /* not final */ int temp, final int voltageMv, final int chargeUah, + final int chargeFullUah, final long chargeTimeToFullSeconds) { + setBatteryStateLocked(status, health, plugType, level, temp, voltageMv, chargeUah, + chargeFullUah, chargeTimeToFullSeconds, mClocks.elapsedRealtime(), mClocks.uptimeMillis(), System.currentTimeMillis()); } public void setBatteryStateLocked(final int status, final int health, final int plugType, - final int level, /* not final */ int temp, final int volt, final int chargeUAh, - final int chargeFullUAh, final long chargeTimeToFullSeconds, + final int level, /* not final */ int temp, final int voltageMv, final int chargeUah, + final int chargeFullUah, final long chargeTimeToFullSeconds, final long elapsedRealtimeMs, final long uptimeMs, final long currentTimeMs) { // Temperature is encoded without the signed bit, so clamp any negative temperatures to 0. temp = Math.max(0, temp); @@ -13421,7 +13175,7 @@ public class BatteryStatsImpl extends BatteryStats { mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG; mHistoryCur.batteryStatus = (byte)status; mHistoryCur.batteryLevel = (byte)level; - mHistoryCur.batteryChargeUAh = chargeUAh; + mHistoryCur.batteryChargeUah = chargeUah; mMaxChargeStepLevel = mMinDischargeStepLevel = mLastChargeStepLevel = mLastDischargeStepLevel = level; mLastChargingStateLevel = level; @@ -13444,10 +13198,10 @@ public class BatteryStatsImpl extends BatteryStats { } if (ENABLE_FOREGROUND_STATS_COLLECTION) { - mBatteryVolt = volt; + mBatteryVoltageMv = voltageMv; if (onBattery) { - final long energyNwh = (volt * (long) chargeUAh); - final long energyDelta = mLastBatteryEnergyCapacityNWh - energyNwh; + final long energyNwh = (voltageMv * (long) chargeUah); + final long energyDelta = mLastBatteryEnergyCapacityNwh - energyNwh; for (int i = 0; i < mForegroundUids.size(); i++) { final int uid = mForegroundUids.get(i); if (uid == INVALID_UID) { @@ -13458,7 +13212,7 @@ public class BatteryStatsImpl extends BatteryStats { if (pfu.baseTimeMs <= 0) { pfu.baseTimeMs = currentTimeMs; } else { - // Check if mLastBatteryEnergyCapacityNWh > energyNwh, + // Check if mLastBatteryEnergyCapacityNwh > energyNwh, // to make sure we only count discharges if (energyDelta > 0) { pfu.energyNwh += energyDelta; @@ -13476,7 +13230,7 @@ public class BatteryStatsImpl extends BatteryStats { pfu.baseTimeMs = currentTimeMs; } } - mLastBatteryEnergyCapacityNWh = energyNwh; + mLastBatteryEnergyCapacityNwh = energyNwh; } else if (onBattery != mOnBattery) { // Transition to onBattery = false mUidToPower.values().forEach(v -> v.baseTimeMs = 0); @@ -13494,10 +13248,10 @@ public class BatteryStatsImpl extends BatteryStats { mHistoryCur.batteryHealth = (byte)health; mHistoryCur.batteryPlugType = (byte)plugType; mHistoryCur.batteryTemperature = (short)temp; - mHistoryCur.batteryVoltage = (char)volt; - if (chargeUAh < mHistoryCur.batteryChargeUAh) { + mHistoryCur.batteryVoltage = (char) voltageMv; + if (chargeUah < mHistoryCur.batteryChargeUah) { // Only record discharges - final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh; + final long chargeDiff = mHistoryCur.batteryChargeUah - chargeUah; mDischargeCounter.addCountLocked(chargeDiff); mDischargeScreenOffCounter.addCountLocked(chargeDiff); if (Display.isDozeState(mScreenState)) { @@ -13509,8 +13263,8 @@ public class BatteryStatsImpl extends BatteryStats { mDischargeDeepDozeCounter.addCountLocked(chargeDiff); } } - mHistoryCur.batteryChargeUAh = chargeUAh; - setOnBatteryLocked(elapsedRealtimeMs, uptimeMs, onBattery, oldStatus, level, chargeUAh); + mHistoryCur.batteryChargeUah = chargeUah; + setOnBatteryLocked(elapsedRealtimeMs, uptimeMs, onBattery, oldStatus, level, chargeUah); } else { boolean changed = false; if (mHistoryCur.batteryLevel != level) { @@ -13539,16 +13293,16 @@ public class BatteryStatsImpl extends BatteryStats { mHistoryCur.batteryTemperature = (short)temp; changed = true; } - if (volt > (mHistoryCur.batteryVoltage+20) - || volt < (mHistoryCur.batteryVoltage-20)) { - mHistoryCur.batteryVoltage = (char)volt; + if (voltageMv > (mHistoryCur.batteryVoltage + 20) + || voltageMv < (mHistoryCur.batteryVoltage - 20)) { + mHistoryCur.batteryVoltage = (char) voltageMv; changed = true; } - if (chargeUAh >= (mHistoryCur.batteryChargeUAh+10) - || chargeUAh <= (mHistoryCur.batteryChargeUAh-10)) { - if (chargeUAh < mHistoryCur.batteryChargeUAh) { + if (chargeUah >= (mHistoryCur.batteryChargeUah + 10) + || chargeUah <= (mHistoryCur.batteryChargeUah - 10)) { + if (chargeUah < mHistoryCur.batteryChargeUah) { // Only record discharges - final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh; + final long chargeDiff = mHistoryCur.batteryChargeUah - chargeUah; mDischargeCounter.addCountLocked(chargeDiff); mDischargeScreenOffCounter.addCountLocked(chargeDiff); if (Display.isDozeState(mScreenState)) { @@ -13560,7 +13314,7 @@ public class BatteryStatsImpl extends BatteryStats { mDischargeDeepDozeCounter.addCountLocked(chargeDiff); } } - mHistoryCur.batteryChargeUAh = chargeUAh; + mHistoryCur.batteryChargeUah = chargeUah; changed = true; } long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT) @@ -13632,12 +13386,12 @@ public class BatteryStatsImpl extends BatteryStats { mRecordingHistory = DEBUG; } - if (mMinLearnedBatteryCapacity == -1) { - mMinLearnedBatteryCapacity = chargeFullUAh; + if (mMinLearnedBatteryCapacityUah == -1) { + mMinLearnedBatteryCapacityUah = chargeFullUah; } else { - mMinLearnedBatteryCapacity = Math.min(mMinLearnedBatteryCapacity, chargeFullUAh); + mMinLearnedBatteryCapacityUah = Math.min(mMinLearnedBatteryCapacityUah, chargeFullUah); } - mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh); + mMaxLearnedBatteryCapacityUah = Math.max(mMaxLearnedBatteryCapacityUah, chargeFullUah); mBatteryTimeToFullSeconds = chargeTimeToFullSeconds; } @@ -14620,7 +14374,7 @@ public class BatteryStatsImpl extends BatteryStats { Parcel p = Parcel.obtain(); final long start = SystemClock.uptimeMillis(); - writeHistoryBuffer(p, true, true); + writeHistoryBuffer(p, true); if (DEBUG) { Slog.d(TAG, "writeHistoryBuffer duration ms:" + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize()); @@ -14714,7 +14468,7 @@ public class BatteryStatsImpl extends BatteryStats { if (raw.length > 0) { history.unmarshall(raw, 0, raw.length); history.setDataPosition(0); - readHistoryBuffer(history, true); + readHistoryBuffer(history); } if (DEBUG) { Slog.d(TAG, "readLocked history file::" @@ -14738,10 +14492,6 @@ public class BatteryStatsImpl extends BatteryStats { mRecordingHistory = true; final long elapsedRealtimeMs = mClocks.elapsedRealtime(); final long uptimeMs = mClocks.uptimeMillis(); - if (USE_OLD_HISTORY) { - addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs, - HistoryItem.CMD_START, mHistoryCur); - } addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_START, mHistoryCur); startRecordingHistory(elapsedRealtimeMs, uptimeMs, false); } @@ -14753,7 +14503,7 @@ public class BatteryStatsImpl extends BatteryStats { return 0; } - void readHistoryBuffer(Parcel in, boolean andOldHistory) throws ParcelFormatException { + void readHistoryBuffer(Parcel in) throws ParcelFormatException { final int version = in.readInt(); if (version != VERSION) { Slog.w("BatteryStats", "readHistoryBuffer: version got " + version @@ -14781,10 +14531,6 @@ public class BatteryStatsImpl extends BatteryStats { in.setDataPosition(curPos + bufSize); } - if (andOldHistory) { - readOldHistory(in); - } - if (DEBUG_HISTORY) { StringBuilder sb = new StringBuilder(128); sb.append("****************** OLD mHistoryBaseTimeMs: "); @@ -14813,18 +14559,7 @@ public class BatteryStatsImpl extends BatteryStats { } } - void readOldHistory(Parcel in) { - if (!USE_OLD_HISTORY) { - return; - } - mHistory = mHistoryEnd = mHistoryCache = null; - while (in.dataAvail() > 0) { - HistoryItem rec = new HistoryItem(in); - addHistoryRecordLocked(rec); - } - } - - void writeHistoryBuffer(Parcel out, boolean inclData, boolean andOldHistory) { + void writeHistoryBuffer(Parcel out, boolean inclData) { if (DEBUG_HISTORY) { StringBuilder sb = new StringBuilder(128); sb.append("****************** WRITING mHistoryBaseTimeMs: "); @@ -14845,22 +14580,6 @@ public class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: " + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition()); out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize()); - - if (andOldHistory) { - writeOldHistory(out); - } - } - - void writeOldHistory(Parcel out) { - if (!USE_OLD_HISTORY) { - return; - } - HistoryItem rec = mHistory; - while (rec != null) { - if (rec.time >= 0) rec.writeToParcel(out, 0); - rec = rec.next; - } - out.writeLong(-1); } public void readSummaryFromParcel(Parcel in) throws ParcelFormatException { @@ -14873,7 +14592,7 @@ public class BatteryStatsImpl extends BatteryStats { boolean inclHistory = in.readBoolean(); if (inclHistory) { - readHistoryBuffer(in, true); + readHistoryBuffer(in); mBatteryStatsHistory.readFromParcel(in); } @@ -14912,9 +14631,9 @@ public class BatteryStatsImpl extends BatteryStats { mDischargePlugLevel = in.readInt(); mDischargeCurrentLevel = in.readInt(); mCurrentBatteryLevel = in.readInt(); - mEstimatedBatteryCapacity = in.readInt(); - mMinLearnedBatteryCapacity = in.readInt(); - mMaxLearnedBatteryCapacity = in.readInt(); + mEstimatedBatteryCapacityMah = in.readInt(); + mMinLearnedBatteryCapacityUah = in.readInt(); + mMaxLearnedBatteryCapacityUah = in.readInt(); mLowDischargeAmountSinceCharge = in.readInt(); mHighDischargeAmountSinceCharge = in.readInt(); mDischargeAmountScreenOnSinceCharge = in.readInt(); @@ -15392,7 +15111,7 @@ public class BatteryStatsImpl extends BatteryStats { out.writeBoolean(inclHistory); if (inclHistory) { - writeHistoryBuffer(out, true, true); + writeHistoryBuffer(out, true); mBatteryStatsHistory.writeToParcel(out); } @@ -15416,9 +15135,9 @@ public class BatteryStatsImpl extends BatteryStats { out.writeInt(mDischargePlugLevel); out.writeInt(mDischargeCurrentLevel); out.writeInt(mCurrentBatteryLevel); - out.writeInt(mEstimatedBatteryCapacity); - out.writeInt(mMinLearnedBatteryCapacity); - out.writeInt(mMaxLearnedBatteryCapacity); + out.writeInt(mEstimatedBatteryCapacityMah); + out.writeInt(mMinLearnedBatteryCapacityUah); + out.writeInt(mMaxLearnedBatteryCapacityUah); out.writeInt(getLowDischargeAmountSinceCharge()); out.writeInt(getHighDischargeAmountSinceCharge()); out.writeInt(getDischargeAmountScreenOnSinceCharge()); @@ -15905,7 +15624,7 @@ public class BatteryStatsImpl extends BatteryStats { throw new ParcelFormatException("Bad magic number: #" + Integer.toHexString(magic)); } - readHistoryBuffer(in, false); + readHistoryBuffer(in); mBatteryStatsHistory.readFromParcel(in); mStartCount = in.readInt(); @@ -15917,9 +15636,9 @@ public class BatteryStatsImpl extends BatteryStats { mRealtimeUs = in.readLong(); mRealtimeStartUs = in.readLong(); mOnBattery = in.readInt() != 0; - mEstimatedBatteryCapacity = in.readInt(); - mMinLearnedBatteryCapacity = in.readInt(); - mMaxLearnedBatteryCapacity = in.readInt(); + mEstimatedBatteryCapacityMah = in.readInt(); + mMinLearnedBatteryCapacityUah = in.readInt(); + mMaxLearnedBatteryCapacityUah = in.readInt(); mOnBatteryInternal = false; // we are no longer really running. mOnBatteryTimeBase.readFromParcel(in); mOnBatteryScreenOffTimeBase.readFromParcel(in); @@ -16144,7 +15863,7 @@ public class BatteryStatsImpl extends BatteryStats { out.writeInt(MAGIC); - writeHistoryBuffer(out, true, false); + writeHistoryBuffer(out, true); mBatteryStatsHistory.writeToParcel(out); out.writeInt(mStartCount); @@ -16156,9 +15875,9 @@ public class BatteryStatsImpl extends BatteryStats { out.writeLong(mRealtimeUs); out.writeLong(mRealtimeStartUs); out.writeInt(mOnBattery ? 1 : 0); - out.writeInt(mEstimatedBatteryCapacity); - out.writeInt(mMinLearnedBatteryCapacity); - out.writeInt(mMaxLearnedBatteryCapacity); + out.writeInt(mEstimatedBatteryCapacityMah); + out.writeInt(mMinLearnedBatteryCapacityUah); + out.writeInt(mMaxLearnedBatteryCapacityUah); mOnBatteryTimeBase.writeToParcel(out, uSecUptime, uSecRealtime); mOnBatteryScreenOffTimeBase.writeToParcel(out, uSecUptime, uSecRealtime); @@ -16414,8 +16133,8 @@ public class BatteryStatsImpl extends BatteryStats { public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) { if (ENABLE_FOREGROUND_STATS_COLLECTION) { - long actualCharge = -1; - long actualEnergy = -1; + long actualChargeUah = -1; + long actualEnergyNwh = -1; try { IBatteryPropertiesRegistrar registrar = IBatteryPropertiesRegistrar.Stub.asInterface( @@ -16424,27 +16143,27 @@ public class BatteryStatsImpl extends BatteryStats { BatteryProperty prop = new BatteryProperty(); if (registrar.getProperty( BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER, prop) == 0) { - actualCharge = prop.getLong(); + actualChargeUah = prop.getLong(); } prop = new BatteryProperty(); if (registrar.getProperty( BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER, prop) == 0) { - actualEnergy = prop.getLong(); + actualEnergyNwh = prop.getLong(); } } } catch (RemoteException e) { // Ignore. } - pw.printf("ActualCharge (uAh): %d\n", (int) actualCharge); - pw.printf("ActualEnergy (nWh): %d\n", actualEnergy); - pw.printf("mBatteryCharge (uAh): %d\n", mBatteryCharge); - pw.printf("mBatteryVolts (mV): %d\n", mBatteryVolt); - pw.printf("est energy (nWh): %d\n", mBatteryVolt * (long) mBatteryCharge); - pw.printf("mEstimatedBatteryCapacity (mAh): %d\n", mEstimatedBatteryCapacity); - pw.printf("mMinLearnedBatteryCapacity (uAh): %d\n", mMinLearnedBatteryCapacity); - pw.printf("mMaxLearnedBatteryCapacity (uAh): %d\n", mMaxLearnedBatteryCapacity); + pw.printf("ActualCharge (uAh): %d\n", (int) actualChargeUah); + pw.printf("ActualEnergy (nWh): %d\n", actualEnergyNwh); + pw.printf("mBatteryCharge (uAh): %d\n", mBatteryChargeUah); + pw.printf("mBatteryVolts (mV): %d\n", mBatteryVoltageMv); + pw.printf("est energy (nWh): %d\n", mBatteryVoltageMv * (long) mBatteryChargeUah); + pw.printf("mEstimatedBatteryCapacity (mAh): %d\n", mEstimatedBatteryCapacityMah); + pw.printf("mMinLearnedBatteryCapacity (uAh): %d\n", mMinLearnedBatteryCapacityUah); + pw.printf("mMaxLearnedBatteryCapacity (uAh): %d\n", mMaxLearnedBatteryCapacityUah); pw.printf("est. capacity: %f\n", - (float) actualCharge / (mEstimatedBatteryCapacity * 1000)); + (float) actualChargeUah / (mEstimatedBatteryCapacityMah * 1000)); pw.printf("mCurrentBatteryLevel: %d\n", mCurrentBatteryLevel); pw.println("Total Power per app:"); mUidToPower.entrySet().forEach(e -> diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index 233ba1912dcd..15b584d1fd06 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -22,12 +22,15 @@ import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.Bundle; -import android.os.SystemClock; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; + import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Uses accumulated battery stats data and PowerCalculators to produce power @@ -52,6 +55,7 @@ public class BatteryUsageStatsProvider { mPowerCalculators = new ArrayList<>(); // Power calculators are applied in the order of registration + mPowerCalculators.add(new DischargedPowerCalculator(mPowerProfile)); mPowerCalculators.add(new CpuPowerCalculator(mPowerProfile)); mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile)); mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile)); @@ -84,7 +88,7 @@ public class BatteryUsageStatsProvider { } /** - * Returns a snapshot of battery attribution data. + * Returns snapshots of battery attribution data, one per supplied query. */ public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) { @@ -106,30 +110,42 @@ public class BatteryUsageStatsProvider { ArrayList<BatteryUsageStats> results = new ArrayList<>(queries.size()); for (int i = 0; i < queries.size(); i++) { - results.add(getBatteryUsageStats(queries.get(i), batteryStatsHelper)); + results.add(getBatteryUsageStats(queries.get(i))); } return results; } - private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query, - BatteryStatsHelper batteryStatsHelper) { - // TODO(b/174186358): read extra power component number from configuration - final int customPowerComponentCount = 0; + /** + * Returns a snapshot of battery attribution data. + */ + @VisibleForTesting + public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) { + final long realtimeUs = mStats.mClocks.elapsedRealtime() * 1000; + final long uptimeUs = mStats.mClocks.uptimeMillis() * 1000; + + final long[] customMeasuredEnergiesMicroJoules = + mStats.getCustomMeasuredEnergiesMicroJoules(); + final int customPowerComponentCount = customMeasuredEnergiesMicroJoules != null + ? customMeasuredEnergiesMicroJoules.length + : 0; + + // TODO(b/174186358): read extra time component number from configuration final int customTimeComponentCount = 0; final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder(customPowerComponentCount, customTimeComponentCount) - .setDischargePercentage(batteryStatsHelper.getStats().getDischargeAmount(0)) - .setConsumedPower(batteryStatsHelper.getTotalPower()); + .setStatsStartRealtime(mStats.getStatsStartRealtime() / 1000); SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats(); for (int i = uidStats.size() - 1; i >= 0; i--) { - batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i)); + final BatteryStats.Uid uid = uidStats.valueAt(i); + batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid) + .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, + getProcessBackgroundTimeMs(uid, realtimeUs)) + .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, + getProcessForegroundTimeMs(uid, realtimeUs)); } - final long realtimeUs = SystemClock.elapsedRealtime() * 1000; - final long uptimeUs = SystemClock.uptimeMillis() * 1000; - final List<PowerCalculator> powerCalculators = getPowerCalculators(); for (int i = 0, count = powerCalculators.size(); i < count; i++) { PowerCalculator powerCalculator = powerCalculators.get(i); @@ -137,6 +153,51 @@ public class BatteryUsageStatsProvider { query); } + if ((query.getFlags() + & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) { + ArrayList<BatteryStats.HistoryTag> tags = new ArrayList<>( + mStats.mHistoryTagPool.size()); + for (Map.Entry<BatteryStats.HistoryTag, Integer> entry : + mStats.mHistoryTagPool.entrySet()) { + final BatteryStats.HistoryTag tag = entry.getKey(); + tag.poolIdx = entry.getValue(); + tags.add(tag); + } + + batteryUsageStatsBuilder.setBatteryHistory(mStats.mHistoryBuffer, tags); + } + return batteryUsageStatsBuilder.build(); } + + private long getProcessForegroundTimeMs(BatteryStats.Uid uid, long realtimeUs) { + final long topStateDurationMs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP, + realtimeUs, BatteryStats.STATS_SINCE_CHARGED) / 1000; + + long foregroundActivityDurationMs = 0; + final BatteryStats.Timer foregroundActivityTimer = uid.getForegroundActivityTimer(); + if (foregroundActivityTimer != null) { + foregroundActivityDurationMs = foregroundActivityTimer.getTotalTimeLocked(realtimeUs, + BatteryStats.STATS_SINCE_CHARGED) / 1000; + } + + // Use the min value of STATE_TOP time and foreground activity time, since both of these + // times are imprecise + final long foregroundDurationMs = Math.min(topStateDurationMs, + foregroundActivityDurationMs); + + long foregroundServiceDurationMs = 0; + final BatteryStats.Timer foregroundServiceTimer = uid.getForegroundServiceTimer(); + if (foregroundServiceTimer != null) { + foregroundServiceDurationMs = foregroundServiceTimer.getTotalTimeLocked(realtimeUs, + BatteryStats.STATS_SINCE_CHARGED) / 1000; + } + + return foregroundDurationMs + foregroundServiceDurationMs; + } + + private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, long realtimeUs) { + return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, realtimeUs, + BatteryStats.STATS_SINCE_CHARGED) / 1000; + } } diff --git a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java index 4babe8d5fe96..2606d80eaa09 100644 --- a/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java +++ b/core/java/com/android/internal/os/CustomMeasuredPowerCalculator.java @@ -15,7 +15,12 @@ */ package com.android.internal.os; +import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; /** * Calculates the amount of power consumed by custom energy consumers (i.e. consumers of type @@ -26,6 +31,38 @@ public class CustomMeasuredPowerCalculator extends PowerCalculator { } @Override + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + super.calculate(builder, batteryStats, rawRealtimeUs, rawUptimeUs, query); + final double[] customMeasuredPowerMah = calculateMeasuredEnergiesMah( + batteryStats.getCustomMeasuredEnergiesMicroJoules()); + if (customMeasuredPowerMah != null) { + final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder = + builder.getOrCreateSystemBatteryConsumerBuilder( + SystemBatteryConsumer.DRAIN_TYPE_CUSTOM); + for (int i = 0; i < customMeasuredPowerMah.length; i++) { + systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i, + customMeasuredPowerMah[i]); + } + } + } + + @Override + protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + final double[] customMeasuredPowerMah = calculateMeasuredEnergiesMah( + u.getCustomMeasuredEnergiesMicroJoules()); + if (customMeasuredPowerMah != null) { + for (int i = 0; i < customMeasuredPowerMah.length; i++) { + app.setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i, + customMeasuredPowerMah[i]); + } + } + } + + @Override protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { updateCustomMeasuredPowerMah(app, u.getCustomMeasuredEnergiesMicroJoules()); diff --git a/core/java/com/android/internal/os/DischargedPowerCalculator.java b/core/java/com/android/internal/os/DischargedPowerCalculator.java new file mode 100644 index 000000000000..e94020cc4f18 --- /dev/null +++ b/core/java/com/android/internal/os/DischargedPowerCalculator.java @@ -0,0 +1,52 @@ +/* + * 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.internal.os; + +import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.UserHandle; +import android.util.SparseArray; + +import java.util.List; + +/** + * Estimates the battery discharge amounts. + */ +public class DischargedPowerCalculator extends PowerCalculator { + private final double mBatteryCapacity; + + public DischargedPowerCalculator(PowerProfile powerProfile) { + mBatteryCapacity = powerProfile.getBatteryCapacity(); + } + + @Override + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + builder.setDischargePercentage( + batteryStats.getDischargeAmount(BatteryStats.STATS_SINCE_CHARGED)) + .setDischargedPowerRange( + batteryStats.getLowDischargeAmountSinceCharge() * mBatteryCapacity / 100, + batteryStats.getHighDischargeAmountSinceCharge() * mBatteryCapacity / 100); + } + + @Override + public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { + // Not implemented. The computation is done by BatteryStatsHelper + } +} diff --git a/core/java/com/android/internal/os/KernelCpuBpfTracking.java b/core/java/com/android/internal/os/KernelCpuBpfTracking.java index 28525478be05..387d3270f5e4 100644 --- a/core/java/com/android/internal/os/KernelCpuBpfTracking.java +++ b/core/java/com/android/internal/os/KernelCpuBpfTracking.java @@ -16,11 +16,79 @@ package com.android.internal.os; -/** CPU tracking using eBPF. */ +import android.annotation.Nullable; + +/** + * CPU tracking using eBPF. + * + * The tracking state and data about available frequencies are cached to avoid JNI calls and + * creating temporary arrays. The data is stored in a format that is convenient for metrics + * computation. + * + * Synchronization is not needed because the underlying native library can be invoked concurrently + * and getters are idempotent. + */ public final class KernelCpuBpfTracking { + private static boolean sTracking = false; + + /** Cached mapping from frequency index to frequency in kHz. */ + private static long[] sFreqs = null; + + /** Cached mapping from frequency index to CPU cluster / policy. */ + private static int[] sFreqsClusters = null; + private KernelCpuBpfTracking() { } /** Returns whether CPU tracking using eBPF is supported. */ public static native boolean isSupported(); + + /** Starts CPU tracking using eBPF. */ + public static boolean startTracking() { + if (!sTracking) { + sTracking = startTrackingInternal(); + } + return sTracking; + } + + private static native boolean startTrackingInternal(); + + /** Returns frequencies in kHz on which CPU is tracked. Empty if not supported. */ + public static long[] getFreqs() { + if (sFreqs == null) { + long[] freqs = getFreqsInternal(); + if (freqs == null) { + return new long[0]; + } + sFreqs = freqs; + } + return sFreqs; + } + + @Nullable + static native long[] getFreqsInternal(); + + /** + * Returns the cluster (policy) number for each frequency on which CPU is tracked. Empty if + * not supported. + */ + public static int[] getFreqsClusters() { + if (sFreqsClusters == null) { + int[] freqsClusters = getFreqsClustersInternal(); + if (freqsClusters == null) { + return new int[0]; + } + sFreqsClusters = freqsClusters; + } + return sFreqsClusters; + } + + @Nullable + private static native int[] getFreqsClustersInternal(); + + /** Returns the number of clusters (policies). */ + public static int getClusters() { + int[] freqClusters = getFreqsClusters(); + return freqClusters.length > 0 ? freqClusters[freqClusters.length - 1] + 1 : 0; + } } diff --git a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java index 06760e140de1..553eda48e61d 100644 --- a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java +++ b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java @@ -16,22 +16,22 @@ package com.android.internal.os; -/** - * Reads total CPU time bpf map. - */ +import android.annotation.Nullable; + +/** Reads total CPU time bpf map. */ public final class KernelCpuTotalBpfMapReader { private KernelCpuTotalBpfMapReader() { } - /** Reads total CPU time from bpf map. */ - public static native boolean read(Callback callback); - - /** Callback accepting values read from bpf map. */ - public interface Callback { - /** - * Accepts values read from bpf map: cluster index, frequency in kilohertz and time in - * milliseconds that the cpu cluster spent at the frequency (excluding sleep). - */ - void accept(int cluster, int freqKhz, long timeMs); + /** Reads total CPU times (excluding sleep) per frequency in milliseconds from bpf map. */ + @Nullable + public static long[] read() { + if (!KernelCpuBpfTracking.startTracking()) { + return null; + } + return readInternal(); } + + @Nullable + private static native long[] readInternal(); } diff --git a/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java index dafb924f37bd..52c0c3fa93df 100644 --- a/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java +++ b/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java @@ -68,14 +68,15 @@ public abstract class KernelCpuUidBpfMapReader { final String mTag = this.getClass().getSimpleName(); private int mErrors = 0; - private boolean mTracking = false; protected SparseArray<long[]> mData = new SparseArray<>(); private long mLastReadTime = 0; protected final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock(); protected final ReentrantReadWriteLock.ReadLock mReadLock = mLock.readLock(); protected final ReentrantReadWriteLock.WriteLock mWriteLock = mLock.writeLock(); - public native boolean startTrackingBpfTimes(); + public boolean startTrackingBpfTimes() { + return KernelCpuBpfTracking.startTracking(); + } protected abstract boolean readBpfData(); @@ -116,7 +117,7 @@ public abstract class KernelCpuUidBpfMapReader { if (mErrors > ERROR_THRESHOLD) { return null; } - if (!mTracking && !startTrackingBpfTimes()) { + if (!startTrackingBpfTimes()) { Slog.w(mTag, "Failed to start tracking"); mErrors++; return null; @@ -182,7 +183,9 @@ public abstract class KernelCpuUidBpfMapReader { protected final native boolean readBpfData(); @Override - public final native long[] getDataDimensions(); + public final long[] getDataDimensions() { + return KernelCpuBpfTracking.getFreqsInternal(); + } @Override public void removeUidsInRange(int startUid, int endUid) { diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java index 4299f0936dae..64c68ce0c83b 100644 --- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java +++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java @@ -152,6 +152,10 @@ public abstract class KernelCpuUidTimeReader<T> { */ public void removeUid(int uid) { mLastTimes.delete(uid); + + if (mBpfTimesAvailable) { + mBpfReader.removeUidsInRange(uid, uid); + } } /** diff --git a/core/java/com/android/internal/os/PowerCalculator.java b/core/java/com/android/internal/os/PowerCalculator.java index 7c45cc09e18c..fe4fb7afa1f5 100644 --- a/core/java/com/android/internal/os/PowerCalculator.java +++ b/core/java/com/android/internal/os/PowerCalculator.java @@ -113,6 +113,19 @@ public abstract class PowerCalculator { } /** + * Returns either the measured energy converted to mAh or a usage-based estimate. + */ + protected static double getMeasuredOrEstimatedPower(long measuredEnergyUj, + UsageBasedPowerEstimator powerEstimator, long durationMs, + boolean forceUsePowerProfileModel) { + if (measuredEnergyUj != BatteryStats.ENERGY_DATA_UNAVAILABLE + && !forceUsePowerProfileModel) { + return uJtoMah(measuredEnergyUj); + } + return powerEstimator.calculatePower(durationMs); + } + + /** * Converts charge in mAh to string. */ public static String formatCharge(double power) { diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java index c1dd7ce662f7..5dca8d5e70fa 100644 --- a/core/java/com/android/internal/os/ScreenPowerCalculator.java +++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java @@ -61,12 +61,10 @@ public class ScreenPowerCalculator extends PowerCalculator { public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { final PowerAndDuration totalPowerAndDuration = new PowerAndDuration(); - final boolean forceUsePowerProfileModel = (query.getFlags() - & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL) != 0; final boolean useEnergyData = calculateTotalDurationAndPower(totalPowerAndDuration, batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, - forceUsePowerProfileModel); + query.shouldForceUsePowerProfileModel()); builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN) .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING index 9698f190a419..791e9ad5ef9d 100644 --- a/core/java/com/android/internal/os/TEST_MAPPING +++ b/core/java/com/android/internal/os/TEST_MAPPING @@ -1,6 +1,23 @@ { "presubmit": [ { + "file_patterns": ["Battery[^/]*\\.java"], + "name": "FrameworksCoreTests", + "options": [ + { "include-filter": "com.android.internal.os.BatteryStatsTests" }, + { "exclude-annotation": "com.android.internal.os.SkipPresubmit" } + ] + }, + { + "file_patterns": ["Battery[^/]*\\.java"], + "name": "FrameworksServicesTests", + "options": [ + { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }, + { "include-filter": "com.android.server.am.MeasuredEnergySnapshotTest" }, + { "include-filter": "com.android.server.am.BatteryExternalStatsWorkerTest" } + ] + }, + { "name": "FrameworksCoreTests", "options": [ { diff --git a/core/java/com/android/internal/os/UsageBasedPowerEstimator.java b/core/java/com/android/internal/os/UsageBasedPowerEstimator.java index 5910b61f0f6f..fd5201431eab 100644 --- a/core/java/com/android/internal/os/UsageBasedPowerEstimator.java +++ b/core/java/com/android/internal/os/UsageBasedPowerEstimator.java @@ -32,6 +32,10 @@ public class UsageBasedPowerEstimator { mAveragePowerMahPerMs = averagePowerMilliAmp / MILLIS_IN_HOUR; } + public boolean isSupported() { + return mAveragePowerMahPerMs != 0; + } + /** * Given a {@link BatteryStats.Timer}, returns the accumulated duration. */ diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java index 63763f76c6ed..98f613fc1c40 100644 --- a/core/java/com/android/internal/os/WifiPowerCalculator.java +++ b/core/java/com/android/internal/os/WifiPowerCalculator.java @@ -15,8 +15,13 @@ */ package com.android.internal.os; +import android.os.BatteryConsumer; import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; import android.os.Process; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.util.Log; import android.util.SparseArray; @@ -30,25 +35,93 @@ import java.util.List; public class WifiPowerCalculator extends PowerCalculator { private static final boolean DEBUG = BatteryStatsHelper.DEBUG; private static final String TAG = "WifiPowerCalculator"; - private final double mIdleCurrentMa; - private final double mTxCurrentMa; - private final double mRxCurrentMa; - private final PowerProfile mPowerProfile; + private final UsageBasedPowerEstimator mIdlePowerEstimator; + private final UsageBasedPowerEstimator mTxPowerEstimator; + private final UsageBasedPowerEstimator mRxPowerEstimator; + private final UsageBasedPowerEstimator mPowerOnPowerEstimator; + private final UsageBasedPowerEstimator mScanPowerEstimator; + private final UsageBasedPowerEstimator mBatchScanPowerEstimator; private final boolean mHasWifiPowerController; - private double mTotalAppPowerDrain = 0; - private long mTotalAppRunningTime = 0; - private WifiPowerEstimator mWifiPowerEstimator; - private boolean mHasWifiPowerReporting; + private final double mWifiPowerPerPacket; + + private static class PowerDurationAndTraffic { + public double powerMah; + public long durationMs; + + public long wifiRxPackets; + public long wifiTxPackets; + public long wifiRxBytes; + public long wifiTxBytes; + } public WifiPowerCalculator(PowerProfile profile) { - mPowerProfile = profile; + mPowerOnPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_WIFI_ON)); + mScanPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)); + mBatchScanPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN)); + mIdlePowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE)); + mTxPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX)); + mRxPowerEstimator = new UsageBasedPowerEstimator( + profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX)); + + mWifiPowerPerPacket = getWifiPowerPerPacket(profile); + + mHasWifiPowerController = + mIdlePowerEstimator.isSupported() && mTxPowerEstimator.isSupported() + && mRxPowerEstimator.isSupported(); + } + + @Override + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + + // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point, + // so always check this field. + final boolean hasWifiPowerReporting = + mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); + + final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder = + builder.getOrCreateSystemBatteryConsumerBuilder( + SystemBatteryConsumer.DRAIN_TYPE_WIFI); - mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE); - mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX); - mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX); + long totalAppDurationMs = 0; + double totalAppPowerMah = 0; + final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic(); + final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = + builder.getUidBatteryConsumerBuilders(); + for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { + final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); + calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED, + hasWifiPowerReporting); - mHasWifiPowerController = mIdleCurrentMa != 0 && mTxCurrentMa != 0 && mRxCurrentMa != 0; - mWifiPowerEstimator = new WifiPowerEstimator(mPowerProfile); + totalAppDurationMs += powerDurationAndTraffic.durationMs; + totalAppPowerMah += powerDurationAndTraffic.powerMah; + + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI, + powerDurationAndTraffic.durationMs); + app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI, + powerDurationAndTraffic.powerMah); + + if (app.getUid() == Process.WIFI_UID) { + systemBatteryConsumerBuilder.addUidBatteryConsumer(app); + app.excludeFromBatteryUsageStats(); + } + } + + calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED, + hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah); + + systemBatteryConsumerBuilder + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI, + powerDurationAndTraffic.durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI, + powerDurationAndTraffic.powerMah); } /** @@ -64,100 +137,151 @@ public class WifiPowerCalculator extends PowerCalculator { // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point, // so always check this field. - mHasWifiPowerReporting = mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); - - super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); + final boolean hasWifiPowerReporting = + mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); - BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0); - calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); + final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0); + long totalAppDurationMs = 0; + double totalAppPowerMah = 0; + final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic(); for (int i = sippers.size() - 1; i >= 0; i--) { - BatterySipper app = sippers.get(i); - if (app.getUid() == Process.WIFI_UID) { - if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs); - app.isAggregated = true; - bs.add(app); + final BatterySipper app = sippers.get(i); + if (app.drainType == BatterySipper.DrainType.APP) { + calculateApp(powerDurationAndTraffic, app.uidObj, rawRealtimeUs, statsType, + hasWifiPowerReporting); + + totalAppDurationMs += powerDurationAndTraffic.durationMs; + totalAppPowerMah += powerDurationAndTraffic.powerMah; + + app.wifiPowerMah = powerDurationAndTraffic.powerMah; + app.wifiRunningTimeMs = powerDurationAndTraffic.durationMs; + app.wifiRxBytes = powerDurationAndTraffic.wifiRxBytes; + app.wifiRxPackets = powerDurationAndTraffic.wifiRxPackets; + app.wifiTxBytes = powerDurationAndTraffic.wifiTxBytes; + app.wifiTxPackets = powerDurationAndTraffic.wifiTxPackets; + if (app.getUid() == Process.WIFI_UID) { + if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs); + app.isAggregated = true; + bs.add(app); + } } } + + calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, statsType, + hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah); + + bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs; + bs.wifiPowerMah += powerDurationAndTraffic.powerMah; + if (bs.sumPower() > 0) { sippers.add(bs); } } - @Override - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - if (!mHasWifiPowerReporting) { - mWifiPowerEstimator.calculateApp(app, u, rawRealtimeUs, rawUptimeUs, statsType); - return; - } - - final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity(); - if (counter == null) { - return; - } - - final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType); - final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType); - final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType); - app.wifiRunningTimeMs = idleTime + rxTime + txTime; - mTotalAppRunningTime += app.wifiRunningTimeMs; - - app.wifiPowerMah = - ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa)) - / (1000 * 60 * 60); - mTotalAppPowerDrain += app.wifiPowerMah; - - app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA, + private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, BatteryStats.Uid u, + long rawRealtimeUs, + int statsType, boolean hasWifiPowerReporting) { + powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets( + BatteryStats.NETWORK_WIFI_RX_DATA, statsType); - app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA, + powerDurationAndTraffic.wifiTxPackets = u.getNetworkActivityPackets( + BatteryStats.NETWORK_WIFI_TX_DATA, statsType); - app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA, + powerDurationAndTraffic.wifiRxBytes = u.getNetworkActivityBytes( + BatteryStats.NETWORK_WIFI_RX_DATA, statsType); - app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA, + powerDurationAndTraffic.wifiTxBytes = u.getNetworkActivityBytes( + BatteryStats.NETWORK_WIFI_TX_DATA, statsType); - if (DEBUG && app.wifiPowerMah != 0) { - Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime + "ms tx=" + - txTime + "ms power=" + formatCharge(app.wifiPowerMah)); - } - } + if (hasWifiPowerReporting) { + final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity(); + if (counter != null) { + final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType); + final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType); + final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType); + + powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime; + powerDurationAndTraffic.powerMah = mIdlePowerEstimator.calculatePower(idleTime) + + mTxPowerEstimator.calculatePower(txTime) + + mRxPowerEstimator.calculatePower(rxTime); - private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - if (!mHasWifiPowerReporting) { - mWifiPowerEstimator.calculateRemaining(app, stats, rawRealtimeUs, rawUptimeUs, - statsType); - return; + if (DEBUG && powerDurationAndTraffic.powerMah != 0) { + Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime + + "ms tx=" + txTime + "ms power=" + formatCharge( + powerDurationAndTraffic.powerMah)); + } + } + } else { + final double wifiPacketPower = ( + powerDurationAndTraffic.wifiRxPackets + powerDurationAndTraffic.wifiTxPackets) + * mWifiPowerPerPacket; + final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000; + final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000; + long batchScanTimeMs = 0; + for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { + batchScanTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000; + } + + powerDurationAndTraffic.durationMs = wifiRunningTime; + powerDurationAndTraffic.powerMah = wifiPacketPower + + mPowerOnPowerEstimator.calculatePower(wifiRunningTime) + + mScanPowerEstimator.calculatePower(wifiScanTimeMs) + + mBatchScanPowerEstimator.calculatePower(batchScanTimeMs); + + if (DEBUG && powerDurationAndTraffic.powerMah != 0) { + Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge( + powerDurationAndTraffic.powerMah)); + } } + } - final BatteryStats.ControllerActivityCounter counter = stats.getWifiControllerActivity(); + private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic, + BatteryStats stats, long rawRealtimeUs, + int statsType, boolean hasWifiPowerReporting, long totalAppDurationMs, + double totalAppPowerMah) { + long totalDurationMs; + double totalPowerMah; + if (hasWifiPowerReporting) { + final BatteryStats.ControllerActivityCounter counter = + stats.getWifiControllerActivity(); - final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType); - final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType); - final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType); + final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType); + final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType); + final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType); - app.wifiRunningTimeMs = Math.max(0, - (idleTimeMs + rxTimeMs + txTimeMs) - mTotalAppRunningTime); + totalDurationMs = idleTimeMs + rxTimeMs + txTimeMs; - double powerDrainMah = counter.getPowerCounter().getCountLocked(statsType) - / (double) (1000 * 60 * 60); - if (powerDrainMah == 0) { - // Some controllers do not report power drain, so we can calculate it here. - powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa) - + (rxTimeMs * mRxCurrentMa)) / (1000 * 60 * 60); + totalPowerMah = + counter.getPowerCounter().getCountLocked(statsType) / (double) (1000 * 60 * 60); + if (totalPowerMah == 0) { + // Some controllers do not report power drain, so we can calculate it here. + totalPowerMah = mIdlePowerEstimator.calculatePower(idleTimeMs) + + mTxPowerEstimator.calculatePower(txTimeMs) + + mRxPowerEstimator.calculatePower(rxTimeMs); + } + } else { + totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000; + totalPowerMah = mPowerOnPowerEstimator.calculatePower(totalDurationMs); } - app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain); + + powerDurationAndTraffic.durationMs = Math.max(0, totalDurationMs - totalAppDurationMs); + powerDurationAndTraffic.powerMah = Math.max(0, totalPowerMah - totalAppPowerMah); if (DEBUG) { - Log.d(TAG, "left over WiFi power: " + formatCharge(app.wifiPowerMah)); + Log.d(TAG, "left over WiFi power: " + formatCharge(powerDurationAndTraffic.powerMah)); } } - @Override - public void reset() { - mTotalAppPowerDrain = 0; - mTotalAppRunningTime = 0; - mWifiPowerEstimator.reset(); + /** + * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB. + */ + private static double getWifiPowerPerPacket(PowerProfile profile) { + // TODO(b/179392913): Extract average bit rates from system + final long wifiBps = 1000000; + final double averageWifiActivePower = + profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) / 3600; + return averageWifiActivePower / (((double) wifiBps) / 8 / 2048); } } diff --git a/core/java/com/android/internal/os/WifiPowerEstimator.java b/core/java/com/android/internal/os/WifiPowerEstimator.java deleted file mode 100644 index d0a105cfd958..000000000000 --- a/core/java/com/android/internal/os/WifiPowerEstimator.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES 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.internal.os; - -import android.os.BatteryStats; -import android.os.Process; -import android.os.UserHandle; -import android.util.Log; -import android.util.SparseArray; - -import java.util.List; - -/** - * Estimates WiFi power usage based on timers in BatteryStats. - */ -public class WifiPowerEstimator extends PowerCalculator { - private static final boolean DEBUG = BatteryStatsHelper.DEBUG; - private static final String TAG = "WifiPowerEstimator"; - private final double mWifiPowerPerPacket; - private final double mWifiPowerOn; - private final double mWifiPowerScan; - private final double mWifiPowerBatchScan; - private long mTotalAppWifiRunningTimeMs = 0; - - public WifiPowerEstimator(PowerProfile profile) { - mWifiPowerPerPacket = getWifiPowerPerPacket(profile); - mWifiPowerOn = profile.getAveragePower(PowerProfile.POWER_WIFI_ON); - mWifiPowerScan = profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN); - mWifiPowerBatchScan = profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN); - } - - @Override - public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { - super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); - - BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0); - calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); - - for (int i = sippers.size() - 1; i >= 0; i--) { - BatterySipper app = sippers.get(i); - if (app.getUid() == Process.WIFI_UID) { - if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs); - app.isAggregated = true; - bs.add(app); - } - } - if (bs.sumPower() > 0) { - sippers.add(bs); - } - } - - /** - * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB. - */ - private static double getWifiPowerPerPacket(PowerProfile profile) { - final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system - final double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) - / 3600; - return WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048); - } - - @Override - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA, - statsType); - app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA, - statsType); - app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA, - statsType); - app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA, - statsType); - - final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets) - * mWifiPowerPerPacket; - - app.wifiRunningTimeMs = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000; - mTotalAppWifiRunningTimeMs += app.wifiRunningTimeMs; - final double wifiLockPower = (app.wifiRunningTimeMs * mWifiPowerOn) / (1000*60*60); - - final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000; - final double wifiScanPower = (wifiScanTimeMs * mWifiPowerScan) / (1000*60*60); - - double wifiBatchScanPower = 0; - for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { - final long batchScanTimeMs = - u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000; - final double batchScanPower = (batchScanTimeMs * mWifiPowerBatchScan) / (1000*60*60); - wifiBatchScanPower += batchScanPower; - } - - app.wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower; - if (DEBUG && app.wifiPowerMah != 0) { - Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(app.wifiPowerMah)); - } - } - - void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - final long totalRunningTimeMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) - / 1000; - final double powerDrain = ((totalRunningTimeMs - mTotalAppWifiRunningTimeMs) * mWifiPowerOn) - / (1000*60*60); - app.wifiRunningTimeMs = totalRunningTimeMs; - app.wifiPowerMah = Math.max(0, powerDrain); - } - - @Override - public void reset() { - mTotalAppWifiRunningTimeMs = 0; - } -} diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index c8afea9b0982..6b1d408bee9a 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -18,8 +18,8 @@ package com.android.internal.os; import static android.system.OsConstants.O_CLOEXEC; -import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC; - +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.pm.ApplicationInfo; import android.net.Credentials; import android.net.LocalServerSocket; @@ -36,17 +36,16 @@ import android.util.Log; import com.android.internal.net.NetworkUtilsInternal; +import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; import dalvik.system.ZygoteHooks; import libcore.io.IoUtils; -import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.FileDescriptor; import java.io.IOException; -import java.io.InputStreamReader; /** @hide */ public final class Zygote { @@ -103,7 +102,7 @@ public final class Zygote { */ public static final int PROFILE_FROM_SHELL = 1 << 15; - /** + /* * Enable using the ART app image startup cache */ public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16; @@ -116,6 +115,13 @@ public final class Zygote { */ public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17; + /** + * Disable runtime access to {@link android.annotation.TestApi} annotated members. + * + * <p>This only takes effect if Hidden API access restrictions are enabled as well. + */ + public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18; + public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20); public static final int MEMORY_TAG_LEVEL_NONE = 0; @@ -231,6 +237,8 @@ public final class Zygote { */ public static final String CHILD_ZYGOTE_UID_RANGE_END = "--uid-range-end="; + private static final String TAG = "Zygote"; + /** Prefix prepended to socket names created by init */ private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_"; @@ -401,6 +409,10 @@ public final class Zygote { // Note that this event ends at the end of handleChildProc. Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); + if (gids != null && gids.length > 0) { + NetworkUtilsInternal.setAllowNetworkingForProcess(containsInetGid(gids)); + } + // Set the Java Language thread priority to the default value for new apps. Thread.currentThread().setPriority(Thread.NORM_PRIORITY); @@ -573,114 +585,163 @@ public final class Zygote { private static native int nativeGetUsapPoolEventFD(); /** - * Fork a new unspecialized app process from the zygote + * Fork a new unspecialized app process from the zygote. Adds the Usap to the native + * Usap table. * * @param usapPoolSocket The server socket the USAP will call accept on - * @param sessionSocketRawFDs Anonymous session sockets that are currently open - * @param isPriorityFork Value controlling the process priority level until accept is called - * @return In the Zygote process this function will always return null; in unspecialized app - * processes this function will return a Runnable object representing the new - * application that is passed up from usapMain. + * @param sessionSocketRawFDs Anonymous session sockets that are currently open. + * These are closed in the child. + * @param isPriorityFork Raise the initial process priority level because this is on the + * critical path for application startup. + * @return In the child process, this returns a Runnable that waits for specialization + * info to start an app process. In the sygote/parent process this returns null. */ - static Runnable forkUsap(LocalServerSocket usapPoolSocket, - int[] sessionSocketRawFDs, - boolean isPriorityFork) { - FileDescriptor[] pipeFDs; + static @Nullable Runnable forkUsap(LocalServerSocket usapPoolSocket, + int[] sessionSocketRawFDs, + boolean isPriorityFork) { + FileDescriptor readFD; + FileDescriptor writeFD; try { - pipeFDs = Os.pipe2(O_CLOEXEC); + FileDescriptor[] pipeFDs = Os.pipe2(O_CLOEXEC); + readFD = pipeFDs[0]; + writeFD = pipeFDs[1]; } catch (ErrnoException errnoEx) { throw new IllegalStateException("Unable to create USAP pipe.", errnoEx); } - int pid = - nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(), - sessionSocketRawFDs, isPriorityFork); - + int pid = nativeForkApp(readFD.getInt$(), writeFD.getInt$(), + sessionSocketRawFDs, /*argsKnown=*/ false, isPriorityFork); if (pid == 0) { - IoUtils.closeQuietly(pipeFDs[0]); - return usapMain(usapPoolSocket, pipeFDs[1]); + IoUtils.closeQuietly(readFD); + return childMain(null, usapPoolSocket, writeFD); + } else if (pid == -1) { + // Fork failed. + return null; } else { - // The read-end of the pipe will be closed by the native code. - // See removeUsapTableEntry(); - IoUtils.closeQuietly(pipeFDs[1]); + // readFD will be closed by the native code. See removeUsapTableEntry(); + IoUtils.closeQuietly(writeFD); + nativeAddUsapTableEntry(pid, readFD.getInt$()); return null; } } - private static native int nativeForkUsap(int readPipeFD, - int writePipeFD, - int[] sessionSocketRawFDs, - boolean isPriorityFork); + private static native int nativeForkApp(int readPipeFD, + int writePipeFD, + int[] sessionSocketRawFDs, + boolean argsKnown, + boolean isPriorityFork); + + /** + * Add an entry for a new Usap to the table maintained in native code. + */ + @CriticalNative + private static native void nativeAddUsapTableEntry(int pid, int readPipeFD); + + /** + * Fork a new app process from the zygote. argBuffer contains a fork command that + * request neither a child zygote, nor a wrapped process. Continue to accept connections + * on the specified socket, use those to refill argBuffer, and continue to process + * sufficiently simple fork requests. We presume that the only open file descriptors + * requiring special treatment are the session socket embedded in argBuffer, and + * zygoteSocket. + * @param argBuffer containing initial command and the connected socket from which to + * read more + * @param zygoteSocket socket from which to obtain new connections when current argBuffer + * one is disconnected + * @param expectedUId Uid of peer for initial requests. Subsequent requests from a different + * peer will cause us to return rather than perform the requested fork. + * @param minUid Minimum Uid enforced for all but first fork request. The caller checks + * the Uid policy for the initial request. + * @param firstNiceName name of first created process. Used for error reporting only. + * @return A Runnable in each child process, null in the parent. + * If this returns in then argBuffer still contains a command needing to be executed. + */ + static @Nullable Runnable forkSimpleApps(@NonNull ZygoteCommandBuffer argBuffer, + @NonNull FileDescriptor zygoteSocket, + int expectedUid, + int minUid, + @Nullable String firstNiceName) { + boolean in_child = + argBuffer.forkRepeatedly(zygoteSocket, expectedUid, minUid, firstNiceName); + if (in_child) { + return childMain(argBuffer, /*usapPoolSocket=*/null, /*writePipe=*/null); + } else { + return null; + } + } /** - * This function is used by unspecialized app processes to wait for specialization requests from - * the system server. + * Specialize the current process into one described by argBuffer or the command read from + * usapPoolSocket. Exactly one of those must be null. If we are given an argBuffer, we close + * it. Used both for a specializing a USAP process, and for process creation without USAPs. + * In both cases, we specialize the process after first returning to Java code. * * @param writePipe The write end of the reporting pipe used to communicate with the poll loop * of the ZygoteServer. * @return A runnable oject representing the new application. */ - private static Runnable usapMain(LocalServerSocket usapPoolSocket, - FileDescriptor writePipe) { + private static Runnable childMain(@Nullable ZygoteCommandBuffer argBuffer, + @Nullable LocalServerSocket usapPoolSocket, + FileDescriptor writePipe) { final int pid = Process.myPid(); - Process.setArgV0(Process.is64Bit() ? "usap64" : "usap32"); - LocalSocket sessionSocket = null; DataOutputStream usapOutputStream = null; - Credentials peerCredentials = null; ZygoteArguments args = null; - // Change the priority to max before calling accept so we can respond to new specialization - // requests as quickly as possible. This will be reverted to the default priority in the - // native specialization code. - boostUsapPriority(); - - while (true) { - try { - sessionSocket = usapPoolSocket.accept(); - - // Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool. - blockSigTerm(); - - BufferedReader usapReader = - new BufferedReader(new InputStreamReader(sessionSocket.getInputStream())); - usapOutputStream = - new DataOutputStream(sessionSocket.getOutputStream()); + // Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool. + blockSigTerm(); - peerCredentials = sessionSocket.getPeerCredentials(); + LocalSocket sessionSocket = null; + if (argBuffer == null) { + // Read arguments from usapPoolSocket instead. - String[] argStrings = readArgumentList(usapReader); + Process.setArgV0(Process.is64Bit() ? "usap64" : "usap32"); - if (argStrings != null) { - args = new ZygoteArguments(argStrings); + // Change the priority to max before calling accept so we can respond to new + // specialization requests as quickly as possible. This will be reverted to the + // default priority in the native specialization code. + boostUsapPriority(); + while (true) { + ZygoteCommandBuffer tmpArgBuffer = null; + try { + sessionSocket = usapPoolSocket.accept(); + + usapOutputStream = + new DataOutputStream(sessionSocket.getOutputStream()); + Credentials peerCredentials = sessionSocket.getPeerCredentials(); + tmpArgBuffer = new ZygoteCommandBuffer(sessionSocket); + args = ZygoteArguments.getInstance(argBuffer); + applyUidSecurityPolicy(args, peerCredentials); // TODO (chriswailes): Should this only be run for debug builds? validateUsapCommand(args); break; - } else { - Log.e("USAP", "Truncated command received."); - IoUtils.closeQuietly(sessionSocket); - - // Re-enable SIGTERM so the USAP can be flushed from the pool if necessary. - unblockSigTerm(); + } catch (Exception ex) { + Log.e("USAP", ex.getMessage()); } - } catch (Exception ex) { - Log.e("USAP", ex.getMessage()); - IoUtils.closeQuietly(sessionSocket); - // Re-enable SIGTERM so the USAP can be flushed from the pool if necessary. unblockSigTerm(); + IoUtils.closeQuietly(sessionSocket); + IoUtils.closeQuietly(tmpArgBuffer); + blockSigTerm(); + } + } else { + try { + args = ZygoteArguments.getInstance(argBuffer); + } catch (Exception ex) { + Log.e("AppStartup", ex.getMessage()); + throw new AssertionError("Failed to parse application start command", ex); } + // peerCredentials were checked in parent. + } + if (args == null) { + throw new AssertionError("Empty command line"); } - try { - // SIGTERM is blocked on loop exit. This prevents a USAP that is specializing from - // being killed during a pool flush. - - setAppProcessName(args, "USAP"); + // SIGTERM is blocked here. This prevents a USAP that is specializing from being + // killed during a pool flush. - applyUidSecurityPolicy(args, peerCredentials); applyDebuggerSystemProperty(args); int[][] rlimits = null; @@ -689,53 +750,57 @@ public final class Zygote { rlimits = args.mRLimits.toArray(INT_ARRAY_2D); } - // This must happen before the SELinux policy for this process is - // changed when specializing. - try { - // Used by ZygoteProcess.zygoteSendArgsAndGetResult to fill in a - // Process.ProcessStartResult object. - usapOutputStream.writeInt(pid); - } catch (IOException ioEx) { - Log.e("USAP", "Failed to write response to session socket: " - + ioEx.getMessage()); - throw new RuntimeException(ioEx); - } finally { - IoUtils.closeQuietly(sessionSocket); - + if (argBuffer == null) { + // This must happen before the SELinux policy for this process is + // changed when specializing. try { - // This socket is closed using Os.close due to an issue with the implementation - // of LocalSocketImp.close(). Because the raw FD is created by init and then - // loaded from an environment variable (as opposed to being created by the - // LocalSocketImpl itself) the current implementation will not actually close - // the underlying FD. - // - // See b/130309968 for discussion of this issue. - Os.close(usapPoolSocket.getFileDescriptor()); - } catch (ErrnoException ex) { - Log.e("USAP", "Failed to close USAP pool socket"); - throw new RuntimeException(ex); + // Used by ZygoteProcess.zygoteSendArgsAndGetResult to fill in a + // Process.ProcessStartResult object. + usapOutputStream.writeInt(pid); + } catch (IOException ioEx) { + Log.e("USAP", "Failed to write response to session socket: " + + ioEx.getMessage()); + throw new RuntimeException(ioEx); + } finally { + try { + // Since the raw FD is created by init and then loaded from an environment + // variable (as opposed to being created by the LocalSocketImpl itself), + // the LocalSocket/LocalSocketImpl does not own the Os-level socket. See + // the spec for LocalSocket.createConnectedLocalSocket(FileDescriptor fd). + // Thus closing the LocalSocket does not suffice. See b/130309968 for more + // discussion. + FileDescriptor fd = usapPoolSocket.getFileDescriptor(); + usapPoolSocket.close(); + Os.close(fd); + } catch (ErrnoException | IOException ex) { + Log.e("USAP", "Failed to close USAP pool socket"); + throw new RuntimeException(ex); + } } } - try { - ByteArrayOutputStream buffer = - new ByteArrayOutputStream(Zygote.USAP_MANAGEMENT_MESSAGE_BYTES); - DataOutputStream outputStream = new DataOutputStream(buffer); - - // This is written as a long so that the USAP reporting pipe and USAP pool event FD - // handlers in ZygoteServer.runSelectLoop can be unified. These two cases should - // both send/receive 8 bytes. - outputStream.writeLong(pid); - outputStream.flush(); - - Os.write(writePipe, buffer.toByteArray(), 0, buffer.size()); - } catch (Exception ex) { - Log.e("USAP", - String.format("Failed to write PID (%d) to pipe (%d): %s", - pid, writePipe.getInt$(), ex.getMessage())); - throw new RuntimeException(ex); - } finally { - IoUtils.closeQuietly(writePipe); + if (writePipe != null) { + try { + ByteArrayOutputStream buffer = + new ByteArrayOutputStream(Zygote.USAP_MANAGEMENT_MESSAGE_BYTES); + DataOutputStream outputStream = new DataOutputStream(buffer); + + // This is written as a long so that the USAP reporting pipe and USAP pool + // event FD handlers in ZygoteServer.runSelectLoop can be unified. These two + // cases should both send/receive 8 bytes. + // TODO: Needs tweaking to handle the non-Usap invoke-with case, which expects + // a different format. + outputStream.writeLong(pid); + outputStream.flush(); + Os.write(writePipe, buffer.toByteArray(), 0, buffer.size()); + } catch (Exception ex) { + Log.e("USAP", + String.format("Failed to write PID (%d) to pipe (%d): %s", + pid, writePipe.getInt$(), ex.getMessage())); + throw new RuntimeException(ex); + } finally { + IoUtils.closeQuietly(writePipe); + } } specializeAppProcess(args.mUid, args.mGid, args.mGids, @@ -842,13 +907,29 @@ public final class Zygote { return nativeRemoveUsapTableEntry(usapPID); } + @CriticalNative private static native boolean nativeRemoveUsapTableEntry(int usapPID); /** - * uid 1000 (Process.SYSTEM_UID) may specify any uid > 1000 in normal + * Return the minimum child uid that the given peer is allowed to create. + * uid 1000 (Process.SYSTEM_UID) may specify any uid ≥ 1000 in normal * operation. It may also specify any gid and setgroups() list it chooses. * In factory test mode, it may specify any UID. - * + */ + static int minChildUid(Credentials peer) { + if (peer.getUid() == Process.SYSTEM_UID + && FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF) { + /* In normal operation, SYSTEM_UID can only specify a restricted + * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid. + */ + return Process.SYSTEM_UID; + } else { + return 0; + } + } + + /* + * Adjust uid and gid arguments, ensuring that the security policy is satisfied. * @param args non-null; zygote spawner arguments * @param peer non-null; peer credentials * @throws ZygoteSecurityException Indicates a security issue when applying the UID based @@ -857,17 +938,10 @@ public final class Zygote { static void applyUidSecurityPolicy(ZygoteArguments args, Credentials peer) throws ZygoteSecurityException { - if (peer.getUid() == Process.SYSTEM_UID) { - /* In normal operation, SYSTEM_UID can only specify a restricted - * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid. - */ - boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF; - - if (uidRestricted && args.mUidSpecified && (args.mUid < Process.SYSTEM_UID)) { - throw new ZygoteSecurityException( - "System UID may not launch process with UID < " - + Process.SYSTEM_UID); - } + if (args.mUidSpecified && (args.mUid < minChildUid(peer))) { + throw new ZygoteSecurityException( + "System UID may not launch process with UID < " + + Process.SYSTEM_UID); } // If not otherwise specified, uid and gid are inherited from peer @@ -953,45 +1027,6 @@ public final class Zygote { } /** - * Reads an argument list from the provided socket - * @return Argument list or null if EOF is reached - * @throws IOException passed straight through - */ - static String[] readArgumentList(BufferedReader socketReader) throws IOException { - int argc; - - try { - String argc_string = socketReader.readLine(); - - if (argc_string == null) { - // EOF reached. - return null; - } - argc = Integer.parseInt(argc_string); - - } catch (NumberFormatException ex) { - Log.e("Zygote", "Invalid Zygote wire format: non-int at argc"); - throw new IOException("Invalid wire format"); - } - - // See bug 1092107: large argc can be used for a DOS attack - if (argc > MAX_ZYGOTE_ARGC) { - throw new IOException("Max arg count exceeded"); - } - - String[] args = new String[argc]; - for (int arg_index = 0; arg_index < argc; arg_index++) { - args[arg_index] = socketReader.readLine(); - if (args[arg_index] == null) { - // We got an unexpected EOF. - throw new IOException("Truncated request"); - } - } - - return args; - } - - /** * Creates a managed LocalServerSocket object using a file descriptor * created by an init.rc script. The init scripts that specify the * sockets name can be found in system/core/rootdir. The socket is bound diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java index 32b808ab2528..65b454d47db2 100644 --- a/core/java/com/android/internal/os/ZygoteArguments.java +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -16,8 +16,8 @@ package com.android.internal.os; +import java.io.EOFException; import java.util.ArrayList; -import java.util.Arrays; /** * Handles argument parsing for args related to the zygote spawner. @@ -245,20 +245,34 @@ class ZygoteArguments { /** * Constructs instance and parses args * - * @param args zygote command-line args + * @param args zygote command-line args as ZygoteCommandBuffer, positioned after argument count. */ - ZygoteArguments(String[] args) throws IllegalArgumentException { - parseArgs(args); + private ZygoteArguments(ZygoteCommandBuffer args, int argCount) + throws IllegalArgumentException, EOFException { + parseArgs(args, argCount); + } + + /** + * Return a new ZygoteArguments reflecting the contents of the given ZygoteCommandBuffer. Return + * null if the ZygoteCommandBuffer was positioned at EOF. Assumes the buffer is initially + * positioned at the beginning of the command. + */ + public static ZygoteArguments getInstance(ZygoteCommandBuffer args) + throws IllegalArgumentException, EOFException { + int argCount = args.getCount(); + return argCount == 0 ? null : new ZygoteArguments(args, argCount); } /** * Parses the commandline arguments intended for the Zygote spawner (such as "--setuid=" and - * "--setgid=") and creates an array containing the remaining args. + * "--setgid=") and creates an array containing the remaining args. Return false if we were + * at EOF. * * Per security review bug #1112214, duplicate args are disallowed in critical cases to make * injection harder. */ - private void parseArgs(String[] args) throws IllegalArgumentException { + private void parseArgs(ZygoteCommandBuffer args, int argCount) + throws IllegalArgumentException, EOFException { /* * See android.os.ZygoteProcess.zygoteSendArgsAndGetResult() * Presently the wire format to the zygote process is: @@ -269,13 +283,13 @@ class ZygoteArguments { * the child or -1 on failure. */ - int curArg = 0; - + String unprocessedArg = null; + int curArg = 0; // Index of arg boolean seenRuntimeArgs = false; - boolean expectRuntimeArgs = true; - for ( /* curArg */ ; curArg < args.length; curArg++) { - String arg = args[curArg]; + + for ( /* curArg */ ; curArg < argCount; ++curArg) { + String arg = args.nextArg(); if (arg.equals("--")) { curArg++; @@ -367,7 +381,8 @@ class ZygoteArguments { "Duplicate arg specified"); } try { - mInvokeWith = args[++curArg]; + ++curArg; + mInvokeWith = args.nextArg(); } catch (IndexOutOfBoundsException ex) { throw new IllegalArgumentException( "--invoke-with requires argument"); @@ -397,12 +412,14 @@ class ZygoteArguments { } else if (arg.startsWith("--app-data-dir=")) { mAppDataDir = getAssignmentValue(arg); } else if (arg.equals("--preload-app")) { - mPreloadApp = args[++curArg]; + ++curArg; + mPreloadApp = args.nextArg(); } else if (arg.equals("--preload-package")) { - mPreloadPackage = args[++curArg]; - mPreloadPackageLibs = args[++curArg]; - mPreloadPackageLibFileName = args[++curArg]; - mPreloadPackageCacheKey = args[++curArg]; + curArg += 4; + mPreloadPackage = args.nextArg(); + mPreloadPackageLibs = args.nextArg(); + mPreloadPackageLibFileName = args.nextArg(); + mPreloadPackageCacheKey = args.nextArg(); } else if (arg.equals("--preload-default")) { mPreloadDefault = true; expectRuntimeArgs = false; @@ -411,8 +428,11 @@ class ZygoteArguments { } else if (arg.equals("--set-api-denylist-exemptions")) { // consume all remaining args; this is a stand-alone command, never included // with the regular fork command. - mApiDenylistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length); - curArg = args.length; + mApiDenylistExemptions = new String[argCount - curArg - 1]; + ++curArg; + for (int i = 0; curArg < argCount; ++curArg, ++i) { + mApiDenylistExemptions[i] = args.nextArg(); + } expectRuntimeArgs = false; } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) { String rateStr = getAssignmentValue(arg); @@ -462,35 +482,46 @@ class ZygoteArguments { } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) { mBindMountAppDataDirs = true; } else { + unprocessedArg = arg; break; } } + // curArg is the index of the first unprocessed argument. That argument is either referenced + // by unprocessedArg or not read yet. if (mBootCompleted) { - if (args.length - curArg > 0) { + if (argCount > curArg) { throw new IllegalArgumentException("Unexpected arguments after --boot-completed"); } } else if (mAbiListQuery || mPidQuery) { - if (args.length - curArg > 0) { + if (argCount > curArg) { throw new IllegalArgumentException("Unexpected arguments after --query-abi-list."); } } else if (mPreloadPackage != null) { - if (args.length - curArg > 0) { + if (argCount > curArg) { throw new IllegalArgumentException( "Unexpected arguments after --preload-package."); } } else if (mPreloadApp != null) { - if (args.length - curArg > 0) { + if (argCount > curArg) { throw new IllegalArgumentException( "Unexpected arguments after --preload-app."); } } else if (expectRuntimeArgs) { if (!seenRuntimeArgs) { - throw new IllegalArgumentException("Unexpected argument : " + args[curArg]); + throw new IllegalArgumentException("Unexpected argument : " + + (unprocessedArg == null ? args.nextArg() : unprocessedArg)); } - mRemainingArgs = new String[args.length - curArg]; - System.arraycopy(args, curArg, mRemainingArgs, 0, mRemainingArgs.length); + mRemainingArgs = new String[argCount - curArg]; + int i = 0; + if (unprocessedArg != null) { + mRemainingArgs[0] = unprocessedArg; + ++i; + } + for (; i < argCount - curArg; ++i) { + mRemainingArgs[i] = args.nextArg(); + } } if (mStartChildZygote) { diff --git a/core/java/com/android/internal/os/ZygoteCommandBuffer.java b/core/java/com/android/internal/os/ZygoteCommandBuffer.java new file mode 100644 index 000000000000..b61ae7acfacd --- /dev/null +++ b/core/java/com/android/internal/os/ZygoteCommandBuffer.java @@ -0,0 +1,187 @@ +/* + * 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.internal.os; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.LocalSocket; + +import java.io.FileDescriptor; +import java.lang.ref.Reference; // For reachabilityFence. + +/** + * A native-accessible buffer for Zygote commands. Designed to support repeated forking + * of applications without intervening memory allocation, thus keeping zygote memory + * as stable as possible. + * A ZygoteCommandBuffer may have an associated socket from which it can be refilled. + * Otherwise the contents are explicitly set by getInstance(). + * + * NOT THREAD-SAFE. No methods may be called concurrently from multiple threads. + * + * Only one ZygoteCommandBuffer can exist at a time. + * Must be explicitly closed before being dropped. + * @hide + */ +class ZygoteCommandBuffer implements AutoCloseable { + private long mNativeBuffer; // Not final so that we can clear it in close(). + + /** + * The command socket. + * + * mSocket is retained in the child process in "peer wait" mode, so + * that it closes when the child process terminates. In other cases, + * it is closed in the peer. + */ + private final LocalSocket mSocket; + private final int mNativeSocket; + + /** + * Constructs instance from file descriptor from which the command will be read. + * Only a single instance may be live in a given process. The native code checks. + * + * @param fd file descriptor to read from. The setCommand() method may be used if and only if + * fd is null. + */ + ZygoteCommandBuffer(@Nullable LocalSocket socket) { + mSocket = socket; + if (socket == null) { + mNativeSocket = -1; + } else { + mNativeSocket = mSocket.getFileDescriptor().getInt$(); + } + mNativeBuffer = getNativeBuffer(mNativeSocket); + } + + /** + * Constructs an instance with explicitly supplied arguments and an invalid + * file descriptor. Can only be used for a single command. + */ + ZygoteCommandBuffer(@NonNull String[] args) { + this((LocalSocket) null); + setCommand(args); + } + + + private static native long getNativeBuffer(int fd); + + /** + * Deallocate native resources associated with the one and only command buffer, and prevent + * reuse. Subsequent calls to getInstance() will yield a new buffer. + * We do not close the associated socket, if any. + */ + @Override + public void close() { + freeNativeBuffer(mNativeBuffer); + mNativeBuffer = 0; + } + + private static native void freeNativeBuffer(long /* NativeCommandBuffer* */ nbuffer); + + /** + * Read at least the first line of the next command into the buffer, return the argument count + * from that line. Assumes we are initially positioned at the beginning of the first line of + * the command. Leave the buffer positioned at the beginning of the second command line, i.e. + * the first argument. If the buffer has no associated file descriptor, we just reposition to + * the beginning of the buffer, and reread existing contents. Returns zero if we started out + * at EOF. + */ + int getCount() { + try { + return nativeGetCount(mNativeBuffer); + } finally { + // Make sure the mNativeSocket doesn't get closed due to early finalization. + Reference.reachabilityFence(mSocket); + } + } + + private static native int nativeGetCount(long /* NativeCommandBuffer* */ nbuffer); + + + /* + * Set the buffer to contain the supplied sequence of arguments. + */ + private void setCommand(String[] command) { + int nArgs = command.length; + insert(mNativeBuffer, Integer.toString(nArgs)); + for (String s: command) { + insert(mNativeBuffer, s); + } + // Native code checks there is no socket; hence no reachabilityFence. + } + + private static native void insert(long /* NativeCommandBuffer* */ nbuffer, String s); + + /** + * Retrieve the next argument/line from the buffer, filling the buffer as necessary. + */ + String nextArg() { + try { + return nativeNextArg(mNativeBuffer); + } finally { + Reference.reachabilityFence(mSocket); + } + } + + private static native String nativeNextArg(long /* NativeCommandBuffer* */ nbuffer); + + void readFullyAndReset() { + try { + nativeReadFullyAndReset(mNativeBuffer); + } finally { + Reference.reachabilityFence(mSocket); + } + } + + private static native void nativeReadFullyAndReset(long /* NativeCommandBuffer* */ nbuffer); + + /** + * Fork a child as specified by the current command in the buffer, and repeat this process + * after refilling the buffer, so long as the buffer clearly contains another fork command. + * + * @param zygoteSocket socket from which to obtain new connections when current one is + * disconnected + * @param expectedUid Peer UID for current connection. We refuse to deal with requests from + * a different UID. + * @param minUid the smallest uid that may be request for the child process. + * @param firstNiceName The name for the initial process to be forked. Used only for error + * reporting. + * + * @return true in the child, false in the parent. In the parent case, the buffer is positioned + * at the beginning of a command that still needs to be processed. + */ + boolean forkRepeatedly(FileDescriptor zygoteSocket, int expectedUid, int minUid, + String firstNiceName) { + try { + return nativeForkRepeatedly(mNativeBuffer, zygoteSocket.getInt$(), + expectedUid, minUid, firstNiceName); + } finally { + Reference.reachabilityFence(mSocket); + Reference.reachabilityFence(zygoteSocket); + } + } + + /* + * Repeatedly fork children as above. It commonly does not return in the parent, but it may. + * @return true in the chaild, false in the parent if we encounter a command we couldn't handle. + */ + private static native boolean nativeForkRepeatedly(long /* NativeCommandBuffer* */ nbuffer, + int zygoteSocketRawFd, + int expectedUid, + int minUid, + String firstNiceName); + +} diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 5a576ebbc442..37c75907061c 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -36,16 +36,15 @@ import android.system.StructPollfd; import android.util.Log; import dalvik.system.VMRuntime; +import dalvik.system.ZygoteHooks; import libcore.io.IoUtils; -import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileDescriptor; import java.io.IOException; -import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.concurrent.TimeUnit; @@ -67,7 +66,6 @@ class ZygoteConnection { private final LocalSocket mSocket; @UnsupportedAppUsage private final DataOutputStream mSocketOutStream; - private final BufferedReader mSocketReader; @UnsupportedAppUsage private final Credentials peer; private final String abiList; @@ -85,9 +83,6 @@ class ZygoteConnection { this.abiList = abiList; mSocketOutStream = new DataOutputStream(socket.getOutputStream()); - mSocketReader = - new BufferedReader( - new InputStreamReader(socket.getInputStream()), Zygote.SOCKET_BUFFER_SIZE); mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS); @@ -111,178 +106,216 @@ class ZygoteConnection { } /** - * Reads one start command from the command socket. If successful, a child is forked and a + * Reads a command from the command socket. If a child is successfully forked, a * {@code Runnable} that calls the childs main method (or equivalent) is returned in the child * process. {@code null} is always returned in the parent process (the zygote). + * If multipleOK is set, we may keep processing additional fork commands before returning. * * If the client closes the socket, an {@code EOF} condition is set, which callers can test * for by calling {@code ZygoteConnection.isClosedByPeer}. */ - Runnable processOneCommand(ZygoteServer zygoteServer) { - String[] args; - - try { - args = Zygote.readArgumentList(mSocketReader); - } catch (IOException ex) { - throw new IllegalStateException("IOException on command socket", ex); - } - - // readArgumentList returns null only when it has reached EOF with no available - // data to read. This will only happen when the remote socket has disconnected. - if (args == null) { - isEof = true; - return null; - } - - int pid; - FileDescriptor childPipeFd = null; - FileDescriptor serverPipeFd = null; - - ZygoteArguments parsedArgs = new ZygoteArguments(args); - - if (parsedArgs.mBootCompleted) { - handleBootCompleted(); - return null; - } - - if (parsedArgs.mAbiListQuery) { - handleAbiListQuery(); - return null; - } - - if (parsedArgs.mPidQuery) { - handlePidQuery(); - return null; - } + Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) { + ZygoteArguments parsedArgs; + + try (ZygoteCommandBuffer argBuffer = new ZygoteCommandBuffer(mSocket)) { + while (true) { + try { + parsedArgs = ZygoteArguments.getInstance(argBuffer); + // Keep argBuffer around, since we need it to fork. + } catch (IOException ex) { + throw new IllegalStateException("IOException on command socket", ex); + } + if (parsedArgs == null) { + isEof = true; + return null; + } - if (parsedArgs.mUsapPoolStatusSpecified) { - return handleUsapPoolStatusChange(zygoteServer, parsedArgs.mUsapPoolEnabled); - } + int pid; + FileDescriptor childPipeFd = null; + FileDescriptor serverPipeFd = null; - if (parsedArgs.mPreloadDefault) { - handlePreload(); - return null; - } + if (parsedArgs.mBootCompleted) { + handleBootCompleted(); + return null; + } - if (parsedArgs.mPreloadPackage != null) { - handlePreloadPackage(parsedArgs.mPreloadPackage, parsedArgs.mPreloadPackageLibs, - parsedArgs.mPreloadPackageLibFileName, parsedArgs.mPreloadPackageCacheKey); - return null; - } + if (parsedArgs.mAbiListQuery) { + handleAbiListQuery(); + return null; + } - if (canPreloadApp() && parsedArgs.mPreloadApp != null) { - byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp); - Parcel appInfoParcel = Parcel.obtain(); - appInfoParcel.unmarshall(rawParcelData, 0, rawParcelData.length); - appInfoParcel.setDataPosition(0); - ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(appInfoParcel); - appInfoParcel.recycle(); - if (appInfo != null) { - handlePreloadApp(appInfo); - } else { - throw new IllegalArgumentException("Failed to deserialize --preload-app"); - } - return null; - } + if (parsedArgs.mPidQuery) { + handlePidQuery(); + return null; + } - if (parsedArgs.mApiDenylistExemptions != null) { - return handleApiDenylistExemptions(zygoteServer, parsedArgs.mApiDenylistExemptions); - } + if (parsedArgs.mUsapPoolStatusSpecified) { + // Handle this once we've released the argBuffer, to avoid opening a second one. + break; + } - if (parsedArgs.mHiddenApiAccessLogSampleRate != -1 - || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) { - return handleHiddenApiAccessLogSampleRate(zygoteServer, - parsedArgs.mHiddenApiAccessLogSampleRate, - parsedArgs.mHiddenApiAccessStatslogSampleRate); - } + if (parsedArgs.mPreloadDefault) { + handlePreload(); + return null; + } - if (parsedArgs.mPermittedCapabilities != 0 || parsedArgs.mEffectiveCapabilities != 0) { - throw new ZygoteSecurityException("Client may not specify capabilities: " - + "permitted=0x" + Long.toHexString(parsedArgs.mPermittedCapabilities) - + ", effective=0x" + Long.toHexString(parsedArgs.mEffectiveCapabilities)); - } + if (parsedArgs.mPreloadPackage != null) { + handlePreloadPackage(parsedArgs.mPreloadPackage, + parsedArgs.mPreloadPackageLibs, + parsedArgs.mPreloadPackageLibFileName, + parsedArgs.mPreloadPackageCacheKey); + return null; + } - Zygote.applyUidSecurityPolicy(parsedArgs, peer); - Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer); + if (canPreloadApp() && parsedArgs.mPreloadApp != null) { + byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp); + Parcel appInfoParcel = Parcel.obtain(); + appInfoParcel.unmarshall(rawParcelData, 0, rawParcelData.length); + appInfoParcel.setDataPosition(0); + ApplicationInfo appInfo = + ApplicationInfo.CREATOR.createFromParcel(appInfoParcel); + appInfoParcel.recycle(); + if (appInfo != null) { + handlePreloadApp(appInfo); + } else { + throw new IllegalArgumentException("Failed to deserialize --preload-app"); + } + return null; + } - Zygote.applyDebuggerSystemProperty(parsedArgs); - Zygote.applyInvokeWithSystemProperty(parsedArgs); + if (parsedArgs.mApiDenylistExemptions != null) { + return handleApiDenylistExemptions(zygoteServer, + parsedArgs.mApiDenylistExemptions); + } - int[][] rlimits = null; + if (parsedArgs.mHiddenApiAccessLogSampleRate != -1 + || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) { + return handleHiddenApiAccessLogSampleRate(zygoteServer, + parsedArgs.mHiddenApiAccessLogSampleRate, + parsedArgs.mHiddenApiAccessStatslogSampleRate); + } - if (parsedArgs.mRLimits != null) { - rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D); - } + if (parsedArgs.mPermittedCapabilities != 0 + || parsedArgs.mEffectiveCapabilities != 0) { + throw new ZygoteSecurityException("Client may not specify capabilities: " + + "permitted=0x" + Long.toHexString(parsedArgs.mPermittedCapabilities) + + ", effective=0x" + + Long.toHexString(parsedArgs.mEffectiveCapabilities)); + } - int[] fdsToIgnore = null; + Zygote.applyUidSecurityPolicy(parsedArgs, peer); + Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer); - if (parsedArgs.mInvokeWith != null) { - try { - FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC); - childPipeFd = pipeFds[1]; - serverPipeFd = pipeFds[0]; - Os.fcntlInt(childPipeFd, F_SETFD, 0); - fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()}; - } catch (ErrnoException errnoEx) { - throw new IllegalStateException("Unable to set up pipe for invoke-with", errnoEx); - } - } + Zygote.applyDebuggerSystemProperty(parsedArgs); + Zygote.applyInvokeWithSystemProperty(parsedArgs); - /* - * In order to avoid leaking descriptors to the Zygote child, - * the native code must close the two Zygote socket descriptors - * in the child process before it switches from Zygote-root to - * the UID and privileges of the application being launched. - * - * In order to avoid "bad file descriptor" errors when the - * two LocalSocket objects are closed, the Posix file - * descriptors are released via a dup2() call which closes - * the socket and substitutes an open descriptor to /dev/null. - */ + int[][] rlimits = null; - int [] fdsToClose = { -1, -1 }; + if (parsedArgs.mRLimits != null) { + rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D); + } - FileDescriptor fd = mSocket.getFileDescriptor(); + int[] fdsToIgnore = null; + + if (parsedArgs.mInvokeWith != null) { + try { + FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC); + childPipeFd = pipeFds[1]; + serverPipeFd = pipeFds[0]; + Os.fcntlInt(childPipeFd, F_SETFD, 0); + fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()}; + } catch (ErrnoException errnoEx) { + throw new IllegalStateException("Unable to set up pipe for invoke-with", + errnoEx); + } + } - if (fd != null) { - fdsToClose[0] = fd.getInt$(); - } + /* + * In order to avoid leaking descriptors to the Zygote child, + * the native code must close the two Zygote socket descriptors + * in the child process before it switches from Zygote-root to + * the UID and privileges of the application being launched. + * + * In order to avoid "bad file descriptor" errors when the + * two LocalSocket objects are closed, the Posix file + * descriptors are released via a dup2() call which closes + * the socket and substitutes an open descriptor to /dev/null. + */ - fd = zygoteServer.getZygoteSocketFileDescriptor(); + int [] fdsToClose = { -1, -1 }; - if (fd != null) { - fdsToClose[1] = fd.getInt$(); - } + FileDescriptor fd = mSocket.getFileDescriptor(); - pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids, - parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo, - parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote, - parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp, - parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList, - parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs); + if (fd != null) { + fdsToClose[0] = fd.getInt$(); + } - try { - if (pid == 0) { - // in child - zygoteServer.setForkChild(); + FileDescriptor zygoteFd = zygoteServer.getZygoteSocketFileDescriptor(); - zygoteServer.closeServerSocket(); - IoUtils.closeQuietly(serverPipeFd); - serverPipeFd = null; + if (zygoteFd != null) { + fdsToClose[1] = zygoteFd.getInt$(); + } - return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote); - } else { - // In the parent. A pid < 0 indicates a failure and will be handled in - // handleParentProc. - IoUtils.closeQuietly(childPipeFd); - childPipeFd = null; - handleParentProc(pid, serverPipeFd); - return null; + if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote + || !multipleOK || peer.getUid() != Process.SYSTEM_UID) { + // Continue using old code for now. TODO: Handle these cases in the other path. + pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, + parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits, + parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName, + fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote, + parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, + parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList, + parsedArgs.mWhitelistedDataInfoList, parsedArgs.mBindMountAppDataDirs, + parsedArgs.mBindMountAppStorageDirs); + + try { + if (pid == 0) { + // in child + zygoteServer.setForkChild(); + + zygoteServer.closeServerSocket(); + IoUtils.closeQuietly(serverPipeFd); + serverPipeFd = null; + + return handleChildProc(parsedArgs, childPipeFd, + parsedArgs.mStartChildZygote); + } else { + // In the parent. A pid < 0 indicates a failure and will be handled in + // handleParentProc. + IoUtils.closeQuietly(childPipeFd); + childPipeFd = null; + handleParentProc(pid, serverPipeFd); + return null; + } + } finally { + IoUtils.closeQuietly(childPipeFd); + IoUtils.closeQuietly(serverPipeFd); + } + } else { + ZygoteHooks.preFork(); + Runnable result = Zygote.forkSimpleApps(argBuffer, + zygoteServer.getZygoteSocketFileDescriptor(), + peer.getUid(), Zygote.minChildUid(peer), parsedArgs.mNiceName); + if (result == null) { + // parent; we finished some number of forks. Result is Boolean. + // We already did the equivalent of handleParentProc(). + ZygoteHooks.postForkCommon(); + // argBuffer contains a command not understood by forksimpleApps. + continue; + } else { + // child; result is a Runnable. + zygoteServer.setForkChild(); + Zygote.setAppProcessName(parsedArgs, TAG); // ??? Necessary? + return result; + } + } } - } finally { - IoUtils.closeQuietly(childPipeFd); - IoUtils.closeQuietly(serverPipeFd); } + if (parsedArgs.mUsapPoolStatusSpecified) { + // Now that we've released argBuffer: + return handleUsapPoolStatusChange(zygoteServer, parsedArgs.mUsapPoolEnabled); + } + throw new AssertionError("Shouldn't get here"); } private void handleAbiListQuery() { @@ -557,7 +590,7 @@ class ZygoteConnection { if (res > 0) { if ((fds[0].revents & POLLIN) != 0) { - // Only read one byte, so as not to block. + // Only read one byte, so as not to block. Really needed? int readBytes = android.system.Os.read(pipeFd, data, dataIndex, 1); if (readBytes < 0) { throw new RuntimeException("Some error"); diff --git a/core/java/com/android/internal/os/ZygoteConnectionConstants.java b/core/java/com/android/internal/os/ZygoteConnectionConstants.java index 506e39f30617..0c1cd6de1bb4 100644 --- a/core/java/com/android/internal/os/ZygoteConnectionConstants.java +++ b/core/java/com/android/internal/os/ZygoteConnectionConstants.java @@ -31,9 +31,6 @@ public class ZygoteConnectionConstants { */ public static final int CONNECTION_TIMEOUT_MILLIS = 1000; - /** max number of arguments that a connection can specify */ - public static final int MAX_ZYGOTE_ARGC = 1024; - /** * Wait time for a wrapped app to report back its pid. * diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index f2ed50ef6a07..d5b778e9c9e1 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -65,6 +65,7 @@ import dalvik.system.ZygoteHooks; import libcore.io.IoUtils; import java.io.BufferedReader; +import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -782,7 +783,13 @@ public class ZygoteInit { int pid; try { - parsedArgs = new ZygoteArguments(args); + ZygoteCommandBuffer commandBuffer = new ZygoteCommandBuffer(args); + try { + parsedArgs = ZygoteArguments.getInstance(commandBuffer); + } catch (EOFException e) { + throw new AssertionError("Unexpected argument error for forking system server", e); + } + commandBuffer.close(); Zygote.applyDebuggerSystemProperty(parsedArgs); Zygote.applyInvokeWithSystemProperty(parsedArgs); @@ -861,7 +868,7 @@ public class ZygoteInit { * into new processes are required to either set the priority to the default value or terminate * before executing any non-system code. The native side of this occurs in SpecializeCommon, * while the Java Language priority is changed in ZygoteInit.handleSystemServerProcess, - * ZygoteConnection.handleChildProc, and Zygote.usapMain. + * ZygoteConnection.handleChildProc, and Zygote.childMain. * * @param argv Command line arguments used to specify the Zygote's configuration. */ diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index 585ddf6ddf98..f71b31493035 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -337,7 +337,7 @@ class ZygoteServer { * @param sessionSocketRawFDs Anonymous session sockets that are currently open * @return In the Zygote process this function will always return null; in unspecialized app * processes this function will return a Runnable object representing the new - * application that is passed up from usapMain. + * application that is passed up from childMain (the usap's main wait loop). */ Runnable fillUsapPool(int[] sessionSocketRawFDs, boolean isPriorityRefill) { @@ -420,6 +420,7 @@ class ZygoteServer { * Runs the zygote process's select loop. Accepts new connections as * they happen, and reads commands from connections one spawn-request's * worth at a time. + * @param abiList list of ABIs supported by this zygote. */ Runnable runSelectLoop(String abiList) { ArrayList<FileDescriptor> socketFDs = new ArrayList<>(); @@ -537,22 +538,23 @@ class ZygoteServer { if (pollIndex == 0) { // Zygote server socket - ZygoteConnection newPeer = acceptCommandPeer(abiList); peers.add(newPeer); socketFDs.add(newPeer.getFileDescriptor()); - } else if (pollIndex < usapPoolEventFDIndex) { // Session socket accepted from the Zygote server socket try { ZygoteConnection connection = peers.get(pollIndex); - final Runnable command = connection.processOneCommand(this); + boolean multipleForksOK = !isUsapPoolEnabled() + && ZygoteHooks.indefiniteThreadSuspensionOK(); + final Runnable command = + connection.processCommand(this, multipleForksOK); // TODO (chriswailes): Is this extra check necessary? if (mIsForkChild) { // We're in the child. We should always have a command to run at - // this stage if processOneCommand hasn't called "exec". + // this stage if processCommand hasn't called "exec". if (command == null) { throw new IllegalStateException("command == null"); } @@ -565,7 +567,7 @@ class ZygoteServer { } // We don't know whether the remote side of the socket was closed or - // not until we attempt to read from it from processOneCommand. This + // not until we attempt to read from it from processCommand. This // shows up as a regular POLLIN event in our regular processing // loop. if (connection.isClosedByPeer()) { diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java index 56b25b2060ea..93ba0372df08 100644 --- a/core/java/com/android/internal/policy/TransitionAnimation.java +++ b/core/java/com/android/internal/policy/TransitionAnimation.java @@ -180,6 +180,7 @@ public class TransitionAnimation { "android", com.android.internal.R.anim.cross_profile_apps_thumbnail_enter); } + /** Load animation by resource Id from specific LayoutParams. */ @Nullable private Animation loadAnimationRes(LayoutParams lp, int resId) { Context context = mContext; @@ -193,6 +194,7 @@ public class TransitionAnimation { return null; } + /** Load animation by resource Id from specific package. */ @Nullable private Animation loadAnimationRes(String packageName, int resId) { if (ResourceId.isValid(resId)) { @@ -204,6 +206,13 @@ public class TransitionAnimation { return null; } + /** Load animation by resource Id from android package. */ + @Nullable + public Animation loadDefaultAnimationRes(int resId) { + return loadAnimationRes("android", resId); + } + + /** Load animation by attribute Id from specific LayoutParams */ @Nullable public Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) { int resId = Resources.ID_NULL; @@ -222,6 +231,25 @@ public class TransitionAnimation { return null; } + /** Load animation by attribute Id from android package. */ + @Nullable + public Animation loadDefaultAnimationAttr(int animAttr) { + int resId = Resources.ID_NULL; + Context context = mContext; + if (animAttr >= 0) { + AttributeCache.Entry ent = getCachedAnimations("android", + mDefaultWindowAnimationStyleResId); + if (ent != null) { + context = ent.context; + resId = ent.array.getResourceId(animAttr, 0); + } + } + if (ResourceId.isValid(resId)) { + return loadAnimationSafely(context, resId, mTag); + } + return null; + } + @Nullable private AttributeCache.Entry getCachedAnimations(LayoutParams lp) { if (mDebug) { diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java index d7b4d78c56cf..d49203c731e9 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -193,34 +193,30 @@ public class MeasuredEnergyStats { return mAccumulatedEnergiesMicroJoules.length; } - // TODO: Get rid of the 'accumulate' boolean. It's always true. /** Updates the given standard energy bucket with the given energy if accumulate is true. */ - public void updateStandardBucket(@StandardEnergyBucket int bucket, long energyDeltaUJ, - boolean accumulate) { + public void updateStandardBucket(@StandardEnergyBucket int bucket, long energyDeltaUJ) { checkValidStandardBucket(bucket); - updateEntry(bucket, energyDeltaUJ, accumulate); + updateEntry(bucket, energyDeltaUJ); } /** Updates the given custom energy bucket with the given energy if accumulate is true. */ - public void updateCustomBucket(int customBucket, long energyDeltaUJ, boolean accumulate) { + public void updateCustomBucket(int customBucket, long energyDeltaUJ) { if (!isValidCustomBucket(customBucket)) { Slog.e(TAG, "Attempted to update invalid custom bucket " + customBucket); return; } final int index = customBucketToIndex(customBucket); - updateEntry(index, energyDeltaUJ, accumulate); + updateEntry(index, energyDeltaUJ); } /** Updates the given index with the given energy if accumulate is true. */ - private void updateEntry(int index, long energyDeltaUJ, boolean accumulate) { - if (accumulate) { - if (mAccumulatedEnergiesMicroJoules[index] >= 0L) { - mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ; - } else { - Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket " - + getBucketName(index) + " whose value was " - + mAccumulatedEnergiesMicroJoules[index]); - } + private void updateEntry(int index, long energyDeltaUJ) { + if (mAccumulatedEnergiesMicroJoules[index] >= 0L) { + mAccumulatedEnergiesMicroJoules[index] += energyDeltaUJ; + } else { + Slog.wtf(TAG, "Attempting to add " + energyDeltaUJ + " to unavailable bucket " + + getBucketName(index) + " whose value was " + + mAccumulatedEnergiesMicroJoules[index]); } } diff --git a/core/java/com/android/internal/power/TEST_MAPPING b/core/java/com/android/internal/power/TEST_MAPPING new file mode 100644 index 000000000000..96f31bcbe5b2 --- /dev/null +++ b/core/java/com/android/internal/power/TEST_MAPPING @@ -0,0 +1,19 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { "include-filter": "com.android.internal.os.BatteryStatsTests" }, + { "exclude-annotation": "com.android.internal.os.SkipPresubmit" } + ] + }, + { + "name": "FrameworksServicesTests", + "options": [ + { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }, + { "include-filter": "com.android.server.am.MeasuredEnergySnapshotTest" }, + { "include-filter": "com.android.server.am.BatteryExternalStatsWorkerTest" } + ] + } + ] +} diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index bb0fd7fb8c23..fde48e86b0f3 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -22,6 +22,7 @@ import android.graphics.Rect; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.net.Uri; +import android.os.Bundle; import android.os.UserHandle; import android.service.notification.StatusBarNotification; @@ -66,7 +67,7 @@ interface IStatusBarService void onNotificationError(String pkg, String tag, int id, int uid, int initialPid, String message, int userId); void onClearAllNotifications(int userId); - void onNotificationClear(String pkg, String tag, int id, int userId, String key, + void onNotificationClear(String pkg, int userId, String key, int dismissalSurface, int dismissalSentiment, in NotificationVisibility nv); void onNotificationVisibilityChanged( in NotificationVisibility[] newlyVisibleKeys, in NotificationVisibility[] noLongerVisibleKeys); @@ -82,6 +83,7 @@ interface IStatusBarService void hideCurrentInputMethodForBubbles(); void grantInlineReplyUriPermission(String key, in Uri uri, in UserHandle user, String packageName); void clearInlineReplyUriPermissions(String key); + void onNotificationFeedbackReceived(String key, in Bundle feedback); void onGlobalActionsShown(); void onGlobalActionsHidden(); diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index f7d440d9fd95..95e0a3b524c5 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -94,6 +94,6 @@ interface ITelephonyRegistry { void notifyBarringInfoChanged(int slotIndex, int subId, in BarringInfo barringInfo); void notifyPhysicalChannelConfigForSubscriber(in int subId, in List<PhysicalChannelConfig> configs); - void notifyDataEnabled(boolean enabled, int reason); + void notifyDataEnabled(in int phoneId, int subId, boolean enabled, int reason); void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in Map allowedNetworkTypeList); } diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index ef2275d4218c..ab0149fce0a0 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -30,6 +30,7 @@ import android.view.IWindowSession; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.PointerIcon; +import android.view.ScrollCaptureResponse; import android.view.WindowInsets.Type.InsetsType; import android.window.ClientWindowFrames; @@ -160,7 +161,9 @@ public class BaseIWindow extends IWindow.Stub { @Override public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { try { - callbacks.onUnavailable(); + callbacks.onScrollCaptureResponse( + new ScrollCaptureResponse.Builder().setDescription("Not Implemented").build()); + } catch (RemoteException ex) { // ignore } diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java index 85fa791b429c..a41511b74a7d 100644 --- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java +++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java @@ -16,15 +16,19 @@ package com.android.internal.view; +import android.annotation.UiThread; +import android.content.Context; +import android.content.pm.ActivityInfo; import android.graphics.HardwareRenderer; import android.graphics.Matrix; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.RenderNode; -import android.os.Handler; +import android.os.CancellationSignal; import android.util.DisplayMetrics; import android.util.Log; +import android.view.Display.ColorMode; import android.view.ScrollCaptureCallback; import android.view.ScrollCaptureSession; import android.view.Surface; @@ -44,32 +48,42 @@ import java.util.function.Consumer; * @param <V> the specific View subclass handled * @see ScrollCaptureViewHelper */ +@UiThread public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCallback { - public static final long NO_FRAME_PRODUCED = -1; - private static final String TAG = "ScrollCaptureViewSupport"; private static final boolean WAIT_FOR_ANIMATION = true; private final WeakReference<V> mWeakView; private final ScrollCaptureViewHelper<V> mViewHelper; - private ViewRenderer mRenderer; - private Handler mUiHandler; + private final ViewRenderer mRenderer; private boolean mStarted; private boolean mEnded; ScrollCaptureViewSupport(V containingView, ScrollCaptureViewHelper<V> viewHelper) { mWeakView = new WeakReference<>(containingView); mRenderer = new ViewRenderer(); - mUiHandler = containingView.getHandler(); + // TODO(b/177649144): provide access to color space from android.media.Image mViewHelper = viewHelper; } - // Base implementation of ScrollCaptureCallback + /** Based on ViewRootImpl#updateColorModeIfNeeded */ + @ColorMode + private static int getColorMode(View containingView) { + Context context = containingView.getContext(); + int colorMode = containingView.getViewRootImpl().mWindowAttributes.getColorMode(); + if (!context.getResources().getConfiguration().isScreenWideColorGamut()) { + colorMode = ActivityInfo.COLOR_MODE_DEFAULT; + } + return colorMode; + } @Override - public final void onScrollCaptureSearch(Consumer<Rect> onReady) { + public final void onScrollCaptureSearch(CancellationSignal signal, Consumer<Rect> onReady) { + if (signal.isCanceled()) { + return; + } V view = mWeakView.get(); mStarted = false; mEnded = false; @@ -82,7 +96,11 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa } @Override - public final void onScrollCaptureStart(ScrollCaptureSession session, Runnable onReady) { + public final void onScrollCaptureStart(ScrollCaptureSession session, CancellationSignal signal, + Runnable onReady) { + if (signal.isCanceled()) { + return; + } V view = mWeakView.get(); mEnded = false; @@ -99,18 +117,22 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa } @Override - public final void onScrollCaptureImageRequest(ScrollCaptureSession session, Rect requestRect) { + public final void onScrollCaptureImageRequest(ScrollCaptureSession session, + CancellationSignal signal, Rect requestRect, Consumer<Rect> onComplete) { + if (signal.isCanceled()) { + return; + } V view = mWeakView.get(); if (view == null || !view.isVisibleToUser()) { // Signal to the controller that we have a problem and can't continue. - session.notifyBufferSent(NO_FRAME_PRODUCED, new Rect()); + onComplete.accept(new Rect()); return; } // Ask the view to scroll as needed to bring this area into view. ScrollResult scrollResult = mViewHelper.onScrollRequested(view, session.getScrollBounds(), requestRect); if (scrollResult.availableArea.isEmpty()) { - session.notifyBufferSent(NO_FRAME_PRODUCED, scrollResult.availableArea); + onComplete.accept(scrollResult.availableArea); return; } view.invalidate(); // don't wait for vsync @@ -121,17 +143,13 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa viewCaptureArea.offset(0, -scrollResult.scrollDelta); if (WAIT_FOR_ANIMATION) { - Log.d(TAG, "render: delaying until animation"); view.postOnAnimation(() -> { - Log.d(TAG, "postOnAnimation(): rendering now"); - long resultFrame = mRenderer.renderView(view, viewCaptureArea); - Log.d(TAG, "notifyBufferSent: " + scrollResult.availableArea); - - session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea)); + mRenderer.renderView(view, viewCaptureArea); + onComplete.accept(new Rect(scrollResult.availableArea)); }); } else { - long resultFrame = mRenderer.renderView(view, viewCaptureArea); - session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea)); + mRenderer.renderView(view, viewCaptureArea); + onComplete.accept(new Rect(scrollResult.availableArea)); } } @@ -239,7 +257,7 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa mCaptureRenderNode.endRecording(); } - public long renderView(View view, Rect sourceRect) { + public void renderView(View view, Rect sourceRect) { if (updateForView(view)) { setupLighting(view); } @@ -258,7 +276,7 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa switch (request.syncAndDraw()) { case HardwareRenderer.SYNC_OK: case HardwareRenderer.SYNC_REDRAW_REQUESTED: - return frameNumber; + return; case HardwareRenderer.SYNC_FRAME_DROPPED: Log.e(TAG, "syncAndDraw(): SYNC_FRAME_DROPPED !"); @@ -270,7 +288,6 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa Log.e(TAG, "syncAndDraw(): SYNC_CONTEXT_IS_STOPPED !"); break; } - return NO_FRAME_PRODUCED; } public void trimMemory() { @@ -289,5 +306,17 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa mTempMatrix.mapRect(mTempRectF); mTempRectF.round(outRect); } + + public void setColorMode(@ColorMode int colorMode) { + mRenderer.setColorMode(colorMode); + } + } + + @Override + public String toString() { + return "ScrollCaptureViewSupport{" + + "view=" + mWeakView.get() + + ", helper=" + mViewHelper + + '}'; } } diff --git a/core/java/com/android/internal/widget/NotificationVanishingFrameLayout.java b/core/java/com/android/internal/widget/NotificationVanishingFrameLayout.java new file mode 100644 index 000000000000..742bdfdde756 --- /dev/null +++ b/core/java/com/android/internal/widget/NotificationVanishingFrameLayout.java @@ -0,0 +1,70 @@ +/* + * 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.internal.widget; + +import android.annotation.Nullable; +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.RemoteViews; + +/** + * This view will measure itself as having 0 size if all of its children are {@link #GONE}. + * Otherwise it acts like a normal {@link FrameLayout}. + */ +@RemoteViews.RemoteView +public class NotificationVanishingFrameLayout extends FrameLayout { + public NotificationVanishingFrameLayout(Context context) { + this(context, null, 0, 0); + } + + public NotificationVanishingFrameLayout(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0, 0); + } + + public NotificationVanishingFrameLayout(Context context, @Nullable AttributeSet attrs, + int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public NotificationVanishingFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (allChildrenGone()) { + int zeroSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY); + super.onMeasure(zeroSpec, zeroSpec); + } else { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + + private boolean allChildrenGone() { + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + if (child != null && child.getVisibility() != GONE) { + return false; + } + } + return true; + } +} diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index a2de0aff5dfa..fd6038fd655d 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -399,7 +399,10 @@ public class PointerLocationView extends View implements InputDeviceListener, if (mCurDown && ps.mCurDown) { // Draw crosshairs. canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint); - canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint); + // Extend crosshairs to cover screen regardless of rotation (ie. since the rotated + // canvas can "expose" content past 0 and up-to the largest screen dimension). + canvas.drawLine(ps.mCoords.x, -getHeight(), ps.mCoords.x, + Math.max(getHeight(), getWidth()), mTargetPaint); // Draw current point. int pressureLevel = (int)(ps.mCoords.pressure * 255); diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index 95999a716707..fe3042d9da54 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -74,6 +74,7 @@ public class BootReceiver extends BroadcastReceiver { private static final int GMSCORE_LASTK_LOG_SIZE = 196608; private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE"; + private static final String TAG_TOMBSTONE_PROTO = "SYSTEM_TOMBSTONE_PROTO"; // The pre-froyo package and class of the system updater, which // ran in the system process. We need to remove its packages here @@ -251,14 +252,14 @@ public class BootReceiver extends BroadcastReceiver { * @param ctx Context * @param tombstone path to the tombstone */ - public static void addTombstoneToDropBox(Context ctx, File tombstone) { + public static void addTombstoneToDropBox(Context ctx, File tombstone, boolean proto) { final DropBoxManager db = ctx.getSystemService(DropBoxManager.class); final String bootReason = SystemProperties.get("ro.boot.bootreason", null); HashMap<String, Long> timestamps = readTimestamps(); try { final String headers = getBootHeadersToLogAndUpdate(); addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE, - TAG_TOMBSTONE); + proto ? TAG_TOMBSTONE_PROTO : TAG_TOMBSTONE); } catch (IOException e) { Slog.e(TAG, "Can't log tombstone", e); } diff --git a/core/java/com/android/server/OWNERS b/core/java/com/android/server/OWNERS new file mode 100644 index 000000000000..1262925447b9 --- /dev/null +++ b/core/java/com/android/server/OWNERS @@ -0,0 +1 @@ +per-file SystemConfig.java = toddke@google.com diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 8982519eff96..b40ffb0136f2 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -24,6 +24,7 @@ import android.app.ActivityManager; import android.content.ComponentName; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; +import android.hardware.SensorPrivacyManager; import android.os.Build; import android.os.CarrierAssociatedAppEntry; import android.os.Environment; @@ -1234,10 +1235,10 @@ public class SystemConfig { addFeature(PackageManager.FEATURE_RAM_NORMAL, 0); } - if (IncrementalManager.isFeatureEnabled()) { + final int incrementalVersion = IncrementalManager.getVersion(); + if (incrementalVersion > 0) { addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY, 0); - addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY_VERSION, - IncrementalManager.isV2Available() ? 2 : 1); + addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY_VERSION, incrementalVersion); } if (PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT) { @@ -1252,6 +1253,14 @@ public class SystemConfig { addFeature(PackageManager.FEATURE_CROSS_LAYER_BLUR, 0); } + if (SensorPrivacyManager.USE_MICROPHONE_TOGGLE) { + addFeature(PackageManager.FEATURE_MICROPHONE_TOGGLE, 0); + } + + if (SensorPrivacyManager.USE_CAMERA_TOGGLE) { + addFeature(PackageManager.FEATURE_CAMERA_TOGGLE, 0); + } + for (String featureName : mUnavailableFeatures) { removeFeature(featureName); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index d72a0ec8641f..2287900795a5 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -1,3 +1,21 @@ + +package { + default_applicable_licenses: ["frameworks_base_core_jni_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_core_jni_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_library_shared { name: "libandroid_runtime", host_supported: true, @@ -103,6 +121,7 @@ cc_library_shared { "android_view_PointerIcon.cpp", "android_view_Surface.cpp", "android_view_SurfaceControl.cpp", + "android_view_SurfaceControlFpsListener.cpp", "android_graphics_BLASTBufferQueue.cpp", "android_view_SurfaceSession.cpp", "android_view_TextureView.cpp", @@ -157,6 +176,7 @@ cc_library_shared { "android_hardware_Camera.cpp", "android_hardware_camera2_CameraMetadata.cpp", "android_hardware_camera2_DngCreator.cpp", + "android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp", "android_hardware_camera2_utils_SurfaceUtils.cpp", "android_hardware_display_DisplayManagerGlobal.cpp", "android_hardware_display_DisplayViewport.cpp", @@ -191,6 +211,7 @@ cc_library_shared { "com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp", "com_android_internal_os_KernelSingleUidTimeReader.cpp", "com_android_internal_os_Zygote.cpp", + "com_android_internal_os_ZygoteCommandBuffer.cpp", "com_android_internal_os_ZygoteInit.cpp", "hwbinder/EphemeralStorage.cpp", "fd_utils.cpp", @@ -209,10 +230,11 @@ cc_library_shared { ], shared_libs: [ - "android.hardware.memtrack-unstable-ndk_platform", + "android.hardware.memtrack-V1-ndk_platform", "audioclient-types-aidl-cpp", "audioflinger-aidl-cpp", "av-types-aidl-cpp", + "android.hardware.camera.device@3.2", "libandroidicu", "libbpf_android", "libnetdbpf", @@ -242,6 +264,7 @@ cc_library_shared { "libdataloader", "libvulkan", "libETC1", + "libjpeg", "libhardware", "libhardware_legacy", "libselinux", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 38bcc0f4c59e..ddd861380fab 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -74,6 +74,7 @@ extern int register_android_opengl_jni_GLES32(JNIEnv* env); extern int register_android_hardware_Camera(JNIEnv *env); extern int register_android_hardware_camera2_CameraMetadata(JNIEnv *env); extern int register_android_hardware_camera2_DngCreator(JNIEnv *env); +extern int register_android_hardware_camera2_impl_CameraExtensionJpegProcessor(JNIEnv* env); extern int register_android_hardware_camera2_utils_SurfaceUtils(JNIEnv* env); extern int register_android_hardware_display_DisplayManagerGlobal(JNIEnv* env); extern int register_android_hardware_HardwareBuffer(JNIEnv *env); @@ -119,6 +120,7 @@ extern int register_android_view_InputApplicationHandle(JNIEnv* env); extern int register_android_view_InputWindowHandle(JNIEnv* env); extern int register_android_view_Surface(JNIEnv* env); extern int register_android_view_SurfaceControl(JNIEnv* env); +extern int register_android_view_SurfaceControlFpsListener(JNIEnv* env); extern int register_android_view_SurfaceSession(JNIEnv* env); extern int register_android_view_CompositionSamplingListener(JNIEnv* env); extern int register_android_view_TextureView(JNIEnv* env); @@ -196,6 +198,7 @@ extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env extern int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv* env); extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env); extern int register_com_android_internal_os_Zygote(JNIEnv *env); +extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env); extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env); extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env); @@ -1487,6 +1490,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_InputWindowHandle), REG_JNI(register_android_view_Surface), REG_JNI(register_android_view_SurfaceControl), + REG_JNI(register_android_view_SurfaceControlFpsListener), REG_JNI(register_android_view_SurfaceSession), REG_JNI(register_android_view_CompositionSamplingListener), REG_JNI(register_android_view_TextureView), @@ -1528,11 +1532,13 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_net_NetworkUtilsInternal), REG_JNI(register_com_android_internal_os_ClassLoaderFactory), REG_JNI(register_com_android_internal_os_Zygote), + REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer), REG_JNI(register_com_android_internal_os_ZygoteInit), REG_JNI(register_com_android_internal_util_VirtualRefBasePtr), REG_JNI(register_android_hardware_Camera), REG_JNI(register_android_hardware_camera2_CameraMetadata), REG_JNI(register_android_hardware_camera2_DngCreator), + REG_JNI(register_android_hardware_camera2_impl_CameraExtensionJpegProcessor), REG_JNI(register_android_hardware_camera2_utils_SurfaceUtils), REG_JNI(register_android_hardware_display_DisplayManagerGlobal), REG_JNI(register_android_hardware_HardwareBuffer), diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index 5b327d40ac69..d0504fb481ca 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -421,11 +421,18 @@ private: }; static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jlong sensorManager, - jobject eventQWeak, jobject msgQ, jstring packageName, jint mode) { + jobject eventQWeak, jobject msgQ, jstring packageName, + jint mode, jstring opPackageName, jstring attributionTag) { SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager); ScopedUtfChars packageUtf(env, packageName); String8 clientName(packageUtf.c_str()); - sp<SensorEventQueue> queue(mgr->createEventQueue(clientName, mode)); + + String16 attributionTagName(""); + if (attributionTag != nullptr) { + ScopedUtfChars attrUtf(env, attributionTag); + attributionTagName = String16(attrUtf.c_str()); + } + sp<SensorEventQueue> queue(mgr->createEventQueue(clientName, mode, attributionTagName)); if (queue == NULL) { jniThrowRuntimeException(env, "Cannot construct native SensorEventQueue."); @@ -517,29 +524,20 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = { }; static const JNINativeMethod gBaseEventQueueMethods[] = { - {"nativeInitBaseEventQueue", - "(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;Ljava/lang/String;ILjava/lang/String;)J", - (void*)nativeInitSensorEventQueue }, + {"nativeInitBaseEventQueue", + "(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;Ljava/lang/String;ILjava/lang/" + "String;Ljava/lang/String;)J", + (void *)nativeInitSensorEventQueue}, - {"nativeEnableSensor", - "(JIII)I", - (void*)nativeEnableSensor }, + {"nativeEnableSensor", "(JIII)I", (void *)nativeEnableSensor}, - {"nativeDisableSensor", - "(JI)I", - (void*)nativeDisableSensor }, + {"nativeDisableSensor", "(JI)I", (void *)nativeDisableSensor}, - {"nativeDestroySensorEventQueue", - "(J)V", - (void*)nativeDestroySensorEventQueue }, + {"nativeDestroySensorEventQueue", "(J)V", (void *)nativeDestroySensorEventQueue}, - {"nativeFlushSensor", - "(J)I", - (void*)nativeFlushSensor }, + {"nativeFlushSensor", "(J)I", (void *)nativeFlushSensor}, - {"nativeInjectSensorData", - "(JI[FIJ)I", - (void*)nativeInjectSensorData }, + {"nativeInjectSensorData", "(JI[FIJ)I", (void *)nativeInjectSensorData}, }; } //unnamed namespace diff --git a/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp b/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp new file mode 100644 index 000000000000..139075907bf3 --- /dev/null +++ b/core/jni/android_hardware_camera2_impl_CameraExtensionJpegProcessor.cpp @@ -0,0 +1,629 @@ +/* + * 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. + */ + +#include <array> +#include <cstring> +#include <cstdio> +#include <inttypes.h> +#include <memory.h> +#include <vector> + +#include <setjmp.h> + +#include <android/hardware/camera/device/3.2/types.h> + +#include "core_jni_helpers.h" +#include "jni.h" +#include <nativehelper/JNIHelp.h> + +#define CAMERA_PROCESSOR_CLASS_NAME "android/hardware/camera2/impl/CameraExtensionJpegProcessor" + +extern "C" { +#include "jpeglib.h" +} + +using namespace std; +using namespace android; + +using android::hardware::camera::device::V3_2::CameraBlob; +using android::hardware::camera::device::V3_2::CameraBlobId; + +class Transform; +struct Plane; + +inline int sgn(int val) { return (0 < val) - (val < 0); } + +inline int min(int a, int b) { return a < b ? a : b; } + +inline int max(int a, int b) { return a > b ? a : b; } + +/** + * Represents a combined cropping and rotation transformation. + * + * The transformation maps the coordinates (mOrigX, mOrigY) and (mOneX, mOneY) + * in the input image to the origin and (mOutputWidth, mOutputHeight) + * respectively. + */ +class Transform { + public: + Transform(int origX, int origY, int oneX, int oneY); + + static Transform forCropFollowedByRotation(int cropLeft, int cropTop, + int cropRight, int cropBottom, int rot90); + + inline int getOutputWidth() const { return mOutputWidth; } + + inline int getOutputHeight() const { return mOutputHeight; } + + bool operator==(const Transform& other) const; + + /** + * Transforms the input coordinates. Coordinates outside the cropped region + * are clamped to valid values. + */ + void map(int x, int y, int* outX, int* outY) const; + + private: + int mOutputWidth; + int mOutputHeight; + + // The coordinates of the point to map the origin to. + const int mOrigX, mOrigY; + // The coordinates of the point to map the point (getOutputWidth(), + // getOutputHeight()) to. + const int mOneX, mOneY; + + // A matrix for the rotational component. + int mMat00, mMat01; + int mMat10, mMat11; +}; + +/** + * Represents a model for accessing pixel data for a single plane of an image. + * Note that the actual data is not owned by this class, and the underlying + * data does not need to be stored in separate planes. + */ +struct Plane { + // The dimensions of this plane of the image + int width; + int height; + + // A pointer to raw pixel data + const unsigned char* data; + // The difference in address between consecutive pixels in the same row + int pixelStride; + // The difference in address between the start of consecutive rows + int rowStride; +}; + +/** + * Provides an interface for simultaneously reading a certain number of rows of + * an image plane as contiguous arrays, suitable for use with libjpeg. + */ +template <unsigned int ROWS> +class RowIterator { + public: + /** + * Creates a new RowIterator which will crop and rotate with the given + * transform. + * + * @param plane the plane to iterate over + * @param transform the transformation to map output values into the + * coordinate space of the plane + * @param rowLength the length of the rows returned via LoadAt(). If this is + * longer than the width of the output (after applying the transform), then + * the right-most value is repeated. + */ + inline RowIterator(Plane plane, Transform transform, int rowLength); + + /** + * Returns an array of pointers into consecutive rows of contiguous image + * data starting at y. That is, samples within each row are contiguous. + * However, the individual arrays pointed-to may be separate. + * When the end of the image is reached, the last row of the image is + * repeated. + * The returned pointers are valid until the next call to loadAt(). + */ + inline const std::array<unsigned char*, ROWS> loadAt(int baseY); + + private: + Plane mPlane; + Transform mTransform; + // The length of a row, with padding to the next multiple of 64. + int mPaddedRowLength; + std::vector<unsigned char> mBuffer; +}; + +template <unsigned int ROWS> +RowIterator<ROWS>::RowIterator(Plane plane, Transform transform, + int rowLength) + : mPlane(plane), mTransform(transform) { + mPaddedRowLength = rowLength; + mBuffer = std::vector<unsigned char>(rowLength * ROWS); +} + +template <unsigned int ROWS> +const std::array<unsigned char*, ROWS> RowIterator<ROWS>::loadAt(int baseY) { + std::array<unsigned char*, ROWS> bufPtrs; + for (unsigned int i = 0; i < ROWS; i++) { + bufPtrs[i] = &mBuffer[mPaddedRowLength * i]; + } + + if (mPlane.width == 0 || mPlane.height == 0) { + return bufPtrs; + } + + for (unsigned int i = 0; i < ROWS; i++) { + int y = i + baseY; + y = min(y, mTransform.getOutputHeight() - 1); + + int output_width = mPaddedRowLength; + output_width = min(output_width, mTransform.getOutputWidth()); + output_width = min(output_width, mPlane.width); + + // Each row in the output image will be copied into buf_ by gathering pixels + // along an axis-aligned line in the plane. + // The line is defined by (startX, startY) -> (endX, endY), computed via the + // current Transform. + int startX; + int startY; + mTransform.map(0, y, &startX, &startY); + + int endX; + int endY; + mTransform.map(output_width - 1, y, &endX, &endY); + + // Clamp (startX, startY) and (endX, endY) to the valid bounds of the plane. + startX = min(startX, mPlane.width - 1); + startY = min(startY, mPlane.height - 1); + endX = min(endX, mPlane.width - 1); + endY = min(endY, mPlane.height - 1); + startX = max(startX, 0); + startY = max(startY, 0); + endX = max(endX, 0); + endY = max(endY, 0); + + // To reduce work inside the copy-loop, precompute the start, end, and + // stride relating the values to be gathered from mPlane into buf + // for this particular scan-line. + int dx = sgn(endX - startX); + int dy = sgn(endY - startY); + if (!(dx == 0 || dy == 0)) { + ALOGE("%s: Unexpected bounds: %dx%d %dx%d!", __FUNCTION__, startX, endX, startY, endY); + return bufPtrs; + } + + // The index into mPlane.data of (startX, startY) + int plane_start = startX * mPlane.pixelStride + startY * mPlane.rowStride; + // The index into mPlane.data of (endX, endY) + int plane_end = endX * mPlane.pixelStride + endY * mPlane.rowStride; + // The stride, in terms of indices in plane_data, required to enumerate the + // samples between the start and end points. + int stride = dx * mPlane.pixelStride + dy * mPlane.rowStride; + // In the degenerate-case of a 1x1 plane, startX and endX are equal, so + // stride would be 0, resulting in an infinite-loop. To avoid this case, + // use a stride of at-least 1. + if (stride == 0) { + stride = 1; + } + + int outX = 0; + for (int idx = plane_start; idx >= min(plane_start, plane_end) && + idx <= max(plane_start, plane_end); idx += stride) { + bufPtrs[i][outX] = mPlane.data[idx]; + outX++; + } + + // Fill the remaining right-edge of the buffer by extending the last + // value. + unsigned char right_padding_value = bufPtrs[i][outX - 1]; + for (; outX < mPaddedRowLength; outX++) { + bufPtrs[i][outX] = right_padding_value; + } + } + + return bufPtrs; +} + +template <typename T> +void safeDelete(T& t) { + delete t; + t = nullptr; +} + +template <typename T> +void safeDeleteArray(T& t) { + delete[] t; + t = nullptr; +} + +Transform::Transform(int origX, int origY, int oneX, int oneY) + : mOrigX(origX), mOrigY(origY), mOneX(oneX), mOneY(oneY) { + if (origX == oneX || origY == oneY) { + // Handle the degenerate case of cropping to a 0x0 rectangle. + mMat00 = 0; + mMat01 = 0; + mMat10 = 0; + mMat11 = 0; + return; + } + + if (oneX > origX && oneY > origY) { + // 0-degree rotation + mMat00 = 1; + mMat01 = 0; + mMat10 = 0; + mMat11 = 1; + mOutputWidth = abs(oneX - origX); + mOutputHeight = abs(oneY - origY); + } else if (oneX < origX && oneY > origY) { + // 90-degree CCW rotation + mMat00 = 0; + mMat01 = -1; + mMat10 = 1; + mMat11 = 0; + mOutputWidth = abs(oneY - origY); + mOutputHeight = abs(oneX - origX); + } else if (oneX > origX && oneY < origY) { + // 270-degree CCW rotation + mMat00 = 0; + mMat01 = 1; + mMat10 = -1; + mMat11 = 0; + mOutputWidth = abs(oneY - origY); + mOutputHeight = abs(oneX - origX);; + } else if (oneX < origX && oneY < origY) { + // 180-degree CCW rotation + mMat00 = -1; + mMat01 = 0; + mMat10 = 0; + mMat11 = -1; + mOutputWidth = abs(oneX - origX); + mOutputHeight = abs(oneY - origY); + } +} + +Transform Transform::forCropFollowedByRotation(int cropLeft, int cropTop, int cropRight, + int cropBottom, int rot90) { + // The input crop-region excludes cropRight and cropBottom, so transform the + // crop rect such that it defines the entire valid region of pixels + // inclusively. + cropRight -= 1; + cropBottom -= 1; + + int cropXLow = min(cropLeft, cropRight); + int cropYLow = min(cropTop, cropBottom); + int cropXHigh = max(cropLeft, cropRight); + int cropYHigh = max(cropTop, cropBottom); + rot90 %= 4; + if (rot90 == 0) { + return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1); + } else if (rot90 == 1) { + return Transform(cropXHigh, cropYLow, cropXLow - 1, cropYHigh + 1); + } else if (rot90 == 2) { + return Transform(cropXHigh, cropYHigh, cropXLow - 1, cropYLow - 1); + } else if (rot90 == 3) { + return Transform(cropXLow, cropYHigh, cropXHigh + 1, cropYLow - 1); + } + // Impossible case. + return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1); +} + +bool Transform::operator==(const Transform& other) const { + return other.mOrigX == mOrigX && // + other.mOrigY == mOrigY && // + other.mOneX == mOneX && // + other.mOneY == mOneY; +} + +/** + * Transforms the input coordinates. Coordinates outside the cropped region + * are clamped to valid values. + */ +void Transform::map(int x, int y, int* outX, int* outY) const { + x = max(x, 0); + y = max(y, 0); + x = min(x, getOutputWidth() - 1); + y = min(y, getOutputHeight() - 1); + *outX = x * mMat00 + y * mMat01 + mOrigX; + *outY = x * mMat10 + y * mMat11 + mOrigY; +} + +int compress(int img_width, int img_height, RowIterator<16>& y_row_generator, + RowIterator<8>& cb_row_generator, RowIterator<8>& cr_row_generator, + unsigned char* out_buf, size_t out_buf_capacity, std::function<void(size_t)> flush, + int quality) { + // libjpeg requires the use of setjmp/longjmp to recover from errors. Since + // this doesn't play well with RAII, we must use pointers and manually call + // delete. See POSIX documentation for longjmp() for details on why the + // volatile keyword is necessary. + volatile jpeg_compress_struct cinfov; + + jpeg_compress_struct& cinfo = + *const_cast<struct jpeg_compress_struct*>(&cinfov); + + JSAMPROW* volatile yArr = nullptr; + JSAMPROW* volatile cbArr = nullptr; + JSAMPROW* volatile crArr = nullptr; + + JSAMPARRAY imgArr[3]; + + // Error handling + + struct my_error_mgr { + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; + } err; + + cinfo.err = jpeg_std_error(&err.pub); + + // Default error_exit will call exit(), so override + // to return control via setjmp/longjmp. + err.pub.error_exit = [](j_common_ptr cinfo) { + my_error_mgr* myerr = reinterpret_cast<my_error_mgr*>(cinfo->err); + + (*cinfo->err->output_message)(cinfo); + + // Return control to the setjmp point (see call to setjmp()). + longjmp(myerr->setjmp_buffer, 1); + }; + + cinfo.err = (struct jpeg_error_mgr*)&err; + + // Set the setjmp point to return to in case of error. + if (setjmp(err.setjmp_buffer)) { + // If libjpeg hits an error, control will jump to this point (see call to + // longjmp()). + jpeg_destroy_compress(&cinfo); + + safeDeleteArray(yArr); + safeDeleteArray(cbArr); + safeDeleteArray(crArr); + + return -1; + } + + // Create jpeg compression context + jpeg_create_compress(&cinfo); + + // Stores data needed by our c-style callbacks into libjpeg + struct ClientData { + unsigned char* out_buf; + size_t out_buf_capacity; + std::function<void(size_t)> flush; + int totalOutputBytes; + } clientData{out_buf, out_buf_capacity, flush, 0}; + + cinfo.client_data = &clientData; + + // Initialize destination manager + jpeg_destination_mgr dest; + + dest.init_destination = [](j_compress_ptr cinfo) { + ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data); + + cinfo->dest->next_output_byte = cdata.out_buf; + cinfo->dest->free_in_buffer = cdata.out_buf_capacity; + }; + + dest.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean { + ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data); + + size_t numBytesInBuffer = cdata.out_buf_capacity; + cdata.flush(numBytesInBuffer); + cdata.totalOutputBytes += numBytesInBuffer; + + // Reset the buffer + cinfo->dest->next_output_byte = cdata.out_buf; + cinfo->dest->free_in_buffer = cdata.out_buf_capacity; + + return true; + }; + + dest.term_destination = [](j_compress_ptr cinfo __unused) { + // do nothing to terminate the output buffer + }; + + cinfo.dest = &dest; + + // Set jpeg parameters + cinfo.image_width = img_width; + cinfo.image_height = img_height; + cinfo.input_components = 3; + + // Set defaults based on the above values + jpeg_set_defaults(&cinfo); + + jpeg_set_quality(&cinfo, quality, true); + + cinfo.dct_method = JDCT_IFAST; + + cinfo.raw_data_in = true; + + jpeg_set_colorspace(&cinfo, JCS_YCbCr); + + cinfo.comp_info[0].h_samp_factor = 2; + cinfo.comp_info[0].v_samp_factor = 2; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + + jpeg_start_compress(&cinfo, true); + + yArr = new JSAMPROW[cinfo.comp_info[0].v_samp_factor * DCTSIZE]; + cbArr = new JSAMPROW[cinfo.comp_info[1].v_samp_factor * DCTSIZE]; + crArr = new JSAMPROW[cinfo.comp_info[2].v_samp_factor * DCTSIZE]; + + imgArr[0] = const_cast<JSAMPARRAY>(yArr); + imgArr[1] = const_cast<JSAMPARRAY>(cbArr); + imgArr[2] = const_cast<JSAMPARRAY>(crArr); + + for (int y = 0; y < img_height; y += DCTSIZE * 2) { + std::array<unsigned char*, 16> yData = y_row_generator.loadAt(y); + std::array<unsigned char*, 8> cbData = cb_row_generator.loadAt(y / 2); + std::array<unsigned char*, 8> crData = cr_row_generator.loadAt(y / 2); + + for (int row = 0; row < DCTSIZE * 2; row++) { + yArr[row] = yData[row]; + } + for (int row = 0; row < DCTSIZE; row++) { + cbArr[row] = cbData[row]; + crArr[row] = crData[row]; + } + + jpeg_write_raw_data(&cinfo, imgArr, DCTSIZE * 2); + } + + jpeg_finish_compress(&cinfo); + + int numBytesInBuffer = cinfo.dest->next_output_byte - out_buf; + + flush(numBytesInBuffer); + + clientData.totalOutputBytes += numBytesInBuffer; + + safeDeleteArray(yArr); + safeDeleteArray(cbArr); + safeDeleteArray(crArr); + + jpeg_destroy_compress(&cinfo); + + return clientData.totalOutputBytes; +} + +int compress( + /** Input image dimensions */ + int width, int height, + /** Y Plane */ + unsigned char* yBuf, int yPStride, int yRStride, + /** Cb Plane */ + unsigned char* cbBuf, int cbPStride, int cbRStride, + /** Cr Plane */ + unsigned char* crBuf, int crPStride, int crRStride, + /** Output */ + unsigned char* outBuf, size_t outBufCapacity, + /** Jpeg compression parameters */ + int quality, + /** Crop */ + int cropLeft, int cropTop, int cropRight, int cropBottom, + /** Rotation (multiple of 90). For example, rot90 = 1 implies a 90 degree + * rotation. */ + int rot90) { + int finalWidth; + int finalHeight; + finalWidth = cropRight - cropLeft; + finalHeight = cropBottom - cropTop; + + rot90 %= 4; + // for 90 and 270-degree rotations, flip the final width and height + if (rot90 == 1) { + finalWidth = cropBottom - cropTop; + finalHeight = cropRight - cropLeft; + } else if (rot90 == 3) { + finalWidth = cropBottom - cropTop; + finalHeight = cropRight - cropLeft; + } + + const Plane yP = {width, height, yBuf, yPStride, yRStride}; + const Plane cbP = {width / 2, height / 2, cbBuf, cbPStride, cbRStride}; + const Plane crP = {width / 2, height / 2, crBuf, crPStride, crRStride}; + + auto flush = [](size_t numBytes __unused) { + // do nothing + }; + + // Round up to the nearest multiple of 64. + int y_row_length = (finalWidth + 16 + 63) & ~63; + int cb_row_length = (finalWidth / 2 + 16 + 63) & ~63; + int cr_row_length = (finalWidth / 2 + 16 + 63) & ~63; + + Transform yTrans = Transform::forCropFollowedByRotation( + cropLeft, cropTop, cropRight, cropBottom, rot90); + + Transform chromaTrans = Transform::forCropFollowedByRotation( + cropLeft / 2, cropTop / 2, cropRight / 2, cropBottom / 2, rot90); + + RowIterator<16> yIter(yP, yTrans, y_row_length); + RowIterator<8> cbIter(cbP, chromaTrans, cb_row_length); + RowIterator<8> crIter(crP, chromaTrans, cr_row_length); + + return compress(finalWidth, finalHeight, yIter, cbIter, crIter, outBuf, outBufCapacity, flush, + quality); +} + +extern "C" { + +static jint CameraExtensionJpegProcessor_compressJpegFromYUV420p( + JNIEnv* env, jclass clazz __unused, + /** Input image dimensions */ + jint width, jint height, + /** Y Plane */ + jobject yBuf, jint yPStride, jint yRStride, + /** Cb Plane */ + jobject cbBuf, jint cbPStride, jint cbRStride, + /** Cr Plane */ + jobject crBuf, jint crPStride, jint crRStride, + /** Output */ + jobject outBuf, jint outBufCapacity, + /** Jpeg compression parameters */ + jint quality, + /** Crop */ + jint cropLeft, jint cropTop, jint cropRight, jint cropBottom, + /** Rotation (multiple of 90). For example, rot90 = 1 implies a 90 degree + * rotation. */ + jint rot90) { + jbyte* y = (jbyte*)env->GetDirectBufferAddress(yBuf); + jbyte* cb = (jbyte*)env->GetDirectBufferAddress(cbBuf); + jbyte* cr = (jbyte*)env->GetDirectBufferAddress(crBuf); + jbyte* out = (jbyte*)env->GetDirectBufferAddress(outBuf); + + size_t actualJpegSize = compress(width, height, + (unsigned char*)y, yPStride, yRStride, + (unsigned char*)cb, cbPStride, cbRStride, + (unsigned char*)cr, crPStride, crRStride, + (unsigned char*)out, (size_t)outBufCapacity, + quality, cropLeft, cropTop, cropRight, cropBottom, rot90); + + size_t finalJpegSize = actualJpegSize + sizeof(CameraBlob); + if (finalJpegSize > outBufCapacity) { + ALOGE("%s: Final jpeg buffer %zu not large enough for the jpeg blob header with "\ + "capacity %d", __FUNCTION__, finalJpegSize, outBufCapacity); + return actualJpegSize; + } + + int8_t* header = static_cast<int8_t *> (out) + + (outBufCapacity - sizeof(CameraBlob)); + CameraBlob *blob = reinterpret_cast<CameraBlob *> (header); + blob->blobId = CameraBlobId::JPEG; + blob->blobSize = actualJpegSize; + + return actualJpegSize; +} + +} // extern "C" + +static const JNINativeMethod gCameraExtensionJpegProcessorMethods[] = { + {"compressJpegFromYUV420pNative", + "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIIIIII)I", + (void*)CameraExtensionJpegProcessor_compressJpegFromYUV420p}}; + +// Get all the required offsets in java class and register native functions +int register_android_hardware_camera2_impl_CameraExtensionJpegProcessor(JNIEnv* env) { + // Register native functions + return RegisterMethodsOrDie(env, CAMERA_PROCESSOR_CLASS_NAME, + gCameraExtensionJpegProcessorMethods, NELEM(gCameraExtensionJpegProcessorMethods)); +} diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index 8d193bfa1dd2..0e0f98ec1fc4 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -861,6 +861,23 @@ static int android_media_AudioRecord_set_preferred_microphone_field_dimension( return jStatus; } +static void android_media_AudioRecord_setLogSessionId(JNIEnv *env, jobject thiz, + jstring jlogSessionId) { + sp<AudioRecord> record = getAudioRecord(env, thiz); + if (record == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioRecord pointer for setLogSessionId()"); + } + if (jlogSessionId == nullptr) { + ALOGV("%s: logSessionId nullptr", __func__); + record->setLogSessionId(nullptr); + return; + } + ScopedUtfChars logSessionId(env, jlogSessionId); + ALOGV("%s: logSessionId '%s'", __func__, logSessionId.c_str()); + record->setLogSessionId(logSessionId.c_str()); +} + // ---------------------------------------------------------------------------- static jint android_media_AudioRecord_get_port_id(JNIEnv *env, jobject thiz) { sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); @@ -876,50 +893,48 @@ static jint android_media_AudioRecord_get_port_id(JNIEnv *env, jobject thiz) { // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { - // name, signature, funcPtr - {"native_start", "(II)I", (void *)android_media_AudioRecord_start}, - {"native_stop", "()V", (void *)android_media_AudioRecord_stop}, - {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I", - (void *)android_media_AudioRecord_setup}, - {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, - {"native_release", "()V", (void *)android_media_AudioRecord_release}, - {"native_read_in_byte_array", - "([BIIZ)I", - (void *)android_media_AudioRecord_readInArray<jbyteArray>}, - {"native_read_in_short_array", - "([SIIZ)I", - (void *)android_media_AudioRecord_readInArray<jshortArray>}, - {"native_read_in_float_array", - "([FIIZ)I", - (void *)android_media_AudioRecord_readInArray<jfloatArray>}, - {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I", - (void *)android_media_AudioRecord_readInDirectBuffer}, - {"native_get_buffer_size_in_frames", - "()I", (void *)android_media_AudioRecord_get_buffer_size_in_frames}, - {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos}, - {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos}, - {"native_set_pos_update_period", - "(I)I", (void *)android_media_AudioRecord_set_pos_update_period}, - {"native_get_pos_update_period", - "()I", (void *)android_media_AudioRecord_get_pos_update_period}, - {"native_get_min_buff_size", - "(III)I", (void *)android_media_AudioRecord_get_min_buff_size}, - {"native_getMetrics", "()Landroid/os/PersistableBundle;", - (void *)android_media_AudioRecord_native_getMetrics}, - {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice}, - {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId}, - {"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback}, - {"native_disableDeviceCallback", "()V", - (void *)android_media_AudioRecord_disableDeviceCallback}, - {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I", - (void *)android_media_AudioRecord_get_timestamp}, - {"native_get_active_microphones", "(Ljava/util/ArrayList;)I", - (void *)android_media_AudioRecord_get_active_microphones}, - {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id}, - {"native_set_preferred_microphone_direction", "(I)I", - (void *)android_media_AudioRecord_set_preferred_microphone_direction}, - {"native_set_preferred_microphone_field_dimension", "(F)I", - (void *)android_media_AudioRecord_set_preferred_microphone_field_dimension}, + {"native_start", "(II)I", (void *)android_media_AudioRecord_start}, + {"native_stop", "()V", (void *)android_media_AudioRecord_stop}, + {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I", + (void *)android_media_AudioRecord_setup}, + {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, + {"native_release", "()V", (void *)android_media_AudioRecord_release}, + {"native_read_in_byte_array", "([BIIZ)I", + (void *)android_media_AudioRecord_readInArray<jbyteArray>}, + {"native_read_in_short_array", "([SIIZ)I", + (void *)android_media_AudioRecord_readInArray<jshortArray>}, + {"native_read_in_float_array", "([FIIZ)I", + (void *)android_media_AudioRecord_readInArray<jfloatArray>}, + {"native_read_in_direct_buffer", "(Ljava/lang/Object;IZ)I", + (void *)android_media_AudioRecord_readInDirectBuffer}, + {"native_get_buffer_size_in_frames", "()I", + (void *)android_media_AudioRecord_get_buffer_size_in_frames}, + {"native_set_marker_pos", "(I)I", (void *)android_media_AudioRecord_set_marker_pos}, + {"native_get_marker_pos", "()I", (void *)android_media_AudioRecord_get_marker_pos}, + {"native_set_pos_update_period", "(I)I", + (void *)android_media_AudioRecord_set_pos_update_period}, + {"native_get_pos_update_period", "()I", + (void *)android_media_AudioRecord_get_pos_update_period}, + {"native_get_min_buff_size", "(III)I", (void *)android_media_AudioRecord_get_min_buff_size}, + {"native_getMetrics", "()Landroid/os/PersistableBundle;", + (void *)android_media_AudioRecord_native_getMetrics}, + {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice}, + {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId}, + {"native_enableDeviceCallback", "()V", + (void *)android_media_AudioRecord_enableDeviceCallback}, + {"native_disableDeviceCallback", "()V", + (void *)android_media_AudioRecord_disableDeviceCallback}, + {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I", + (void *)android_media_AudioRecord_get_timestamp}, + {"native_get_active_microphones", "(Ljava/util/ArrayList;)I", + (void *)android_media_AudioRecord_get_active_microphones}, + {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id}, + {"native_set_preferred_microphone_direction", "(I)I", + (void *)android_media_AudioRecord_set_preferred_microphone_direction}, + {"native_set_preferred_microphone_field_dimension", "(F)I", + (void *)android_media_AudioRecord_set_preferred_microphone_field_dimension}, + {"native_setLogSessionId", "(Ljava/lang/String;)V", + (void *)android_media_AudioRecord_setLogSessionId}, }; // field names found in android/media/AudioRecord.java diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 065c79b8601f..cae6db57e99c 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -1419,6 +1419,33 @@ static jint android_media_AudioTrack_getDualMonoMode(JNIEnv *env, jobject thiz, return nativeToJavaStatus(status); } +static void android_media_AudioTrack_setLogSessionId(JNIEnv *env, jobject thiz, + jstring jlogSessionId) { + sp<AudioTrack> track = getAudioTrack(env, thiz); + if (track == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for setLogSessionId()"); + } + if (jlogSessionId == nullptr) { + ALOGV("%s: logSessionId nullptr", __func__); + track->setLogSessionId(nullptr); + return; + } + ScopedUtfChars logSessionId(env, jlogSessionId); + ALOGV("%s: logSessionId '%s'", __func__, logSessionId.c_str()); + track->setLogSessionId(logSessionId.c_str()); +} + +static void android_media_AudioTrack_setPlayerIId(JNIEnv *env, jobject thiz, jint playerIId) { + sp<AudioTrack> track = getAudioTrack(env, thiz); + if (track == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", + "Unable to retrieve AudioTrack pointer for setPlayerIId()"); + } + ALOGV("%s: playerIId %d", __func__, playerIId); + track->setPlayerIId(playerIId); +} + // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { @@ -1496,6 +1523,9 @@ static const JNINativeMethod gMethods[] = { (void *)android_media_AudioTrack_getAudioDescriptionMixLeveldB}, {"native_set_dual_mono_mode", "(I)I", (void *)android_media_AudioTrack_setDualMonoMode}, {"native_get_dual_mono_mode", "([I)I", (void *)android_media_AudioTrack_getDualMonoMode}, + {"native_setLogSessionId", "(Ljava/lang/String;)V", + (void *)android_media_AudioTrack_setLogSessionId}, + {"native_setPlayerIId", "(I)V", (void *)android_media_AudioTrack_setPlayerIId}, }; // field names found in android/media/AudioTrack.java diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp index 0f7611a8ead1..f67007cda209 100644 --- a/core/jni/android_os_Trace.cpp +++ b/core/jni/android_os_Trace.cpp @@ -83,7 +83,7 @@ static void android_os_Trace_nativeAsyncTraceEnd(JNIEnv* env, jclass, } static void android_os_Trace_nativeSetAppTracingAllowed(JNIEnv*, jclass, jboolean allowed) { - atrace_set_debuggable(allowed); + atrace_update_tags(); } static void android_os_Trace_nativeSetTracingEnabled(JNIEnv*, jclass, jboolean enabled) { diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 7a3366acce27..451ea93349f7 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -44,14 +44,15 @@ #include <ui/BlurRegion.h> #include <ui/ConfigStoreTypes.h> #include <ui/DeviceProductInfo.h> -#include <ui/DisplayInfo.h> #include <ui/DisplayMode.h> #include <ui/DisplayedFrameStats.h> +#include <ui/DynamicDisplayInfo.h> #include <ui/FrameStats.h> #include <ui/GraphicTypes.h> #include <ui/HdrCapabilities.h> #include <ui/Rect.h> #include <ui/Region.h> +#include <ui/StaticDisplayInfo.h> #include <utils/LightRefBase.h> #include <utils/Log.h> @@ -86,11 +87,24 @@ static struct { jfieldID density; jfieldID secure; jfieldID deviceProductInfo; -} gDisplayInfoClassInfo; +} gStaticDisplayInfoClassInfo; static struct { jclass clazz; jmethodID ctor; + jfieldID supportedDisplayModes; + jfieldID activeDisplayModeId; + jfieldID supportedColorModes; + jfieldID activeColorMode; + jfieldID hdrCapabilities; + jfieldID autoLowLatencyModeSupported; + jfieldID gameContentTypeSupported; +} gDynamicDisplayInfoClassInfo; + +static struct { + jclass clazz; + jmethodID ctor; + jfieldID id; jfieldID width; jfieldID height; jfieldID xDpi; @@ -580,6 +594,15 @@ static void nativeSetBlurRegions(JNIEnv* env, jclass clazz, jlong transactionObj transaction->setBlurRegions(ctrl, blurRegionVector); } +static void nativeSetStretchEffect(JNIEnv* env, jclass clazz, jlong transactionObj, + jlong nativeObject, jfloat left, jfloat top, jfloat right, + jfloat bottom, jfloat vecX, jfloat vecY, + jfloat maxStretchAmount) { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject); + transaction->setStretchEffect(ctrl, left, top, right, bottom, vecX, vecY, maxStretchAmount); +} + static void nativeSetSize(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jint w, jint h) { auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); @@ -1011,51 +1034,105 @@ static jobject convertDeviceProductInfoToJavaObject( relativeAddress); } -static jobject nativeGetDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) { - DisplayInfo info; +static jobject nativeGetStaticDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) { + ui::StaticDisplayInfo info; if (const auto token = ibinderForJavaObject(env, tokenObj); - !token || SurfaceComposerClient::getDisplayInfo(token, &info) != NO_ERROR) { + !token || SurfaceComposerClient::getStaticDisplayInfo(token, &info) != NO_ERROR) { return nullptr; } - jobject object = env->NewObject(gDisplayInfoClassInfo.clazz, gDisplayInfoClassInfo.ctor); - env->SetBooleanField(object, gDisplayInfoClassInfo.isInternal, - info.connectionType == DisplayConnectionType::Internal); - env->SetFloatField(object, gDisplayInfoClassInfo.density, info.density); - env->SetBooleanField(object, gDisplayInfoClassInfo.secure, info.secure); - env->SetObjectField(object, gDisplayInfoClassInfo.deviceProductInfo, + jobject object = + env->NewObject(gStaticDisplayInfoClassInfo.clazz, gStaticDisplayInfoClassInfo.ctor); + env->SetBooleanField(object, gStaticDisplayInfoClassInfo.isInternal, + info.connectionType == ui::DisplayConnectionType::Internal); + env->SetFloatField(object, gStaticDisplayInfoClassInfo.density, info.density); + env->SetBooleanField(object, gStaticDisplayInfoClassInfo.secure, info.secure); + env->SetObjectField(object, gStaticDisplayInfoClassInfo.deviceProductInfo, convertDeviceProductInfoToJavaObject(env, info.deviceProductInfo)); return object; } -static jobjectArray nativeGetDisplayModes(JNIEnv* env, jclass clazz, jobject tokenObj) { - Vector<ui::DisplayMode> modes; - if (const auto token = ibinderForJavaObject(env, tokenObj); !token || - SurfaceComposerClient::getDisplayModes(token, &modes) != NO_ERROR || modes.isEmpty()) { +static jobject convertDisplayModeToJavaObject(JNIEnv* env, const ui::DisplayMode& config) { + jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor); + env->SetIntField(object, gDisplayModeClassInfo.id, config.id); + env->SetIntField(object, gDisplayModeClassInfo.width, config.resolution.getWidth()); + env->SetIntField(object, gDisplayModeClassInfo.height, config.resolution.getHeight()); + env->SetFloatField(object, gDisplayModeClassInfo.xDpi, config.xDpi); + env->SetFloatField(object, gDisplayModeClassInfo.yDpi, config.yDpi); + + env->SetFloatField(object, gDisplayModeClassInfo.refreshRate, config.refreshRate); + env->SetLongField(object, gDisplayModeClassInfo.appVsyncOffsetNanos, config.appVsyncOffset); + env->SetLongField(object, gDisplayModeClassInfo.presentationDeadlineNanos, + config.presentationDeadline); + env->SetIntField(object, gDisplayModeClassInfo.group, config.group); + return object; +} + +jobject convertDeviceProductInfoToJavaObject(JNIEnv* env, const HdrCapabilities& capabilities) { + const auto& types = capabilities.getSupportedHdrTypes(); + std::vector<int32_t> intTypes; + for (auto type : types) { + intTypes.push_back(static_cast<int32_t>(type)); + } + auto typesArray = env->NewIntArray(types.size()); + env->SetIntArrayRegion(typesArray, 0, intTypes.size(), intTypes.data()); + + return env->NewObject(gHdrCapabilitiesClassInfo.clazz, gHdrCapabilitiesClassInfo.ctor, + typesArray, capabilities.getDesiredMaxLuminance(), + capabilities.getDesiredMaxAverageLuminance(), + capabilities.getDesiredMinLuminance()); +} + +static jobject nativeGetDynamicDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) { + ui::DynamicDisplayInfo info; + if (const auto token = ibinderForJavaObject(env, tokenObj); + !token || SurfaceComposerClient::getDynamicDisplayInfo(token, &info) != NO_ERROR) { return nullptr; } - jobjectArray modesArray = - env->NewObjectArray(modes.size(), gDisplayModeClassInfo.clazz, nullptr); + jobject object = + env->NewObject(gDynamicDisplayInfoClassInfo.clazz, gDynamicDisplayInfoClassInfo.ctor); + if (object == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return NULL; + } - for (size_t c = 0; c < modes.size(); ++c) { - const ui::DisplayMode& mode = modes[c]; - jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor); - env->SetIntField(object, gDisplayModeClassInfo.width, mode.resolution.getWidth()); - env->SetIntField(object, gDisplayModeClassInfo.height, mode.resolution.getHeight()); - env->SetFloatField(object, gDisplayModeClassInfo.xDpi, mode.xDpi); - env->SetFloatField(object, gDisplayModeClassInfo.yDpi, mode.yDpi); + const auto numModes = info.supportedDisplayModes.size(); + jobjectArray modesArray = env->NewObjectArray(numModes, gDisplayModeClassInfo.clazz, nullptr); + for (size_t i = 0; i < numModes; i++) { + const ui::DisplayMode& mode = info.supportedDisplayModes[i]; + jobject displayModeObj = convertDisplayModeToJavaObject(env, mode); + env->SetObjectArrayElement(modesArray, static_cast<jsize>(i), displayModeObj); + env->DeleteLocalRef(displayModeObj); + } + env->SetObjectField(object, gDynamicDisplayInfoClassInfo.supportedDisplayModes, modesArray); + env->SetIntField(object, gDynamicDisplayInfoClassInfo.activeDisplayModeId, + info.activeDisplayModeId); - env->SetFloatField(object, gDisplayModeClassInfo.refreshRate, mode.refreshRate); - env->SetLongField(object, gDisplayModeClassInfo.appVsyncOffsetNanos, mode.appVsyncOffset); - env->SetLongField(object, gDisplayModeClassInfo.presentationDeadlineNanos, - mode.presentationDeadline); - env->SetIntField(object, gDisplayModeClassInfo.group, mode.group); - env->SetObjectArrayElement(modesArray, static_cast<jsize>(c), object); - env->DeleteLocalRef(object); + jintArray colorModesArray = env->NewIntArray(info.supportedColorModes.size()); + if (colorModesArray == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return NULL; } + jint* colorModesArrayValues = env->GetIntArrayElements(colorModesArray, 0); + for (size_t i = 0; i < info.supportedColorModes.size(); i++) { + colorModesArrayValues[i] = static_cast<jint>(info.supportedColorModes[i]); + } + env->ReleaseIntArrayElements(colorModesArray, colorModesArrayValues, 0); + env->SetObjectField(object, gDynamicDisplayInfoClassInfo.supportedColorModes, colorModesArray); + + env->SetIntField(object, gDynamicDisplayInfoClassInfo.activeColorMode, + static_cast<jint>(info.activeColorMode)); + + env->SetObjectField(object, gDynamicDisplayInfoClassInfo.hdrCapabilities, + convertDeviceProductInfoToJavaObject(env, info.hdrCapabilities)); + + env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.autoLowLatencyModeSupported, + info.autoLowLatencyModeSupported); - return modesArray; + env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.gameContentTypeSupported, + info.gameContentTypeSupported); + return object; } static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj, @@ -1063,9 +1140,8 @@ static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobj sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == nullptr) return JNI_FALSE; - size_t defaultMode = - static_cast<size_t>(env->GetIntField(DesiredDisplayModeSpecs, - gDesiredDisplayModeSpecsClassInfo.defaultMode)); + ui::DisplayModeId defaultMode = env->GetIntField(DesiredDisplayModeSpecs, + gDesiredDisplayModeSpecsClassInfo.defaultMode); jboolean allowGroupSwitching = env->GetBooleanField(DesiredDisplayModeSpecs, gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching); @@ -1095,7 +1171,7 @@ static jobject nativeGetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobje sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == nullptr) return nullptr; - size_t defaultMode; + ui::DisplayModeId defaultMode; bool allowGroupSwitching; float primaryRefreshRateMin; float primaryRefreshRateMax; @@ -1115,34 +1191,6 @@ static jobject nativeGetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobje appRequestRefreshRateMax); } -static jint nativeGetActiveDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return -1; - return static_cast<jint>(SurfaceComposerClient::getActiveDisplayModeId(token)); -} - -static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return NULL; - Vector<ui::ColorMode> colorModes; - if (SurfaceComposerClient::getDisplayColorModes(token, &colorModes) != NO_ERROR || - colorModes.isEmpty()) { - return NULL; - } - - jintArray colorModesArray = env->NewIntArray(colorModes.size()); - if (colorModesArray == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", NULL); - return NULL; - } - jint* colorModesArrayValues = env->GetIntArrayElements(colorModesArray, 0); - for (size_t i = 0; i < colorModes.size(); i++) { - colorModesArrayValues[i] = static_cast<jint>(colorModes[i]); - } - env->ReleaseIntArrayElements(colorModesArray, colorModesArrayValues, 0); - return colorModesArray; -} - static jobject nativeGetDisplayNativePrimaries(JNIEnv* env, jclass, jobject tokenObj) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == NULL) return NULL; @@ -1203,12 +1251,6 @@ static jobject nativeGetDisplayNativePrimaries(JNIEnv* env, jclass, jobject toke return jprimaries; } -static jint nativeGetActiveColorMode(JNIEnv* env, jclass, jobject tokenObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return -1; - return static_cast<jint>(SurfaceComposerClient::getActiveColorMode(token)); -} - static jintArray nativeGetCompositionDataspaces(JNIEnv* env, jclass) { ui::Dataspace defaultDataspace, wcgDataspace; ui::PixelFormat defaultPixelFormat, wcgPixelFormat; @@ -1414,40 +1456,6 @@ static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj, transaction->reparent(ctrl, newParent); } -static jobject nativeGetHdrCapabilities(JNIEnv* env, jclass clazz, jobject tokenObject) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); - if (token == NULL) return NULL; - - HdrCapabilities capabilities; - SurfaceComposerClient::getHdrCapabilities(token, &capabilities); - - const auto& types = capabilities.getSupportedHdrTypes(); - std::vector<int32_t> intTypes; - for (auto type : types) { - intTypes.push_back(static_cast<int32_t>(type)); - } - auto typesArray = env->NewIntArray(types.size()); - env->SetIntArrayRegion(typesArray, 0, intTypes.size(), intTypes.data()); - - return env->NewObject(gHdrCapabilitiesClassInfo.clazz, gHdrCapabilitiesClassInfo.ctor, - typesArray, capabilities.getDesiredMaxLuminance(), - capabilities.getDesiredMaxAverageLuminance(), capabilities.getDesiredMinLuminance()); -} - -static jboolean nativeGetAutoLowLatencyModeSupport(JNIEnv* env, jclass clazz, jobject tokenObject) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); - if (token == NULL) return NULL; - - return SurfaceComposerClient::getAutoLowLatencyModeSupport(token); -} - -static jboolean nativeGetGameContentTypeSupport(JNIEnv* env, jclass clazz, jobject tokenObject) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); - if (token == NULL) return NULL; - - return SurfaceComposerClient::getGameContentTypeSupport(token); -} - static void nativeSetAutoLowLatencyMode(JNIEnv* env, jclass clazz, jobject tokenObject, jboolean on) { sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); if (token == NULL) return; @@ -1754,6 +1762,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetLayerStack }, {"nativeSetBlurRegions", "(JJ[[FI)V", (void*)nativeSetBlurRegions }, + {"nativeSetStretchEffect", "(JJFFFFFFF)V", + (void*) nativeSetStretchEffect }, {"nativeSetShadowRadius", "(JJF)V", (void*)nativeSetShadowRadius }, {"nativeSetFrameRate", "(JJFIZ)V", @@ -1778,38 +1788,29 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetDisplayProjection }, {"nativeSetDisplaySize", "(JLandroid/os/IBinder;II)V", (void*)nativeSetDisplaySize }, - {"nativeGetDisplayInfo", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayInfo;", - (void*)nativeGetDisplayInfo }, - {"nativeGetDisplayModes", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayMode;", - (void*)nativeGetDisplayModes }, - {"nativeGetActiveDisplayMode", "(Landroid/os/IBinder;)I", - (void*)nativeGetActiveDisplayMode }, + {"nativeGetStaticDisplayInfo", + "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$StaticDisplayInfo;", + (void*)nativeGetStaticDisplayInfo }, + {"nativeGetDynamicDisplayInfo", + "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DynamicDisplayInfo;", + (void*)nativeGetDynamicDisplayInfo }, {"nativeSetDesiredDisplayModeSpecs", "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;)Z", (void*)nativeSetDesiredDisplayModeSpecs }, {"nativeGetDesiredDisplayModeSpecs", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;", (void*)nativeGetDesiredDisplayModeSpecs }, - {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I", - (void*)nativeGetDisplayColorModes}, - {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;", + {"nativeGetDisplayNativePrimaries", + "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;", (void*)nativeGetDisplayNativePrimaries }, - {"nativeGetActiveColorMode", "(Landroid/os/IBinder;)I", - (void*)nativeGetActiveColorMode}, {"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z", (void*)nativeSetActiveColorMode}, - {"nativeGetAutoLowLatencyModeSupport", "(Landroid/os/IBinder;)Z", - (void*)nativeGetAutoLowLatencyModeSupport }, {"nativeSetAutoLowLatencyMode", "(Landroid/os/IBinder;Z)V", (void*)nativeSetAutoLowLatencyMode }, - {"nativeGetGameContentTypeSupport", "(Landroid/os/IBinder;)Z", - (void*)nativeGetGameContentTypeSupport }, {"nativeSetGameContentType", "(Landroid/os/IBinder;Z)V", (void*)nativeSetGameContentType }, {"nativeGetCompositionDataspaces", "()[I", (void*)nativeGetCompositionDataspaces}, - {"nativeGetHdrCapabilities", "(Landroid/os/IBinder;)Landroid/view/Display$HdrCapabilities;", - (void*)nativeGetHdrCapabilities }, {"nativeClearContentFrameStats", "(J)Z", (void*)nativeClearContentFrameStats }, {"nativeGetContentFrameStats", "(JLandroid/view/WindowContentFrameStats;)Z", @@ -1888,19 +1889,40 @@ int register_android_view_SurfaceControl(JNIEnv* env) gIntegerClassInfo.clazz = MakeGlobalRefOrDie(env, integerClass); gIntegerClassInfo.ctor = GetMethodIDOrDie(env, gIntegerClassInfo.clazz, "<init>", "(I)V"); - jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayInfo"); - gDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz); - gDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V"); - gDisplayInfoClassInfo.isInternal = GetFieldIDOrDie(env, infoClazz, "isInternal", "Z"); - gDisplayInfoClassInfo.density = GetFieldIDOrDie(env, infoClazz, "density", "F"); - gDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, infoClazz, "secure", "Z"); - gDisplayInfoClassInfo.deviceProductInfo = + jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$StaticDisplayInfo"); + gStaticDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz); + gStaticDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V"); + gStaticDisplayInfoClassInfo.isInternal = GetFieldIDOrDie(env, infoClazz, "isInternal", "Z"); + gStaticDisplayInfoClassInfo.density = GetFieldIDOrDie(env, infoClazz, "density", "F"); + gStaticDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, infoClazz, "secure", "Z"); + gStaticDisplayInfoClassInfo.deviceProductInfo = GetFieldIDOrDie(env, infoClazz, "deviceProductInfo", "Landroid/hardware/display/DeviceProductInfo;"); + jclass dynamicInfoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DynamicDisplayInfo"); + gDynamicDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, dynamicInfoClazz); + gDynamicDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, dynamicInfoClazz, "<init>", "()V"); + gDynamicDisplayInfoClassInfo.supportedDisplayModes = + GetFieldIDOrDie(env, dynamicInfoClazz, "supportedDisplayModes", + "[Landroid/view/SurfaceControl$DisplayMode;"); + gDynamicDisplayInfoClassInfo.activeDisplayModeId = + GetFieldIDOrDie(env, dynamicInfoClazz, "activeDisplayModeId", "I"); + gDynamicDisplayInfoClassInfo.supportedColorModes = + GetFieldIDOrDie(env, dynamicInfoClazz, "supportedColorModes", "[I"); + gDynamicDisplayInfoClassInfo.activeColorMode = + GetFieldIDOrDie(env, dynamicInfoClazz, "activeColorMode", "I"); + gDynamicDisplayInfoClassInfo.hdrCapabilities = + GetFieldIDOrDie(env, dynamicInfoClazz, "hdrCapabilities", + "Landroid/view/Display$HdrCapabilities;"); + gDynamicDisplayInfoClassInfo.autoLowLatencyModeSupported = + GetFieldIDOrDie(env, dynamicInfoClazz, "autoLowLatencyModeSupported", "Z"); + gDynamicDisplayInfoClassInfo.gameContentTypeSupported = + GetFieldIDOrDie(env, dynamicInfoClazz, "gameContentTypeSupported", "Z"); + jclass modeClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayMode"); gDisplayModeClassInfo.clazz = MakeGlobalRefOrDie(env, modeClazz); gDisplayModeClassInfo.ctor = GetMethodIDOrDie(env, modeClazz, "<init>", "()V"); + gDisplayModeClassInfo.id = GetFieldIDOrDie(env, modeClazz, "id", "I"); gDisplayModeClassInfo.width = GetFieldIDOrDie(env, modeClazz, "width", "I"); gDisplayModeClassInfo.height = GetFieldIDOrDie(env, modeClazz, "height", "I"); gDisplayModeClassInfo.xDpi = GetFieldIDOrDie(env, modeClazz, "xDpi", "F"); diff --git a/core/jni/android_view_SurfaceControlFpsListener.cpp b/core/jni/android_view_SurfaceControlFpsListener.cpp new file mode 100644 index 000000000000..6fa12e510459 --- /dev/null +++ b/core/jni/android_view_SurfaceControlFpsListener.cpp @@ -0,0 +1,130 @@ +/* + * 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. + */ + +#define LOG_TAG "SurfaceControlFpsListener" + +#include <android/gui/BnFpsListener.h> +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/Log.h> +#include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> +#include <nativehelper/JNIHelp.h> +#include <utils/Log.h> +#include <utils/RefBase.h> + +#include "android_util_Binder.h" +#include "core_jni_helpers.h" + +namespace android { + +namespace { + +struct { + jclass mClass; + jmethodID mDispatchOnFpsReported; +} gListenerClassInfo; + +struct SurfaceControlFpsListener : public gui::BnFpsListener { + SurfaceControlFpsListener(JNIEnv* env, jobject listener) + : mListener(env->NewWeakGlobalRef(listener)) {} + + binder::Status onFpsReported(float fps) override { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + LOG_ALWAYS_FATAL_IF(env == nullptr, "Unable to retrieve JNIEnv in onFpsReported."); + + jobject listener = env->NewGlobalRef(mListener); + if (listener == NULL) { + // Weak reference went out of scope + return binder::Status::ok(); + } + env->CallStaticVoidMethod(gListenerClassInfo.mClass, + gListenerClassInfo.mDispatchOnFpsReported, listener, + static_cast<jfloat>(fps)); + env->DeleteGlobalRef(listener); + + if (env->ExceptionCheck()) { + ALOGE("SurfaceControlFpsListener.onFpsReported() failed."); + LOGE_EX(env); + env->ExceptionClear(); + } + return binder::Status::ok(); + } + +protected: + virtual ~SurfaceControlFpsListener() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteWeakGlobalRef(mListener); + } + +private: + jweak mListener; +}; + +jlong nativeCreate(JNIEnv* env, jclass clazz, jobject obj) { + SurfaceControlFpsListener* listener = new SurfaceControlFpsListener(env, obj); + listener->incStrong((void*)nativeCreate); + return reinterpret_cast<jlong>(listener); +} + +void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) { + SurfaceControlFpsListener* listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr); + listener->decStrong((void*)nativeCreate); +} + +void nativeRegister(JNIEnv* env, jclass clazz, jlong ptr, jlong layerObj) { + sp<SurfaceControlFpsListener> listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr); + auto layer = reinterpret_cast<SurfaceControl*>(layerObj); + sp<IBinder> layerHandle = layer != nullptr ? layer->getHandle() : nullptr; + if (SurfaceComposerClient::addFpsListener(layerHandle, listener) != OK) { + constexpr auto error_msg = "Couldn't addFpsListener"; + ALOGE(error_msg); + jniThrowRuntimeException(env, error_msg); + } +} + +void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) { + sp<SurfaceControlFpsListener> listener = reinterpret_cast<SurfaceControlFpsListener*>(ptr); + + if (SurfaceComposerClient::removeFpsListener(listener) != OK) { + constexpr auto error_msg = "Couldn't removeFpsListener"; + ALOGE(error_msg); + jniThrowRuntimeException(env, error_msg); + } +} + +const JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + {"nativeCreate", "(Landroid/view/SurfaceControlFpsListener;)J", (void*)nativeCreate}, + {"nativeDestroy", "(J)V", (void*)nativeDestroy}, + {"nativeRegister", "(JJ)V", (void*)nativeRegister}, + {"nativeUnregister", "(J)V", (void*)nativeUnregister}}; + +} // namespace + +int register_android_view_SurfaceControlFpsListener(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "android/view/SurfaceControlFpsListener", gMethods, + NELEM(gMethods)); + LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); + + jclass clazz = env->FindClass("android/view/SurfaceControlFpsListener"); + gListenerClassInfo.mClass = MakeGlobalRefOrDie(env, clazz); + gListenerClassInfo.mDispatchOnFpsReported = + env->GetStaticMethodID(clazz, "dispatchOnFpsReported", + "(Landroid/view/SurfaceControlFpsListener;F)V"); + return 0; +} + +} // namespace android diff --git a/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp b/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp index e6a82f6d0cb5..6b41b2ec8f93 100644 --- a/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp +++ b/core/jni/com_android_internal_os_KernelCpuBpfTracking.cpp @@ -24,8 +24,48 @@ static jboolean KernelCpuBpfTracking_isSupported(JNIEnv *, jobject) { return android::bpf::isTrackingUidTimesSupported() ? JNI_TRUE : JNI_FALSE; } +static jboolean KernelCpuBpfTracking_startTrackingInternal(JNIEnv *, jobject) { + return android::bpf::startTrackingUidTimes(); +} + +static jlongArray KernelCpuBpfTracking_getFreqsInternal(JNIEnv *env, jobject) { + auto freqs = android::bpf::getCpuFreqs(); + if (!freqs) return NULL; + + std::vector<uint64_t> allFreqs; + for (const auto &vec : *freqs) std::copy(vec.begin(), vec.end(), std::back_inserter(allFreqs)); + + auto ar = env->NewLongArray(allFreqs.size()); + if (ar != NULL) { + env->SetLongArrayRegion(ar, 0, allFreqs.size(), + reinterpret_cast<const jlong *>(allFreqs.data())); + } + return ar; +} + +static jintArray KernelCpuBpfTracking_getFreqsClustersInternal(JNIEnv *env, jobject) { + auto freqs = android::bpf::getCpuFreqs(); + if (!freqs) return NULL; + + std::vector<uint32_t> freqsClusters; + uint32_t clusters = freqs->size(); + for (uint32_t c = 0; c < clusters; ++c) { + freqsClusters.insert(freqsClusters.end(), (*freqs)[c].size(), c); + } + + auto ar = env->NewIntArray(freqsClusters.size()); + if (ar != NULL) { + env->SetIntArrayRegion(ar, 0, freqsClusters.size(), + reinterpret_cast<const jint *>(freqsClusters.data())); + } + return ar; +} + static const JNINativeMethod methods[] = { {"isSupported", "()Z", (void *)KernelCpuBpfTracking_isSupported}, + {"startTrackingInternal", "()Z", (void *)KernelCpuBpfTracking_startTrackingInternal}, + {"getFreqsInternal", "()[J", (void *)KernelCpuBpfTracking_getFreqsInternal}, + {"getFreqsClustersInternal", "()[I", (void *)KernelCpuBpfTracking_getFreqsClustersInternal}, }; int register_com_android_internal_os_KernelCpuBpfTracking(JNIEnv *env) { diff --git a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp index 72492381e31a..ad43014d321f 100644 --- a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp +++ b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp @@ -20,33 +20,27 @@ namespace android { -static jboolean KernelCpuTotalBpfMapReader_read(JNIEnv *env, jobject, jobject callback) { - jclass callbackClass = env->GetObjectClass(callback); - jmethodID callbackMethod = env->GetMethodID(callbackClass, "accept", "(IIJ)V"); - if (callbackMethod == 0) { - return JNI_FALSE; - } - - auto freqs = android::bpf::getCpuFreqs(); - if (!freqs) return JNI_FALSE; +static jlongArray KernelCpuTotalBpfMapReader_readInternal(JNIEnv *env, jobject) { auto freqTimes = android::bpf::getTotalCpuFreqTimes(); if (!freqTimes) return JNI_FALSE; - auto freqsClusterSize = (*freqs).size(); - for (uint32_t clusterIndex = 0; clusterIndex < freqsClusterSize; ++clusterIndex) { - auto freqsSize = (*freqs)[clusterIndex].size(); - for (uint32_t freqIndex = 0; freqIndex < freqsSize; ++freqIndex) { - env->CallVoidMethod(callback, callbackMethod, clusterIndex, - (*freqs)[clusterIndex][freqIndex], - (*freqTimes)[clusterIndex][freqIndex] / 1000000); + std::vector<uint64_t> allTimes; + for (const auto &vec : *freqTimes) { + for (const auto &timeNs : vec) { + allTimes.push_back(timeNs / 1000000); } } - return JNI_TRUE; + + auto ar = env->NewLongArray(allTimes.size()); + if (ar != NULL) { + env->SetLongArrayRegion(ar, 0, allTimes.size(), + reinterpret_cast<const jlong *>(allTimes.data())); + } + return ar; } static const JNINativeMethod methods[] = { - {"read", "(Lcom/android/internal/os/KernelCpuTotalBpfMapReader$Callback;)Z", - (void *)KernelCpuTotalBpfMapReader_read}, + {"readInternal", "()[J", (void *)KernelCpuTotalBpfMapReader_readInternal}, }; int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv *env) { diff --git a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp index 7c68de504417..7900d301dbb0 100644 --- a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp +++ b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp @@ -82,25 +82,9 @@ static jboolean KernelCpuUidFreqTimeBpfMapReader_readBpfData(JNIEnv *env, jobjec return true; } -static jlongArray KernelCpuUidFreqTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) { - auto freqs = android::bpf::getCpuFreqs(); - if (!freqs) return NULL; - - std::vector<uint64_t> allFreqs; - for (const auto &vec : *freqs) std::copy(vec.begin(), vec.end(), std::back_inserter(allFreqs)); - - auto ar = env->NewLongArray(allFreqs.size()); - if (ar != NULL) { - env->SetLongArrayRegion(ar, 0, allFreqs.size(), - reinterpret_cast<const jlong *>(allFreqs.data())); - } - return ar; -} - static const JNINativeMethod gFreqTimeMethods[] = { {"removeUidRange", "(II)Z", (void *)KernelCpuUidFreqTimeBpfMapReader_removeUidRange}, {"readBpfData", "()Z", (void *)KernelCpuUidFreqTimeBpfMapReader_readBpfData}, - {"getDataDimensions", "()[J", (void *)KernelCpuUidFreqTimeBpfMapReader_getDataDimensions}, }; static jboolean KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) { @@ -186,10 +170,6 @@ static const readerMethods gAllMethods[] = { {"KernelCpuUidClusterTimeBpfMapReader", gClusterTimeMethods, NELEM(gClusterTimeMethods)}, }; -static jboolean KernelCpuUidBpfMapReader_startTrackingBpfTimes(JNIEnv *, jobject) { - return android::bpf::startTrackingUidTimes(); -} - int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env) { gSparseArrayClassInfo.clazz = FindClassOrDie(env, "android/util/SparseArray"); gSparseArrayClassInfo.clazz = MakeGlobalRefOrDie(env, gSparseArrayClassInfo.clazz); @@ -198,14 +178,10 @@ int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env) { gSparseArrayClassInfo.get = GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "get", "(I)Ljava/lang/Object;"); constexpr auto readerName = "com/android/internal/os/KernelCpuUidBpfMapReader"; - constexpr JNINativeMethod method = {"startTrackingBpfTimes", "()Z", - (void *)KernelCpuUidBpfMapReader_startTrackingBpfTimes}; - - int ret = RegisterMethodsOrDie(env, readerName, &method, 1); - if (ret < 0) return ret; auto c = FindClassOrDie(env, readerName); gmData = GetFieldIDOrDie(env, c, "mData", "Landroid/util/SparseArray;"); + int ret = 0; for (const auto &m : gAllMethods) { auto fullName = android::base::StringPrintf("%s$%s", readerName, m.name); ret = RegisterMethodsOrDie(env, fullName.c_str(), m.methods, m.numMethods); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index e8017253fc29..c9062d8a50bc 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "Zygote" #define ATRACE_TAG ATRACE_TAG_DALVIK +#include "com_android_internal_os_Zygote.h" + #include <async_safe/log.h> // sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc @@ -91,19 +93,6 @@ #include "nativebridge/native_bridge.h" -/* Functions in the callchain during the fork shall not be protected with - Armv8.3-A Pointer Authentication, otherwise child will not be able to return. */ -#ifdef __ARM_FEATURE_PAC_DEFAULT -#ifdef __ARM_FEATURE_BTI_DEFAULT -#define NO_PAC_FUNC __attribute__((target("branch-protection=bti"))) -#else -#define NO_PAC_FUNC __attribute__((target("branch-protection=none"))) -#endif /* __ARM_FEATURE_BTI_DEFAULT */ -#else /* !__ARM_FEATURE_PAC_DEFAULT */ -#define NO_PAC_FUNC -#endif /* __ARM_FEATURE_PAC_DEFAULT */ - - namespace { // TODO (chriswailes): Add a function to initialize native Zygote data. @@ -118,8 +107,7 @@ using android::base::StringPrintf; using android::base::WriteStringToFile; using android::base::GetBoolProperty; -#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \ - append(StringPrintf(__VA_ARGS__)) +using android::zygote::ZygoteFailure; // This type is duplicated in fd_utils.h typedef const std::function<void(std::string)>& fail_fn_t; @@ -214,7 +202,7 @@ class UsapTableEntry { static constexpr EntryStorage INVALID_ENTRY_VALUE = {-1, -1}; std::atomic<EntryStorage> mStorage; - static_assert(decltype(mStorage)::is_always_lock_free); + static_assert(decltype(mStorage)::is_always_lock_free); // Accessed from signal handler. public: constexpr UsapTableEntry() : mStorage(INVALID_ENTRY_VALUE) {} @@ -917,36 +905,6 @@ void SetThreadName(const std::string& thread_name) { } /** - * A failure function used to report fatal errors to the managed runtime. This - * function is often curried with the process name information and then passed - * to called functions. - * - * @param env Managed runtime environment - * @param process_name A native representation of the process name - * @param managed_process_name A managed representation of the process name - * @param msg The error message to be reported - */ -[[noreturn]] -static void ZygoteFailure(JNIEnv* env, - const char* process_name, - jstring managed_process_name, - const std::string& msg) { - std::unique_ptr<ScopedUtfChars> scoped_managed_process_name_ptr = nullptr; - if (managed_process_name != nullptr) { - scoped_managed_process_name_ptr.reset(new ScopedUtfChars(env, managed_process_name)); - if (scoped_managed_process_name_ptr->c_str() != nullptr) { - process_name = scoped_managed_process_name_ptr->c_str(); - } - } - - const std::string& error_msg = - (process_name == nullptr) ? msg : StringPrintf("(%s) %s", process_name, msg.c_str()); - - env->FatalError(error_msg.c_str()); - __builtin_unreachable(); -} - -/** * A helper method for converting managed strings to native strings. A fatal * error is generated if a problem is encountered in extracting a non-null * string. @@ -1073,86 +1031,6 @@ static void PAuthKeyChange(JNIEnv* env) { #endif } -// Utility routine to fork a process from the zygote. -NO_PAC_FUNC -static pid_t ForkCommon(JNIEnv* env, bool is_system_server, - const std::vector<int>& fds_to_close, - const std::vector<int>& fds_to_ignore, - bool is_priority_fork) { - SetSignalHandlers(); - - // Curry a failure function. - auto fail_fn = std::bind(ZygoteFailure, env, is_system_server ? "system_server" : "zygote", - nullptr, _1); - - // Temporarily block SIGCHLD during forks. The SIGCHLD handler might - // log, which would result in the logging FDs we close being reopened. - // This would cause failures because the FDs are not allowlisted. - // - // Note that the zygote process is single threaded at this point. - BlockSignal(SIGCHLD, fail_fn); - - // Close any logging related FDs before we start evaluating the list of - // file descriptors. - __android_log_close(); - AStatsSocket_close(); - - // If this is the first fork for this zygote, create the open FD table. If - // it isn't, we just need to check whether the list of open files has changed - // (and it shouldn't in the normal case). - if (gOpenFdTable == nullptr) { - gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, fail_fn); - } else { - gOpenFdTable->Restat(fds_to_ignore, fail_fn); - } - - android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level(); - - // Purge unused native memory in an attempt to reduce the amount of false - // sharing with the child process. By reducing the size of the libc_malloc - // region shared with the child process we reduce the number of pages that - // transition to the private-dirty state when malloc adjusts the meta-data - // on each of the pages it is managing after the fork. - mallopt(M_PURGE, 0); - - pid_t pid = fork(); - - if (pid == 0) { - if (is_priority_fork) { - setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX); - } else { - setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MIN); - } - - // The child process. - PAuthKeyChange(env); - PreApplicationInit(); - - // Clean up any descriptors which must be closed immediately - DetachDescriptors(env, fds_to_close, fail_fn); - - // Invalidate the entries in the USAP table. - ClearUsapTable(); - - // Re-open all remaining open file descriptors so that they aren't shared - // with the zygote across a fork. - gOpenFdTable->ReopenOrDetach(fail_fn); - - // Turn fdsan back on. - android_fdsan_set_error_level(fdsan_error_level); - - // Reset the fd to the unsolicited zygote socket - gSystemServerSocketFd = -1; - } else { - ALOGD("Forked child process %d", pid); - } - - // We blocked SIGCHLD prior to a fork, we unblock it here. - UnblockSignal(SIGCHLD, fail_fn); - - return pid; -} - // Create an app data directory over tmpfs overlayed CE / DE storage, and bind mount it // from the actual app data directory in data mirror. static bool createAndMountAppData(std::string_view package_name, @@ -1973,9 +1851,10 @@ static void AddUsapTableEntry(pid_t usap_pid, int read_pipe_fd) { static int sUsapTableInsertIndex = 0; int search_index = sUsapTableInsertIndex; - do { if (gUsapTable[search_index].SetIfInvalid(usap_pid, read_pipe_fd)) { + ++gUsapPoolCount; + // Start our next search right after where we finished this one. sUsapTableInsertIndex = (search_index + 1) % gUsapTable.size(); @@ -1993,7 +1872,7 @@ static void AddUsapTableEntry(pid_t usap_pid, int read_pipe_fd) { /** * Invalidates the entry in the USAPTable corresponding to the provided * process ID if it is present. If an entry was removed the USAP pool - * count is decremented. + * count is decremented. May be called from signal handler. * * @param usap_pid Process ID of the USAP entry to invalidate * @return True if an entry was invalidated; false otherwise @@ -2069,6 +1948,121 @@ static void UnmountStorageOnInit(JNIEnv* env) { namespace android { +/** + * A failure function used to report fatal errors to the managed runtime. This + * function is often curried with the process name information and then passed + * to called functions. + * + * @param env Managed runtime environment + * @param process_name A native representation of the process name + * @param managed_process_name A managed representation of the process name + * @param msg The error message to be reported + */ +[[noreturn]] +void zygote::ZygoteFailure(JNIEnv* env, + const char* process_name, + jstring managed_process_name, + const std::string& msg) { + std::unique_ptr<ScopedUtfChars> scoped_managed_process_name_ptr = nullptr; + if (managed_process_name != nullptr) { + scoped_managed_process_name_ptr.reset(new ScopedUtfChars(env, managed_process_name)); + if (scoped_managed_process_name_ptr->c_str() != nullptr) { + process_name = scoped_managed_process_name_ptr->c_str(); + } + } + + const std::string& error_msg = + (process_name == nullptr || process_name[0] == '\0') ? + msg : StringPrintf("(%s) %s", process_name, msg.c_str()); + + env->FatalError(error_msg.c_str()); + __builtin_unreachable(); +} + +// Utility routine to fork a process from the zygote. +NO_PAC_FUNC +pid_t zygote::ForkCommon(JNIEnv* env, bool is_system_server, + const std::vector<int>& fds_to_close, + const std::vector<int>& fds_to_ignore, + bool is_priority_fork, + bool purge) { + SetSignalHandlers(); + + // Curry a failure function. + auto fail_fn = std::bind(zygote::ZygoteFailure, env, + is_system_server ? "system_server" : "zygote", + nullptr, _1); + + // Temporarily block SIGCHLD during forks. The SIGCHLD handler might + // log, which would result in the logging FDs we close being reopened. + // This would cause failures because the FDs are not allowlisted. + // + // Note that the zygote process is single threaded at this point. + BlockSignal(SIGCHLD, fail_fn); + + // Close any logging related FDs before we start evaluating the list of + // file descriptors. + __android_log_close(); + AStatsSocket_close(); + + // If this is the first fork for this zygote, create the open FD table. If + // it isn't, we just need to check whether the list of open files has changed + // (and it shouldn't in the normal case). + if (gOpenFdTable == nullptr) { + gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, fail_fn); + } else { + gOpenFdTable->Restat(fds_to_ignore, fail_fn); + } + + android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level(); + + if (purge) { + // Purge unused native memory in an attempt to reduce the amount of false + // sharing with the child process. By reducing the size of the libc_malloc + // region shared with the child process we reduce the number of pages that + // transition to the private-dirty state when malloc adjusts the meta-data + // on each of the pages it is managing after the fork. + mallopt(M_PURGE, 0); + } + + pid_t pid = fork(); + + if (pid == 0) { + if (is_priority_fork) { + setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX); + } else { + setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MIN); + } + + // The child process. + PAuthKeyChange(env); + PreApplicationInit(); + + // Clean up any descriptors which must be closed immediately + DetachDescriptors(env, fds_to_close, fail_fn); + + // Invalidate the entries in the USAP table. + ClearUsapTable(); + + // Re-open all remaining open file descriptors so that they aren't shared + // with the zygote across a fork. + gOpenFdTable->ReopenOrDetach(fail_fn); + + // Turn fdsan back on. + android_fdsan_set_error_level(fdsan_error_level); + + // Reset the fd to the unsolicited zygote socket + gSystemServerSocketFd = -1; + } else { + ALOGD("Forked child process %d", pid); + } + + // We blocked SIGCHLD prior to a fork, we unblock it here. + UnblockSignal(SIGCHLD, fail_fn); + + return pid; +} + static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jclass) { PreApplicationInit(); } @@ -2085,7 +2079,8 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote); if (UNLIKELY(managed_fds_to_close == nullptr)) { - ZygoteFailure(env, "zygote", nice_name, "Zygote received a null fds_to_close vector."); + zygote::ZygoteFailure(env, "zygote", nice_name, + "Zygote received a null fds_to_close vector."); } std::vector<int> fds_to_close = @@ -2111,7 +2106,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( fds_to_ignore.push_back(gSystemServerSocketFd); } - pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore, true); + pid_t pid = zygote::ForkCommon(env, false, fds_to_close, fds_to_ignore, true); if (pid == 0) { SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, @@ -2146,10 +2141,10 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( fds_to_ignore.push_back(gSystemServerSocketFd); } - pid_t pid = ForkCommon(env, true, - fds_to_close, - fds_to_ignore, - true); + pid_t pid = zygote::ForkCommon(env, true, + fds_to_close, + fds_to_ignore, + true); if (pid == 0) { // System server prcoess does not need data isolation so no need to // know pkg_data_info_list. @@ -2189,58 +2184,74 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( * ensuring proper file descriptor hygiene. * * @param env Managed runtime environment - * @param read_pipe_fd The read FD for the USAP reporting pipe. Manually closed by blastlas - * in managed code. + * @param read_pipe_fd The read FD for the USAP reporting pipe. Manually closed by the child + * in managed code. -1 indicates none. * @param write_pipe_fd The write FD for the USAP reporting pipe. Manually closed by the - * zygote in managed code. + * zygote in managed code. -1 indicates none. * @param managed_session_socket_fds A list of anonymous session sockets that must be ignored by * the FD hygiene code and automatically "closed" in the new USAP. + * @param args_known Arguments for specialization are available; no need to read from a socket * @param is_priority_fork Controls the nice level assigned to the newly created process - * @return + * @return child pid in the parent, 0 in the child */ NO_PAC_FUNC -static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env, - jclass, - jint read_pipe_fd, - jint write_pipe_fd, - jintArray managed_session_socket_fds, - jboolean is_priority_fork) { - std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()), - fds_to_ignore(fds_to_close); - +static jint com_android_internal_os_Zygote_nativeForkApp(JNIEnv* env, + jclass, + jint read_pipe_fd, + jint write_pipe_fd, + jintArray managed_session_socket_fds, + jboolean args_known, + jboolean is_priority_fork) { std::vector<int> session_socket_fds = ExtractJIntArray(env, "USAP", nullptr, managed_session_socket_fds) .value_or(std::vector<int>()); + return zygote::forkApp(env, read_pipe_fd, write_pipe_fd, session_socket_fds, + args_known == JNI_TRUE, is_priority_fork == JNI_TRUE, true); +} - // The USAP Pool Event FD is created during the initialization of the - // USAP pool and should always be valid here. +NO_PAC_FUNC +int zygote::forkApp(JNIEnv* env, + int read_pipe_fd, + int write_pipe_fd, + const std::vector<int>& session_socket_fds, + bool args_known, + bool is_priority_fork, + bool purge) { + + std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()), + fds_to_ignore(fds_to_close); fds_to_close.push_back(gZygoteSocketFD); - fds_to_close.push_back(gUsapPoolEventFD); - fds_to_close.insert(fds_to_close.end(), session_socket_fds.begin(), session_socket_fds.end()); if (gSystemServerSocketFd != -1) { fds_to_close.push_back(gSystemServerSocketFd); } + if (args_known) { + fds_to_close.push_back(gUsapPoolSocketFD); + } + fds_to_close.insert(fds_to_close.end(), session_socket_fds.begin(), session_socket_fds.end()); - fds_to_ignore.push_back(gZygoteSocketFD); fds_to_ignore.push_back(gUsapPoolSocketFD); - fds_to_ignore.push_back(gUsapPoolEventFD); - fds_to_ignore.push_back(read_pipe_fd); - fds_to_ignore.push_back(write_pipe_fd); + fds_to_ignore.push_back(gZygoteSocketFD); + if (read_pipe_fd != -1) { + fds_to_ignore.push_back(read_pipe_fd); + } + if (write_pipe_fd != -1) { + fds_to_ignore.push_back(write_pipe_fd); + } fds_to_ignore.insert(fds_to_ignore.end(), session_socket_fds.begin(), session_socket_fds.end()); + + if (gUsapPoolEventFD != -1) { + fds_to_close.push_back(gUsapPoolEventFD); + fds_to_ignore.push_back(gUsapPoolEventFD); + } if (gSystemServerSocketFd != -1) { + if (args_known) { + fds_to_close.push_back(gSystemServerSocketFd); + } fds_to_ignore.push_back(gSystemServerSocketFd); } - - pid_t usap_pid = ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore, - is_priority_fork == JNI_TRUE); - - if (usap_pid != 0) { - ++gUsapPoolCount; - AddUsapTableEntry(usap_pid, read_pipe_fd); - } - - return usap_pid; + return zygote::ForkCommon(env, /* is_system_server= */ false, fds_to_close, + fds_to_ignore, is_priority_fork == JNI_TRUE, purge); } static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork( @@ -2354,7 +2365,7 @@ static void com_android_internal_os_Zygote_nativeInitNativeState(JNIEnv* env, jc */ if (!SetTaskProfiles(0, {})) { - ZygoteFailure(env, "zygote", nullptr, "Zygote SetTaskProfiles failed"); + zygote::ZygoteFailure(env, "zygote", nullptr, "Zygote SetTaskProfiles failed"); } } @@ -2372,15 +2383,21 @@ static jintArray com_android_internal_os_Zygote_nativeGetUsapPipeFDs(JNIEnv* env return managed_usap_fds; } +/* + * Add the given pid and file descriptor to the Usap table. CriticalNative method. + */ +static void com_android_internal_os_Zygote_nativeAddUsapTableEntry(jint pid, jint read_pipe_fd) { + AddUsapTableEntry(pid, read_pipe_fd); +} + /** - * A JNI wrapper around RemoveUsapTableEntry. + * A JNI wrapper around RemoveUsapTableEntry. CriticalNative method. * * @param env Managed runtime environment * @param usap_pid Process ID of the USAP entry to invalidate * @return True if an entry was invalidated; false otherwise. */ -static jboolean com_android_internal_os_Zygote_nativeRemoveUsapTableEntry(JNIEnv* env, jclass, - jint usap_pid) { +static jboolean com_android_internal_os_Zygote_nativeRemoveUsapTableEntry(jint usap_pid) { return RemoveUsapTableEntry(usap_pid); } @@ -2395,7 +2412,8 @@ static jboolean com_android_internal_os_Zygote_nativeRemoveUsapTableEntry(JNIEnv static jint com_android_internal_os_Zygote_nativeGetUsapPoolEventFD(JNIEnv* env, jclass) { if (gUsapPoolEventFD == -1) { if ((gUsapPoolEventFD = eventfd(0, 0)) == -1) { - ZygoteFailure(env, "zygote", nullptr, StringPrintf("Unable to create eventfd: %s", strerror(errno))); + zygote::ZygoteFailure(env, "zygote", nullptr, + StringPrintf("Unable to create eventfd: %s", strerror(errno))); } } @@ -2438,12 +2456,12 @@ static void com_android_internal_os_Zygote_nativeEmptyUsapPool(JNIEnv* env, jcla } static void com_android_internal_os_Zygote_nativeBlockSigTerm(JNIEnv* env, jclass) { - auto fail_fn = std::bind(ZygoteFailure, env, "usap", nullptr, _1); + auto fail_fn = std::bind(zygote::ZygoteFailure, env, "usap", nullptr, _1); BlockSignal(SIGTERM, fail_fn); } static void com_android_internal_os_Zygote_nativeUnblockSigTerm(JNIEnv* env, jclass) { - auto fail_fn = std::bind(ZygoteFailure, env, "usap", nullptr, _1); + auto fail_fn = std::bind(zygote::ZygoteFailure, env, "usap", nullptr, _1); UnblockSignal(SIGTERM, fail_fn); } @@ -2549,7 +2567,10 @@ static const JNINativeMethod gMethods[] = { (void*)com_android_internal_os_Zygote_nativePreApplicationInit}, {"nativeInstallSeccompUidGidFilter", "(II)V", (void*)com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter}, - {"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap}, + {"nativeForkApp", "(II[IZZ)I", (void*)com_android_internal_os_Zygote_nativeForkApp}, + // @CriticalNative + {"nativeAddUsapTableEntry", "(II)V", + (void*)com_android_internal_os_Zygote_nativeAddUsapTableEntry}, {"nativeSpecializeAppProcess", "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/" "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V", @@ -2558,6 +2579,10 @@ static const JNINativeMethod gMethods[] = { (void*)com_android_internal_os_Zygote_nativeInitNativeState}, {"nativeGetUsapPipeFDs", "()[I", (void*)com_android_internal_os_Zygote_nativeGetUsapPipeFDs}, + // @CriticalNative + {"nativeAddUsapTableEntry", "(II)V", + (void*)com_android_internal_os_Zygote_nativeAddUsapTableEntry}, + // @CriticalNative {"nativeRemoveUsapTableEntry", "(I)Z", (void*)com_android_internal_os_Zygote_nativeRemoveUsapTableEntry}, {"nativeGetUsapPoolEventFD", "()I", diff --git a/core/jni/com_android_internal_os_Zygote.h b/core/jni/com_android_internal_os_Zygote.h new file mode 100644 index 000000000000..d2da91476bc7 --- /dev/null +++ b/core/jni/com_android_internal_os_Zygote.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _COM_ANDROID_INTERNAL_OS_ZYGOTE_H +#define _COM_ANDROID_INTERNAL_OS_ZYGOTE_H + +#define LOG_TAG "Zygote" +#define ATRACE_TAG ATRACE_TAG_DALVIK + +/* Functions in the callchain during the fork shall not be protected with + Armv8.3-A Pointer Authentication, otherwise child will not be able to return. */ +#ifdef __ARM_FEATURE_PAC_DEFAULT +#ifdef __ARM_FEATURE_BTI_DEFAULT +#define NO_PAC_FUNC __attribute__((target("branch-protection=bti"))) +#else +#define NO_PAC_FUNC __attribute__((target("branch-protection=none"))) +#endif /* __ARM_FEATURE_BTI_DEFAULT */ +#else /* !__ARM_FEATURE_PAC_DEFAULT */ +#define NO_PAC_FUNC +#endif /* __ARM_FEATURE_PAC_DEFAULT */ + +#include <jni.h> +#include <vector> +#include <android-base/stringprintf.h> + +#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \ + append(StringPrintf(__VA_ARGS__)) + +namespace android { +namespace zygote { + +NO_PAC_FUNC +pid_t ForkCommon(JNIEnv* env,bool is_system_server, + const std::vector<int>& fds_to_close, + const std::vector<int>& fds_to_ignore, + bool is_priority_fork, + bool purge = true); + +/** + * Fork a process. The pipe fds are used for usap communication, or -1 in + * other cases. Session_socket_fds are FDs used for zygote communication that must be dealt + * with hygienically, but are not otherwise used here. Args_known indicates that the process + * will be immediately specialized with arguments that are already known, so no usap + * communication is required. Is_priority_fork should be true if this is on the app startup + * critical path. Purge specifies that unused pages should be purged before the fork. + */ +NO_PAC_FUNC +int forkApp(JNIEnv* env, + int read_pipe_fd, + int write_pipe_fd, + const std::vector<int>& session_socket_fds, + bool args_known, + bool is_priority_fork, + bool purge); + +[[noreturn]] +void ZygoteFailure(JNIEnv* env, + const char* process_name, + jstring managed_process_name, + const std::string& msg); + +} // namespace zygote +} // namespace android + +#endif // _COM_ANDROID_INTERNAL_OS_ZYGOTE_ diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp new file mode 100644 index 000000000000..011e8f8f1b8c --- /dev/null +++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp @@ -0,0 +1,512 @@ +/* + * 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. + */ + +#include "com_android_internal_os_Zygote.h" + +#include <algorithm> +#include <android-base/logging.h> +#include <async_safe/log.h> +#include <cctype> +#include <chrono> +#include <core_jni_helpers.h> +#include <errno.h> +#include <fcntl.h> +#include <jni.h> +#include <nativehelper/JNIHelp.h> +#include <optional> +#include <poll.h> +#include <unistd.h> +#include <utility> +#include <utils/misc.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <vector> + +namespace android { + +using namespace std::placeholders; +using android::base::StringPrintf; +using android::zygote::ZygoteFailure; + +// WARNING: Knows a little about the wire protocol used to communicate with Zygote. +// TODO: Fix error handling. + +constexpr size_t MAX_COMMAND_BYTES = 12200; +constexpr size_t NICE_NAME_BYTES = 50; + +// A buffer optionally bundled with a file descriptor from which we can fill it. +// Does not own the file descriptor; destroying a NativeCommandBuffer does not +// close the descriptor. +class NativeCommandBuffer { + public: + NativeCommandBuffer(int sourceFd): mEnd(0), mNext(0), mLinesLeft(0), mFd(sourceFd) {} + + // Read mNext line from mFd, filling mBuffer from file descriptor, as needed. + // Return a pair of pointers pointing to the first character, and one past the + // mEnd of the line, i.e. at the newline. Returns nothing on failure. + template<class FailFn> + std::optional<std::pair<char*, char*>> readLine(FailFn fail_fn) { + char* result = mBuffer + mNext; + while (true) { + if (mNext == mEnd) { + if (mEnd == MAX_COMMAND_BYTES) { + return {}; + } + if (mFd == -1) { + fail_fn("ZygoteCommandBuffer.readLine attempted to read from mFd -1"); + } + ssize_t nread = TEMP_FAILURE_RETRY(read(mFd, mBuffer + mEnd, MAX_COMMAND_BYTES - mEnd)); + if (nread <= 0) { + if (nread == 0) { + return {}; + } + fail_fn(CREATE_ERROR("session socket read failed: %s", strerror(errno))); + } else if (nread == MAX_COMMAND_BYTES - mEnd) { + // This is pessimistic by one character, but close enough. + fail_fn("ZygoteCommandBuffer overflowed: command too long"); + } + mEnd += nread; + } + // UTF-8 does not allow newline to occur as part of a multibyte character. + char* nl = static_cast<char *>(memchr(mBuffer + mNext, '\n', mEnd - mNext)); + if (nl == nullptr) { + mNext = mEnd; + } else { + mNext = nl - mBuffer + 1; + if (--mLinesLeft < 0) { + fail_fn("ZygoteCommandBuffer.readLine attempted to read past mEnd of command"); + } + return std::make_pair(result, nl); + } + } + } + + void reset() { + mNext = 0; + } + + // Make sure the current command is fully buffered, without reading past the current command. + template<class FailFn> + void readAllLines(FailFn fail_fn) { + while (mLinesLeft > 0) { + readLine(fail_fn); + } + } + + void clear() { + // Don't bother to actually clear the buffer; it'll be unmapped in the child anyway. + reset(); + mNiceName[0] = '\0'; + mEnd = 0; + } + + // Insert line into the mBuffer. Checks that the mBuffer is not associated with an mFd. + // Implicitly adds newline separators. Allows mBuffer contents to be explicitly set. + void insert(const char* line, size_t lineLen) { + DCHECK(mFd == -1); + CHECK(mEnd + lineLen < MAX_COMMAND_BYTES); + strncpy(mBuffer + mEnd, line, lineLen); + mBuffer[mEnd + lineLen] = '\n'; + mEnd += lineLen + 1; + } + + // Clear mBuffer, start reading new command, return the number of arguments, leaving mBuffer + // positioned at the beginning of first argument. Return 0 on EOF. + template<class FailFn> + int getCount(FailFn fail_fn) { + mLinesLeft = 1; + auto line = readLine(fail_fn); + if (!line.has_value()) { + return 0; + } + char* countString = line.value().first; // Newline terminated. + long nArgs = atol(countString); + if (nArgs <= 0 || nArgs >= MAX_COMMAND_BYTES / 2) { + fail_fn(CREATE_ERROR("Unreasonable argument count %ld", nArgs)); + } + mLinesLeft = nArgs; + return static_cast<int>(nArgs); + } + + // Is the mBuffer a simple fork command? + // We disallow request to wrap the child process, child zygotes, anything that + // mentions capabilities or requests uid < minUid. + // We insist that --setuid and --setgid arguments are explicitly included and that the + // command starts with --runtime-args. + // Assumes we are positioned at the beginning of the command after the argument count, + // and leaves the position at some indeterminate position in the buffer. + // As a side effect, this sets mNiceName to a non-empty string, if possible. + template<class FailFn> + bool isSimpleForkCommand(int minUid, FailFn fail_fn) { + if (mLinesLeft <= 0 || mLinesLeft >= MAX_COMMAND_BYTES / 2) { + return false; + } + static const char* RUNTIME_ARGS = "--runtime-args"; + static const char* INVOKE_WITH = "--invoke-with"; + static const char* CHILD_ZYGOTE = "--start-child-zygote"; + static const char* SETUID = "--setuid="; + static const char* SETGID = "--setgid="; + static const char* CAPABILITIES = "--capabilities"; + static const char* NICE_NAME = "--nice-name="; + static const size_t RA_LENGTH = strlen(RUNTIME_ARGS); + static const size_t IW_LENGTH = strlen(INVOKE_WITH); + static const size_t CZ_LENGTH = strlen(CHILD_ZYGOTE); + static const size_t SU_LENGTH = strlen(SETUID); + static const size_t SG_LENGTH = strlen(SETGID); + static const size_t CA_LENGTH = strlen(CAPABILITIES); + static const size_t NN_LENGTH = strlen(NICE_NAME); + + bool saw_setuid = false, saw_setgid = false; + bool saw_runtime_args = false; + + while (mLinesLeft > 0) { + auto read_result = readLine(fail_fn); + if (!read_result.has_value()) { + return false; + } + auto [arg_start, arg_end] = read_result.value(); + if (arg_end - arg_start == RA_LENGTH + && strncmp(arg_start, RUNTIME_ARGS, RA_LENGTH) == 0) { + saw_runtime_args = true; + continue; + } + if (arg_end - arg_start >= NN_LENGTH + && strncmp(arg_start, NICE_NAME, NN_LENGTH) == 0) { + size_t name_len = arg_end - (arg_start + NN_LENGTH); + size_t copy_len = std::min(name_len, NICE_NAME_BYTES - 1); + memcpy(mNiceName, arg_start + NN_LENGTH, copy_len); + mNiceName[copy_len] = '\0'; + continue; + } + if (arg_end - arg_start == IW_LENGTH + && strncmp(arg_start, INVOKE_WITH, IW_LENGTH) == 0) { + // This also removes the need for invoke-with security checks here. + return false; + } + if (arg_end - arg_start == CZ_LENGTH + && strncmp(arg_start, CHILD_ZYGOTE, CZ_LENGTH) == 0) { + return false; + } + if (arg_end - arg_start >= CA_LENGTH + && strncmp(arg_start, CAPABILITIES, CA_LENGTH) == 0) { + return false; + } + if (arg_end - arg_start >= SU_LENGTH + && strncmp(arg_start, SETUID, SU_LENGTH) == 0) { + int uid = digitsVal(arg_start + SU_LENGTH, arg_end); + if (uid < minUid) { + return false; + } + saw_setuid = true; + continue; + } + if (arg_end - arg_start >= SG_LENGTH + && strncmp(arg_start, SETGID, SG_LENGTH) == 0) { + int gid = digitsVal(arg_start + SG_LENGTH, arg_end); + if (gid == -1) { + return false; + } + saw_setgid = true; + } + } + return saw_runtime_args && saw_setuid && saw_setgid; + } + + void setFd(int new_fd) { + mFd = new_fd; + } + + int getFd() const { + return mFd; + } + + const char* niceNameAddr() const { + return mNiceName; + } + + // Debug only: + void logState() const { + ALOGD("mbuffer starts with %c%c, nice name is %s, " + "mEnd = %u, mNext = %u, mLinesLeft = %d, mFd = %d", + mBuffer[0], (mBuffer[1] == '\n' ? ' ' : mBuffer[1]), + niceNameAddr(), + static_cast<unsigned>(mEnd), static_cast<unsigned>(mNext), + static_cast<int>(mLinesLeft), mFd); + } + + private: + // Picky version of atoi(). No sign or unexpected characters allowed. Return -1 on failure. + static int digitsVal(char* start, char* end) { + int result = 0; + if (end - start > 6) { + return -1; + } + for (char* dp = start; dp < end; ++dp) { + if (*dp < '0' || *dp > '9') { + ALOGW("Argument failed integer format check"); + return -1; + } + result = 10 * result + (*dp - '0'); + } + return result; + } + + uint32_t mEnd; // Index of first empty byte in the mBuffer. + uint32_t mNext; // Index of first character past last line returned by readLine. + int32_t mLinesLeft; // Lines in current command that haven't yet been read. + int mFd; // Open file descriptor from which we can read more. -1 if none. + char mNiceName[NICE_NAME_BYTES]; + char mBuffer[MAX_COMMAND_BYTES]; +}; + +static_assert(sizeof(NativeCommandBuffer) < 3 * 4096); + +static int buffersAllocd(0); + +// Get a new NativeCommandBuffer. Can only be called once between freeNativeBuffer calls, +// so that only one buffer exists at a time. +jlong com_android_internal_os_ZygoteCommandBuffer_getNativeBuffer(JNIEnv* env, jclass, jint fd) { + CHECK(buffersAllocd == 0); + ++buffersAllocd; + // MMap explicitly to get it page aligned. + void *bufferMem = mmap(NULL, sizeof(NativeCommandBuffer), PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0); + // Currently we mmap and unmap one for every request handled by the Java code. + // That could be improved, but unclear it matters. + if (bufferMem == MAP_FAILED) { + ZygoteFailure(env, nullptr, nullptr, "Failed to map argument buffer"); + } + return (jlong) new(bufferMem) NativeCommandBuffer(fd); +} + +// Delete native command buffer. +void com_android_internal_os_ZygoteCommandBuffer_freeNativeBuffer(JNIEnv* env, jclass, + jlong j_buffer) { + CHECK(buffersAllocd == 1); + NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer); + n_buffer->~NativeCommandBuffer(); + if (munmap(n_buffer, sizeof(NativeCommandBuffer)) != 0) { + ZygoteFailure(env, nullptr, nullptr, "Failed to unmap argument buffer"); + } + --buffersAllocd; +} + +// Clear the buffer, read the line containing the count, and return the count. +jint com_android_internal_os_ZygoteCommandBuffer_nativeGetCount(JNIEnv* env, jclass, + jlong j_buffer) { + NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer); + auto fail_fn = std::bind(ZygoteFailure, env, nullptr, nullptr, _1); + return n_buffer->getCount(fail_fn); +} + +// Explicitly insert a string as the last line (argument) of the buffer. +void com_android_internal_os_ZygoteCommandBuffer_insert(JNIEnv* env, jclass, jlong j_buffer, + jstring line) { + NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer); + size_t lineLen = static_cast<size_t>(env->GetStringUTFLength(line)); + const char* cstring = env->GetStringUTFChars(line, NULL); + n_buffer->insert(cstring, lineLen); + env->ReleaseStringUTFChars(line, cstring); +} + +// Read a line from the buffer, refilling as necessary. +jstring com_android_internal_os_ZygoteCommandBuffer_nativeNextArg(JNIEnv* env, jclass, + jlong j_buffer) { + NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer); + auto fail_fn = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(), nullptr, _1); + auto line = n_buffer->readLine(fail_fn); + if (!line.has_value()) { + fail_fn("Incomplete zygote command"); + } + auto [cresult, endp] = line.value(); + // OK to temporarily clobber the buffer, since this is not thread safe, and we're modifying + // the buffer anyway. + *endp = '\0'; + jstring result = env->NewStringUTF(cresult); + *endp = '\n'; + return result; +} + +// Read all lines from the current command into the buffer, and then reset the buffer, so +// we will start reading again at the beginning of the command, starting with the argument +// count. And we don't need access to the fd to do so. +void com_android_internal_os_ZygoteCommandBuffer_nativeReadFullyAndReset(JNIEnv* env, jclass, + jlong j_buffer) { + NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer); + auto fail_fn = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(), nullptr, _1); + n_buffer->readAllLines(fail_fn); + n_buffer->reset(); +} + +// Fork a child as specified by the current command buffer, and refill the command +// buffer from the given socket. So long as the result is another simple fork command, +// repeat this process. +// It must contain a fork command, which is currently restricted not to fork another +// zygote or involve a wrapper process. +// The initial buffer should be partially or entirely read; we read it fully and reset it. +// When we return, the buffer contains the command we couldn't handle, and has been reset(). +// We return false in the parent when we see a command we didn't understand, and thus the +// command in the buffer still needs to be executed. +// We return true in each child. +// We only process fork commands if the peer uid matches expected_uid. +// For every fork command after the first, we check that the requested uid is at +// least minUid. +NO_PAC_FUNC +jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly( + JNIEnv* env, + jclass, + jlong j_buffer, + jint zygote_socket_fd, + jint expected_uid, + jint minUid, + jstring managed_nice_name) { + + NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer); + int session_socket = n_buffer->getFd(); + std::vector<int> session_socket_fds {session_socket}; + auto fail_fn_1 = std::bind(ZygoteFailure, env, static_cast<const char*>(nullptr), + static_cast<jstring>(managed_nice_name), _1); + // This binds to the nice name address; the actual names are updated by isSimpleForkCommand: + auto fail_fn_n = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(), + static_cast<jstring>(nullptr), _1); + auto fail_fn_z = std::bind(ZygoteFailure, env, "zygote", nullptr, _1); + + struct pollfd fd_structs[2]; + static const int ZYGOTE_IDX = 0; + static const int SESSION_IDX = 1; + fd_structs[ZYGOTE_IDX].fd = zygote_socket_fd; + fd_structs[ZYGOTE_IDX].events = POLLIN; + fd_structs[SESSION_IDX].fd = session_socket; + fd_structs[SESSION_IDX].events = POLLIN; + + struct timeval timeout; + socklen_t timeout_size = sizeof timeout; + if (getsockopt(session_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, &timeout_size) != 0) { + fail_fn_z("Failed to retrieve session socket timeout"); + } + + struct ucred credentials; + socklen_t cred_size = sizeof credentials; + if (getsockopt(n_buffer->getFd(), SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1 + || cred_size != sizeof credentials) { + fail_fn_1("ForkMany failed to get initial credentials, %s", strerror(errno)); + } + + bool first_time = true; + do { + if (credentials.uid != expected_uid) { + return JNI_FALSE; + } + n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n); + n_buffer->reset(); + int pid = zygote::forkApp(env, /* no pipe FDs */ -1, -1, session_socket_fds, + /*args_known=*/ true, /*is_priority_fork=*/ true, + /*purge=*/ first_time); + if (pid == 0) { + return JNI_TRUE; + } + // We're in the parent. Write big-endian pid, followed by a boolean. + char pid_buf[5]; + int tmp_pid = pid; + for (int i = 3; i >= 0; --i) { + pid_buf[i] = tmp_pid & 0xff; + tmp_pid >>= 8; + } + pid_buf[4] = 0; // Process is not wrapped. + int res = write(session_socket, pid_buf, 5); + if (res != 5) { + if (res == -1) { + (first_time ? fail_fn_1 : fail_fn_n) + (CREATE_ERROR("Pid write error %d: %s", errno, strerror(errno))); + } else { + (first_time ? fail_fn_1 : fail_fn_n) + (CREATE_ERROR("Write unexpectedly returned short: %d < 5", res)); + } + } + // Clear buffer and get count from next command. + n_buffer->clear(); + for (;;) { + // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect. + int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */)); + if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) { + if (n_buffer->getCount(fail_fn_z) != 0) { + break; + } // else disconnected; + } else if (poll_res == 0 || (fd_structs[ZYGOTE_IDX].revents & POLLIN) == 0) { + fail_fn_z( + CREATE_ERROR("Poll returned with no descriptors ready! Poll returned %d", poll_res)); + } + // We've now seen either a disconnect or connect request. + close(session_socket); + int new_fd = accept(zygote_socket_fd, nullptr, nullptr); + if (new_fd == -1) { + fail_fn_z("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno)); + } + if (new_fd != session_socket) { + // Move new_fd back to the old value, so that we don't have to change Java-level data + // structures to reflect a change. This implicitly closes the old one. + if (dup2(new_fd, session_socket) != session_socket) { + fail_fn_z(CREATE_ERROR("Failed to move fd %d to %d: %s", + new_fd, session_socket, strerror(errno))); + } + close(new_fd); + } + // If we ever return, we effectively reuse the old Java ZygoteConnection. + // None of its state needs to change. + if (setsockopt(session_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, timeout_size) != 0) { + fail_fn_z(CREATE_ERROR("Failed to set receive timeout for socket %d: %s", + session_socket, strerror(errno))); + } + if (setsockopt(session_socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, timeout_size) != 0) { + fail_fn_z(CREATE_ERROR("Failed to set send timeout for socket %d: %s", + session_socket, strerror(errno))); + } + if (getsockopt(session_socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1) { + fail_fn_z(CREATE_ERROR("ForkMany failed to get credentials: %s", strerror(errno))); + } + if (cred_size != sizeof credentials) { + fail_fn_z(CREATE_ERROR("ForkMany credential size = %d, should be %d", + cred_size, static_cast<int>(sizeof credentials))); + } + } + first_time = false; + } while (n_buffer->isSimpleForkCommand(minUid, fail_fn_n)); + ALOGW("forkRepeatedly terminated due to non-simple command"); + n_buffer->logState(); + n_buffer->reset(); + return JNI_FALSE; +} + +#define METHOD_NAME(m) com_android_internal_os_ZygoteCommandBuffer_ ## m + +static const JNINativeMethod gMethods[] = { + {"getNativeBuffer", "(I)J", (void *) METHOD_NAME(getNativeBuffer)}, + {"freeNativeBuffer", "(J)V", (void *) METHOD_NAME(freeNativeBuffer)}, + {"insert", "(JLjava/lang/String;)V", (void *) METHOD_NAME(insert)}, + {"nativeNextArg", "(J)Ljava/lang/String;", (void *) METHOD_NAME(nativeNextArg)}, + {"nativeReadFullyAndReset", "(J)V", (void *) METHOD_NAME(nativeReadFullyAndReset)}, + {"nativeGetCount", "(J)I", (void *) METHOD_NAME(nativeGetCount)}, + {"nativeForkRepeatedly", "(JIIILjava/lang/String;)Z", + (void *) METHOD_NAME(nativeForkRepeatedly)}, +}; + +int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv* env) { + return RegisterMethodsOrDie(env, "com/android/internal/os/ZygoteCommandBuffer", gMethods, + NELEM(gMethods)); +} + +} // namespace android diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index c580b345d85b..ad1d25208a9a 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -282,7 +282,8 @@ message GlobalSettingsProto { optional SettingProto force_rtl = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto emulate_display_cutout = 5 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto force_desktop_mode_on_external_displays = 6 [ (android.privacy).dest = DEST_AUTOMATIC ]; - optional SettingProto enable_sizecompat_freeform = 7 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Deprecated, use enable_non_resizable_multi_window + optional SettingProto enable_sizecompat_freeform = 7 [ (android.privacy).dest = DEST_AUTOMATIC, deprecated = true ]; optional SettingProto enable_non_resizable_multi_window = 8 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Development development = 39; diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index d3c2d31779db..ec41a47a8798 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -627,6 +627,7 @@ message ActivityManagerServiceDumpProcessesProto { optional int64 duration_ms = 2; optional string tag = 3; optional int32 type = 4; + optional int32 reason_code = 5; } repeated PendingTempWhitelist pending_temp_whitelist = 26; diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto index 4b1ee02a6c30..f64e146f87b7 100644 --- a/core/proto/android/server/powerstatsservice.proto +++ b/core/proto/android/server/powerstatsservice.proto @@ -144,7 +144,7 @@ message StateResidencyProto { */ optional int64 total_state_entry_count = 3; /** - * Last time this state was entered. Time in milliseconds since boot + * Last time this state was entered. Walltime in milliseconds since Unix epoch. */ optional int64 last_entry_timestamp_ms = 4; } @@ -208,7 +208,7 @@ message EnergyConsumerResultProto { /** Unique index identifying the energy consumer. */ optional int32 id = 1; - /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */ + /** Walltime in milliseconds since Unix epoch */ optional int64 timestamp_ms = 2; /** Accumulated energy since device boot in microwatt-seconds (uWs) */ @@ -247,7 +247,7 @@ message EnergyMeasurementProto { */ optional int32 id = 1; - /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */ + /** Walltime in milliseconds since Unix epoch */ optional int64 timestamp_ms = 2; /** Accumulated energy since device boot in microwatt-seconds (uWs) */ diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index c882431c9366..ec502c3c272f 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -174,10 +174,10 @@ message DisplayContentProto { // Use root_display_area instead optional WindowContainerProto window_container = 1 [deprecated=true]; optional int32 id = 2; - reserved 3; // stacks - optional DockedStackDividerControllerProto docked_stack_divider_controller = 4 [deprecated=true]; + reserved 3; // RootTasks + optional DockedTaskDividerControllerProto docked_task_divider_controller = 4 [deprecated=true]; // Will be removed soon. - optional PinnedStackControllerProto pinned_stack_controller = 5 [deprecated=true]; + optional PinnedTaskControllerProto pinned_task_controller = 5 [deprecated=true]; /* non app windows */ repeated WindowTokenProto above_app_windows = 6 [deprecated=true]; repeated WindowTokenProto below_app_windows = 7 [deprecated=true]; @@ -256,15 +256,15 @@ message DisplayRotationProto { optional int32 last_orientation = 5 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"]; } -/* represents DockedStackDividerController */ -message DockedStackDividerControllerProto { +/* represents DockedTaskDividerController */ +message DockedTaskDividerControllerProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional bool minimized_dock = 1 [deprecated=true]; } -/* represents PinnedStackController */ -message PinnedStackControllerProto { +/* represents PinnedTaskController */ +message PinnedTaskControllerProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional .android.graphics.RectProto default_bounds = 1 [deprecated=true]; @@ -406,6 +406,8 @@ message WindowStateProto { optional int64 finished_seamless_rotation_frame = 40; optional WindowFramesProto window_frames = 41; optional bool force_seamless_rotation = 42; + optional bool in_size_compat_mode = 43; + optional float global_scale = 44; } message IdentifierProto { @@ -516,6 +518,7 @@ message WindowFramesProto { optional .android.graphics.RectProto visible_insets = 13 [deprecated=true]; optional .android.graphics.RectProto stable_insets = 14 [deprecated=true]; optional .android.graphics.RectProto outsets = 15; + optional .android.graphics.RectProto compat_frame = 16; } message InsetsSourceProviderProto { diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto index 8ebb4a9f6649..c8cdfddc3985 100644 --- a/core/proto/android/service/netstats.proto +++ b/core/proto/android/service/netstats.proto @@ -80,6 +80,8 @@ message NetworkIdentityProto { optional bool metered = 5; optional bool default_network = 6; + + optional int32 oem_managed_network = 7; } // Corresponds to NetworkStatsRecorder. diff --git a/core/res/Android.bp b/core/res/Android.bp index 9ee5e5e18185..b988b5a92af7 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -14,11 +14,103 @@ // limitations under the License. // +package { + default_applicable_licenses: ["frameworks_base_core_res_license"], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// See: http://go/android-license-faq +license { + name: "frameworks_base_core_res_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "SPDX-license-identifier-GPL", + ], + license_text: [ + "NOTICE", + ], +} + +genrule { + name: "remote-color-resources-compile-public", + tools: ["aapt2"], + srcs: [ + "remote_color_resources_res/values/public.xml", + ], + out: ["values_public.arsc.flat"], + cmd: "$(location aapt2) compile $(in) -o $(genDir)", +} + +genrule { + name: "remote-color-resources-compile-colors", + tools: ["aapt2"], + srcs: [ + "remote_color_resources_res/values/colors.xml", + ], + out: ["values_colors.arsc.flat"], + cmd: "$(location aapt2) compile $(in) -o $(genDir)", +} + +genrule { + name: "remote-color-resources-apk", + tools: ["aapt2"], + // The first input file in the list must be the manifest + srcs: [ + "RemoteThemeColorsAndroidManifest.xml", + ":remote-color-resources-compile-public", + ":remote-color-resources-compile-colors", + ], + out: ["remote-color-resources.apk"], + cmd: "$(location aapt2) link -o $(out) --manifest $(in)" +} + +genrule { + name: "remote-color-resources-arsc", + srcs: [":remote-color-resources-apk"], + out: ["res/raw/remote_views_color_resources.arsc"], + cmd: "mkdir -p $(genDir)/remote-color-resources-arsc && " + + "unzip -x $(in) resources.arsc -d $(genDir)/remote-color-resources-arsc && " + + "mkdir -p $$(dirname $(out)) && " + + "mv $(genDir)/remote-color-resources-arsc/resources.arsc $(out) && " + + "echo 'Created $(out)'" +} + +genrule { + name: "remote-color-resources-arsc-zip", + tools: ["soong_zip"], + srcs: [ + ":remote-color-resources-arsc", + "remote_color_resources_res/symbols.xml", + ], + out: ["remote_views_color_resources.zip"], + cmd: "INPUTS=($(in)) && " + + "RES_DIR=$$(dirname $$(dirname $${INPUTS[0]})) && " + + "mkdir -p $$RES_DIR/values && " + + "cp $${INPUTS[1]} $$RES_DIR/values && " + + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR && " + + "cp $(out) ." +} + android_app { name: "framework-res", sdk_version: "core_platform", certificate: "platform", + srcs: [":remote-color-resources-arsc"], + // Disable dexpreopt and verify_uses_libraries check as the app // contains no Java code to be dexpreopted. enforce_uses_libs: false, @@ -42,6 +134,10 @@ android_app { "--auto-add-overlay", ], + resource_zips: [ + ":remote-color-resources-arsc-zip", + ], + // Create package-export.apk, which other packages can use to get // PRODUCT-agnostic resource data like IDs and type definitions. export_package_resources: true, @@ -66,6 +162,7 @@ filegroup { srcs: [ "assets/**/*", "res/**/*", + ":remote-color-resources-arsc", ], } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 18cc3acd294e..d5f5d28aa7a2 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -690,6 +690,9 @@ <!-- Made protected in S (was added in R) --> <protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" /> + <!-- Added in S --> + <protected-broadcast android:name="android.intent.action.REBOOT_READY" /> + <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> <!-- ====================================================================== --> @@ -1281,15 +1284,15 @@ android:backgroundPermission="android.permission.RECORD_BACKGROUND_AUDIO" android:protectionLevel="dangerous|instant" /> - <!-- Allows an application to record audio while in the background. - <p>Protection level: dangerous - --> + <!-- @SystemApi @TestApi Allows an application to record audio while in the background. + This permission is not intended to be held by apps. + <p>Protection level: internal + @hide --> <permission android:name="android.permission.RECORD_BACKGROUND_AUDIO" android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_recordBackgroundAudio" android:description="@string/permdesc_recordBackgroundAudio" - android:permissionFlags="hardRestricted|installerExemptIgnored" - android:protectionLevel="dangerous" /> + android:protectionLevel="internal" /> <!-- ====================================================================== --> <!-- Permissions for activity recognition --> @@ -1365,15 +1368,15 @@ android:backgroundPermission="android.permission.BACKGROUND_CAMERA" android:protectionLevel="dangerous|instant" /> - <!-- Required to be able to access the camera device in the background. - <p>Protection level: dangerous - --> + <!-- @SystemApi @TestApi Required to be able to access the camera device in the background. + This permission is not intended to be held by apps. + <p>Protection level: internal + @hide --> <permission android:name="android.permission.BACKGROUND_CAMERA" android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_backgroundCamera" android:description="@string/permdesc_backgroundCamera" - android:permissionFlags="hardRestricted|installerExemptIgnored" - android:protectionLevel="dangerous" /> + android:protectionLevel="internal" /> <!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access system only camera devices. @@ -2638,6 +2641,16 @@ android:label="@string/permlab_manageProfileAndDeviceOwners" android:description="@string/permdesc_manageProfileAndDeviceOwners" /> + <!-- @TestApi @hide Allows an application to reset the record of previous system update freeze + periods. --> + <permission android:name="android.permission.CLEAR_FREEZE_PERIOD" + android:protectionLevel="signature" /> + + <!-- @TestApi @hide Allows an application to force available DevicePolicyManager logs to + DPC. --> + <permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" + android:protectionLevel="signature" /> + <!-- Allows an application to get full detailed information about recently running tasks, with full fidelity to the real state. @hide --> @@ -3905,6 +3918,12 @@ <permission android:name="android.permission.SET_KEYBOARD_LAYOUT" android:protectionLevel="signature" /> + <!-- Allows an app to use exact alarm scheduling APIs to perform timing + sensitive background work. + --> + <permission android:name="android.permission.SCHEDULE_EXACT_ALARM" + android:protectionLevel="normal|appop"/> + <!-- Allows an application to query tablet mode state and monitor changes in it. <p>Not for use by third-party applications. @@ -3963,16 +3982,12 @@ <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES" android:protectionLevel="signature|privileged" /> - <!-- Allows an application to use the package installer v2 APIs. - <p>The package installer v2 APIs are still a work in progress and we're - currently validating they work in all scenarios. + <!-- Allows an application to use System Data Loaders. <p>Not for use by third-party applications. - TODO(b/152310230): use this permission to protect only Incremental installations - once the APIs are confirmed to be sufficient. @hide --> - <permission android:name="com.android.permission.USE_INSTALLER_V2" - android:protectionLevel="signature|verifier" /> + <permission android:name="com.android.permission.USE_SYSTEM_DATA_LOADERS" + android:protectionLevel="signature" /> <!-- @SystemApi @TestApi Allows an application to clear user data. <p>Not for use by third-party applications @@ -5405,6 +5420,12 @@ @hide --> <permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" android:protectionLevel="signature" /> + + <!-- @SystemApi Allows sensor privacy changes to be observed. + @hide --> + <permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" + android:protectionLevel="signature|installer" /> + <!-- @SystemApi Permission that protects the {@link Intent#ACTION_REVIEW_ACCESSIBILITY_SERVICES} intent. @hide --> @@ -5492,11 +5513,11 @@ android:protectionLevel="signature" /> <!-- Must be required by a - {@link android.service.screenshot.ScreenshotHasherService} + {@link android.service.displayhash.DisplayHasherService} to ensure that only the system can bind to it. @hide This is not a third-party API (intended for OEMs and system apps). --> - <permission android:name="android.permission.BIND_SCREENSHOT_HASHER_SERVICE" + <permission android:name="android.permission.BIND_DISPLAY_HASHER_SERVICE" android:protectionLevel="signature" /> <!-- @hide @TestApi Allows an application to enable/disable toast rate limiting. diff --git a/core/res/RemoteThemeColorsAndroidManifest.xml b/core/res/RemoteThemeColorsAndroidManifest.xml new file mode 100644 index 000000000000..11970fd18979 --- /dev/null +++ b/core/res/RemoteThemeColorsAndroidManifest.xml @@ -0,0 +1,5 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android"> +<application/> +</manifest> + diff --git a/core/res/remote_color_resources_res/symbols.xml b/core/res/remote_color_resources_res/symbols.xml new file mode 100644 index 000000000000..82d5e3e2da6d --- /dev/null +++ b/core/res/remote_color_resources_res/symbols.xml @@ -0,0 +1,4 @@ +<resources> + <!-- ARSC file used to overlay local colors when rendering a RemoteViews --> + <java-symbol type="raw" name="remote_views_color_resources" /> +</resources> diff --git a/core/res/remote_color_resources_res/values/colors.xml b/core/res/remote_color_resources_res/values/colors.xml new file mode 100644 index 000000000000..295f16e959e6 --- /dev/null +++ b/core/res/remote_color_resources_res/values/colors.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Note: the values of the colors doesn't really matter (they will always be overwritten before used), but they help a lot debugging, to find out which color is where in the ARSC file. --> + <color name="system_primary_0">#01010101</color> + <color name="system_primary_50">#02020202</color> + <color name="system_primary_100">#03030303</color> + <color name="system_primary_200">#04040404</color> + <color name="system_primary_300">#05050505</color> + <color name="system_primary_400">#06060606</color> + <color name="system_primary_500">#07070707</color> + <color name="system_primary_600">#08080808</color> + <color name="system_primary_700">#09090909</color> + <color name="system_primary_800">#0a0a0a0a</color> + <color name="system_primary_900">#0b0b0b0b</color> + <color name="system_primary_1000">#0c0c0c0c</color> + <color name="system_secondary_0">#10101010</color> + <color name="system_secondary_50">#20202020</color> + <color name="system_secondary_100">#30303030</color> + <color name="system_secondary_200">#40404040</color> + <color name="system_secondary_300">#50505050</color> + <color name="system_secondary_400">#60606060</color> + <color name="system_secondary_500">#70707070</color> + <color name="system_secondary_600">#80808080</color> + <color name="system_secondary_700">#90909090</color> + <color name="system_secondary_800">#a0a0a0a0</color> + <color name="system_secondary_900">#b0b0b0b0</color> + <color name="system_secondary_1000">#c0c0c0c0</color> + <color name="system_neutral_0">#1f1f1f1f</color> + <color name="system_neutral_50">#2f2f2f2f</color> + <color name="system_neutral_100">#3f3f3f3f</color> + <color name="system_neutral_200">#4f4f4f4f</color> + <color name="system_neutral_300">#5f5f5f5f</color> + <color name="system_neutral_400">#6f6f6f6f</color> + <color name="system_neutral_500">#7f7f7f7f</color> + <color name="system_neutral_600">#8f8f8f8f</color> + <color name="system_neutral_700">#9f9f9f9f</color> + <color name="system_neutral_800">#afafafaf</color> + <color name="system_neutral_900">#bfbfbfbf</color> + <color name="system_neutral_1000">#cfcfcfcf</color> +</resources> diff --git a/core/res/remote_color_resources_res/values/public.xml b/core/res/remote_color_resources_res/values/public.xml new file mode 100644 index 000000000000..e628f09b8327 --- /dev/null +++ b/core/res/remote_color_resources_res/values/public.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <public-group type="color" first-id="0x0106001d"> + <public name="system_primary_0" /> + <public name="system_primary_50" /> + <public name="system_primary_100" /> + <public name="system_primary_200" /> + <public name="system_primary_300" /> + <public name="system_primary_400" /> + <public name="system_primary_500" /> + <public name="system_primary_600" /> + <public name="system_primary_700" /> + <public name="system_primary_800" /> + <public name="system_primary_900" /> + <public name="system_primary_1000" /> + <public name="system_secondary_0" /> + <public name="system_secondary_50" /> + <public name="system_secondary_100" /> + <public name="system_secondary_200" /> + <public name="system_secondary_300" /> + <public name="system_secondary_400" /> + <public name="system_secondary_500" /> + <public name="system_secondary_600" /> + <public name="system_secondary_700" /> + <public name="system_secondary_800" /> + <public name="system_secondary_900" /> + <public name="system_secondary_1000" /> + <public name="system_neutral_0" /> + <public name="system_neutral_50" /> + <public name="system_neutral_100" /> + <public name="system_neutral_200" /> + <public name="system_neutral_300" /> + <public name="system_neutral_400" /> + <public name="system_neutral_500" /> + <public name="system_neutral_600" /> + <public name="system_neutral_700" /> + <public name="system_neutral_800" /> + <public name="system_neutral_900" /> + <public name="system_neutral_1000" /> + </public-group> +</resources> diff --git a/core/res/res/drawable/toast_frame.xml b/core/res/res/drawable/toast_frame.xml index d57bd6a554e1..44c00c0521b4 100644 --- a/core/res/res/drawable/toast_frame.xml +++ b/core/res/res/drawable/toast_frame.xml @@ -17,8 +17,7 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> - <!-- background is material_grey_200 with .9 alpha --> - <solid android:color="#E6EEEEEE" /> - <corners android:radius="22dp" /> + <solid android:color="?android:attr/colorBackground" /> + <corners android:radius="28dp" /> </shape> diff --git a/core/res/res/layout/notification_template_conversation_header.xml b/core/res/res/layout/notification_template_conversation_header.xml index b018676e68f0..e01d8035fe35 100644 --- a/core/res/res/layout/notification_template_conversation_header.xml +++ b/core/res/res/layout/notification_template_conversation_header.xml @@ -99,13 +99,26 @@ android:visibility="gone" /> + <TextView + android:id="@+id/verification_divider" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" + android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" + android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" + android:text="@string/notification_header_divider_symbol" + android:layout_gravity="center" + android:paddingTop="1sp" + android:singleLine="true" + android:visibility="gone" + /> + <ImageView android:id="@+id/verification_icon" - android:layout_width="@dimen/notification_badge_size" - android:layout_height="@dimen/notification_badge_size" + android:layout_width="@dimen/notification_verification_icon_size" + android:layout_height="@dimen/notification_verification_icon_size" android:layout_gravity="center" android:layout_marginStart="4dp" - android:contentDescription="@string/notification_alerted_content_description" android:paddingTop="2dp" android:scaleType="fitCenter" android:src="@drawable/ic_notifications_alerted" @@ -140,6 +153,19 @@ /> <ImageView + android:id="@+id/phishing_alert" + android:layout_width="@dimen/notification_phishing_alert_size" + android:layout_height="@dimen/notification_phishing_alert_size" + android:layout_marginStart="4dp" + android:paddingTop="2dp" + android:scaleType="fitCenter" + android:src="@drawable/ic_dialog_alert_material" + android:visibility="gone" + android:contentDescription="@string/notification_phishing_alert_content_description" + /> + + + <ImageView android:id="@+id/profile_badge" android:layout_width="@dimen/notification_badge_size" android:layout_height="@dimen/notification_badge_size" diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index 88998f2167a8..1de1d049197c 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -17,8 +17,8 @@ <NotificationHeaderView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/notification_header" - android:layout_width="wrap_content" - android:layout_height="@dimen/notification_header_solo_height" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_header_height" android:layout_marginBottom="@dimen/notification_header_margin_bottom" android:clipChildren="false" android:gravity="center_vertical" @@ -33,6 +33,7 @@ android:layout_gravity="center_vertical|start" android:layout_marginStart="@dimen/notification_left_icon_start" android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" android:importantForAccessibility="no" android:scaleType="centerCrop" android:visibility="gone" diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml index 41be36bc2b04..b83611bcc177 100644 --- a/core/res/res/layout/notification_template_material_base.xml +++ b/core/res/res/layout/notification_template_material_base.xml @@ -30,6 +30,7 @@ android:layout_gravity="center_vertical|start" android:layout_marginStart="@dimen/notification_left_icon_start" android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" android:importantForAccessibility="no" android:scaleType="centerCrop" android:visibility="gone" @@ -54,6 +55,7 @@ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" android:layout_marginEnd="@dimen/notification_header_expand_icon_size" android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" android:importantForAccessibility="no" android:scaleType="centerCrop" /> @@ -89,26 +91,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginBottom="@dimen/notification_headerless_margin_minimum" - android:layout_marginTop="@dimen/notification_headerless_margin_minimum" + android:layout_marginBottom="@dimen/notification_headerless_margin_twoline" + android:layout_marginTop="@dimen/notification_headerless_margin_twoline" android:orientation="vertical" > - <!-- - This invisible FrameLayout is here as a collapsible padding. Having a layout_weight=1 is - what causes this view (and it's counterpart at the opposite end) to collapse before the - actual content views do. - This pair of 10dp collapsible paddings (plus the 16dp fixed margins) allow us to support - headerless notifications of 1-3 lines (where each line is 20dp tall) where the 1-line - variant is 56dp and the 2- and 3-line variants are both 76dp. - --> - <FrameLayout - android:id="@+id/notification_headerless_margin_extra_top" - android:layout_width="match_parent" - android:layout_height="@dimen/notification_headerless_margin_extra" - android:layout_weight="1" - /> - <!-- extends ViewGroup --> <NotificationTopLineView android:id="@+id/notification_top_line" @@ -151,12 +138,20 @@ android:orientation="vertical" > - <include - layout="@layout/notification_template_text" + <com.android.internal.widget.NotificationVanishingFrameLayout android:layout_width="match_parent" android:layout_height="@dimen/notification_headerless_line_height" - android:layout_marginTop="0dp" - /> + > + <!-- This is the simplest way to keep this text vertically centered without using + gravity="center_vertical" which causes jumpiness in expansion animations. --> + <include + layout="@layout/notification_template_text" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_text_height" + android:layout_gravity="center_vertical" + android:layout_marginTop="0dp" + /> + </com.android.internal.widget.NotificationVanishingFrameLayout> <include layout="@layout/notification_template_progress" @@ -166,21 +161,6 @@ </LinearLayout> - <!-- - This invisible FrameLayout is here as a collapsible padding. Having a layout_weight=1 is - what causes this view (and it's counterpart at the opposite end) to collapse before the - actual content views do. - This pair of 10dp collapsible paddings (plus the 16dp fixed margins) allow us to support - headerless notifications of 1-3 lines (where each line is 20dp tall) where the 1-line - variant is 56dp and the 2- and 3-line variants are both 76dp. - --> - <FrameLayout - android:id="@+id/notification_headerless_margin_extra_bottom" - android:layout_width="match_parent" - android:layout_height="@dimen/notification_headerless_margin_extra" - android:layout_weight="1" - /> - </LinearLayout> </com.android.internal.widget.NotificationMaxHeightFrameLayout> diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml index de537b205866..2d1c3422ca36 100644 --- a/core/res/res/layout/notification_template_material_big_base.xml +++ b/core/res/res/layout/notification_template_material_big_base.xml @@ -38,11 +38,7 @@ android:layout_gravity="top" > - <include - layout="@layout/notification_template_header" - android:layout_width="match_parent" - android:layout_height="@dimen/notification_header_big_height" - /> + <include layout="@layout/notification_template_header" /> <LinearLayout android:id="@+id/notification_main_column" diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml index 696cb6572e29..bdd4430a7985 100644 --- a/core/res/res/layout/notification_template_material_big_media.xml +++ b/core/res/res/layout/notification_template_material_big_media.xml @@ -36,7 +36,6 @@ layout="@layout/notification_template_header" android:layout_width="match_parent" android:layout_height="@dimen/media_notification_header_height" - android:layout_gravity="start" /> <LinearLayout diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml index 25d396f94b2c..6f3c77ff72a4 100644 --- a/core/res/res/layout/notification_template_material_big_picture.xml +++ b/core/res/res/layout/notification_template_material_big_picture.xml @@ -23,11 +23,7 @@ android:clipChildren="false" > - <include - layout="@layout/notification_template_header" - android:layout_width="match_parent" - android:layout_height="@dimen/notification_header_big_height" - /> + <include layout="@layout/notification_template_header" /> <include layout="@layout/notification_template_right_icon" /> @@ -72,6 +68,7 @@ android:layout_marginStart="@dimen/notification_content_margin_start" android:layout_marginEnd="@dimen/notification_content_margin_end" android:background="@drawable/notification_big_picture_outline" + android:clipToOutline="true" android:scaleType="centerCrop" /> diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml index 2452a32b21eb..2954ba2a0903 100644 --- a/core/res/res/layout/notification_template_material_big_text.xml +++ b/core/res/res/layout/notification_template_material_big_text.xml @@ -23,11 +23,7 @@ android:tag="bigText" > - <include - layout="@layout/notification_template_header" - android:layout_width="match_parent" - android:layout_height="@dimen/notification_header_big_height" - /> + <include layout="@layout/notification_template_header" /> <com.android.internal.widget.RemeasuringLinearLayout android:id="@+id/notification_action_list_margin_target" diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml index 7daccd2b8544..baffdd5ac0f1 100644 --- a/core/res/res/layout/notification_template_material_media.xml +++ b/core/res/res/layout/notification_template_material_media.xml @@ -32,7 +32,8 @@ /> <include layout="@layout/notification_template_header" android:layout_width="match_parent" - android:layout_height="@dimen/media_notification_header_height" /> + android:layout_height="@dimen/media_notification_header_height" + /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/core/res/res/layout/notification_template_right_icon.xml b/core/res/res/layout/notification_template_right_icon.xml index d22d4c20dad2..f163ed5f955a 100644 --- a/core/res/res/layout/notification_template_right_icon.xml +++ b/core/res/res/layout/notification_template_right_icon.xml @@ -22,6 +22,7 @@ android:layout_marginEnd="@dimen/notification_header_expand_icon_size" android:layout_marginTop="@dimen/notification_right_icon_big_margin_top" android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" android:importantForAccessibility="no" android:scaleType="centerCrop" /> diff --git a/core/res/res/layout/notification_top_line_views.xml b/core/res/res/layout/notification_top_line_views.xml index 7656dd50b2d4..88bcc4dc9e39 100644 --- a/core/res/res/layout/notification_top_line_views.xml +++ b/core/res/res/layout/notification_top_line_views.xml @@ -123,6 +123,18 @@ /> <ImageView + android:id="@+id/phishing_alert" + android:layout_width="@dimen/notification_phishing_alert_size" + android:layout_height="@dimen/notification_phishing_alert_size" + android:layout_marginStart="4dp" + android:baseline="10dp" + android:scaleType="fitCenter" + android:src="@drawable/ic_dialog_alert_material" + android:visibility="gone" + android:contentDescription="@string/notification_phishing_alert_content_description" + /> + + <ImageView android:id="@+id/profile_badge" android:layout_width="@dimen/notification_badge_size" android:layout_height="@dimen/notification_badge_size" diff --git a/core/res/res/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml index 513da5e431e5..e6d724f1ecb2 100644 --- a/core/res/res/layout/splash_screen_view.xml +++ b/core/res/res/layout/splash_screen_view.xml @@ -21,14 +21,16 @@ android:orientation="vertical"> <View android:id="@+id/splashscreen_icon_view" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:layout_gravity="center"/> + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_gravity="center" + android:contentDescription="@string/splash_screen_view_icon_description"/> <View android:id="@+id/splashscreen_branding_view" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_gravity="center_horizontal|bottom" - android:layout_marginBottom="60dp"/> + android:layout_marginBottom="60dp" + android:contentDescription="@string/splash_screen_view_branding_description"/> </android.window.SplashScreenView>
\ No newline at end of file diff --git a/core/res/res/layout/transient_notification.xml b/core/res/res/layout/transient_notification.xml index db586ec37cf1..8fcb77ff4ebd 100644 --- a/core/res/res/layout/transient_notification.xml +++ b/core/res/res/layout/transient_notification.xml @@ -18,24 +18,27 @@ */ --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:background="?android:attr/toastFrameBackground"> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical" + android:maxWidth="@dimen/toast_width" + android:background="?android:attr/toastFrameBackground" + android:layout_marginEnd="16dp" + android:layout_marginStart="16dp" + android:paddingStart="16dp" + android:paddingEnd="16dp"> <TextView android:id="@android:id/message" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_marginHorizontal="24dp" - android:layout_marginVertical="15dp" - android:layout_gravity="center_horizontal" - android:textAppearance="@style/TextAppearance.Toast" - android:textColor="@color/primary_text_default_material_light" - /> - + android:ellipsize="end" + android:maxLines="2" + android:paddingTop="12dp" + android:paddingBottom="12dp" + android:lineHeight="20sp" + android:textAppearance="@style/TextAppearance.Toast"/> </LinearLayout> - - diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 55eaaf648f84..4b591bf63e7b 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Hierdie toetstel het nie \'n vingerafdruksensor nie."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor is tydelik gedeaktiveer."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gebruik jou vingerafdruk om voort te gaan"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Vingerafdrukikoon"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Kan nie meer gesig herken nie. Probeer weer."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Te eenders. Verander asseblief jou pose."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Draai jou kop \'n bietjie minder."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Draai jou kop \'n bietjie minder."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Draai jou kop \'n bietjie minder."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Verwyder enigiets wat jou gesig versteek."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Maak die bokant van jou skerm skoon, insluitend die swart balk"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Laat die program toe om Moenie Steur Nie-opstelling te lees en skryf."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"begin kyk van toestemminggebruik"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Laat die houer toe om die toestemminggebruik vir \'n program te begin. Behoort nooit vir normale programme nodig te wees nie."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"kry toegang tot sensordata teen \'n hoë monsternemingkoers"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Laat die program toe om monsters van sensordata teen \'n hoër koers as 200 Hz te neem"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Stel wagwoordreëls"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Beheer die lengte en die karakters wat in skermslotwagwoorde en -PIN\'e toegelaat word."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor pogings om skerm te ontsluit"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gebruik kortpad"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Kleuromkering"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Kleurkorreksie"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aangeskakel."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is afgeskakel"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Druk en hou albei volumesleutels drie sekondes lank om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruik"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-versoek is na video-oproep toe verander"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-versoek is na USSD-versoek toe verander"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Na nuwe SS-versoek toe verander"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Uitvissingwaarskuwing"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Werkprofiel"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Kennisgewing gegee"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Vou uit"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maksimeer"</string> <string name="close_button_text" msgid="10603510034455258">"Maak toe"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Antwoord"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Wys af"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Lui af"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Inkomende oproep"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Oproep aan die gang"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Keur tans \'n inkomende oproep"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> gekies</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> gekies</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Om voort te gaan, moet <b><xliff:g id="APP">%s</xliff:g></b> toegang tot jou toestel se kamera hê."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Skakel aan"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorprivaatheid"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 316122669352..a43b29a56131 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም።"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ዳሳሽ ለጊዜው ተሰናክሏል።"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ጣት <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ለመቀጠል የእርስዎን የጣት አሻራ ይጠቀሙ"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"የጣት አሻራ አዶ"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"ከእንግዲህ ፊትን ለይቶ ማወቅ አይችልም። እንደገና ይሞክሩ።"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"በጣም ይመሳሰላል፣ እባክዎ የእርስዎን ፎቶ አነሳስ ይለውጡ"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"ጭንቅላትዎን ትንሽ ብቻ ያዙሩት።"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"ጭንቅላትዎን ትንሽ ብቻ ያዙሩት።"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"ጭንቅላትዎን ትንሽ ብቻ ያዙሩት።"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"የእርስዎን ፊት የሚደብቀውን ሁሉንም ነገር በማስወገድ ላይ"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"የማያ ገጽዎን አናት ያጽዱት፣ ጥቁር አሞሌውን ጨምሮ"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"መተግበሪያው የአትረብሽ ውቅረትን እንዲያነብብ እና እንዲጸፍ ይፈቅዳል።"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"የእይታ ፈቃድ መጠቀምን መጀመር"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ያዢው ለአንድ መተግበሪያ የፈቃድ አጠቃቀሙን እንዲያስጀምር ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ ሊያስፈልግ አይገባም።"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"የዳሳሽ ውሂቡን በከፍተኛ የናሙና ብዛት ላይ ይድረሱበት"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"መተግበሪያው የዳሳሽ ውሂቡን ከ200 ኸ በሚበልጥ ፍጥነት ናሙና እንዲያደርግ ይፈቅድለታል"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"የይለፍ ቃል ደንቦች አዘጋጅ"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"በማያ ገጽ መቆለፊያ የይለፍ ቃሎች እና ፒኖች ውስጥ የሚፈቀዱ ቁምፊዎችን እና ርዝመታቸውን ተቆጣጠር።"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"የማሳያ-ክፈት ሙከራዎችን ክትትል ያድርጉባቸው"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"አቋራጭ ይጠቀሙ"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"ተቃራኒ ቀለም"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"የቀለም ማስተካከያ"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"የድምፅ ቁልፎችን ይዟል። <xliff:g id="SERVICE_NAME">%1$s</xliff:g> በርቷል።"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"የድምፅ ቁልፎችን ይዟል። <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ጠፍተዋል።"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን ለመጠቀም ለሦስት ሰከንዶች ሁለቱንም የድምፅ ቁልፎች ተጭነው ይያዙ"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"የSS ጥያቄ ወደ የቪዲዮ ጥሪ ተለውጧል"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"የSS ጥያቄ ወደ የUSSD ጥያቄ ተለውጧል"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"ወደ አዲስ የSS ጥያቄ ተለውጧል"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"የማስገር ማንቂያ"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"የስራ መገለጫ"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"ነቅተዋል"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ዘርጋ"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"አስፋ"</string> <string name="close_button_text" msgid="10603510034455258">"ዝጋ"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>፦ <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"መልስ"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"አትቀበል"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ስልኩን ዝጋ"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"ገቢ ጥሪ"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"እየተካሄደ ያለ ጥሪ"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"ገቢ ጥሪ ማጣራት"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ተመርጧል</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ተመርጠዋል</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ለመቀጠል፣ <b><xliff:g id="APP">%s</xliff:g></b> የመሣሪያዎን ካሜራ መድረስ ይፈልጋል።"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"አብራ"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ዳሳሽ ግላዊነት"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index a2d3671d667c..3990bab4447b 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -152,8 +152,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi فقط"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> الاتصال الاحتياطي"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: لم تتم إعادة التوجيه"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانية"</string> @@ -592,6 +591,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"لا يحتوي هذا الجهاز على مستشعِر بصمات إصبع."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"تم إيقاف جهاز الاستشعار مؤقتًا."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"الإصبع <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"يمكنك استخدام بصمة الإصبع للمتابعة."</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"رمز بصمة الإصبع"</string> @@ -618,7 +618,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"لم يعُد يمكن التعرّف على الوجه. حاول مرة أخرى."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"الوجه مشابه جدًا، يُرجى تغيير وضعيتك."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"حرّك رأسك قليلاً نحو الأمام مباشرة."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"حرّك رأسك قليلاً نحو الأمام مباشرة."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"حرّك رأسك قليلاً نحو الوسط."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"عليك بإزالة أي شيء يُخفي وجهك."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"يُرجى تنظيف الجزء العلوي من الشاشة، بما في ذلك الشريط الأسود."</string> @@ -697,6 +698,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"للسماح للتطبيق بقراءة إعداد \"عدم الإزعاج\" وكتابتها."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"بدء استخدام إذن العرض"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"للسماح للمالك ببدء استخدام الإذن لأحد التطبيقات. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"الوصول إلى بيانات جهاز الاستشعار بمعدّل مرتفع للبيانات في الملف الصوتي"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"يسمح هذا الأذن للتطبيق بزيادة بيانات جهاز الاستشعار بمعدّل بيانات في الملف الصوتي أكبر من 200 هرتز."</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"تعيين قواعد كلمة المرور"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"للتحكم في الطول والأحرف المسموح بها في كلمات المرور وأرقام التعريف الشخصي في قفل الشاشة."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"مراقبة محاولات فتح قفل الشاشة"</string> @@ -1753,6 +1756,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استخدام الاختصار"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"قلب الألوان"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"تصحيح الألوان"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم تفعيل <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم إيقاف <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"اضغط مع الاستمرار على مفتاحي مستوى الصوت لمدة 3 ثوانٍ لاستخدام <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> @@ -1992,6 +1997,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"تم تغيير طلب SS إلى مكالمة فيديو."</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"تم تغيير طلب SS إلى طلب USSD."</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"تم التغيير إلى طلب SS الجديد."</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"تنبيه بشأن تصيّد احتيالي"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"الملف الشخصي للعمل"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"تمّ تفعيل التنبيه"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"توسيع"</string> @@ -2005,6 +2011,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"تكبير"</string> <string name="close_button_text" msgid="10603510034455258">"إغلاق"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"ردّ"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"رفض"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"قطع الاتصال"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"مكالمة واردة"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"مكالمة جارية"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"رصد مكالمة واردة"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="zero">تم اختيار <xliff:g id="COUNT_1">%1$d</xliff:g> عنصر</item> <item quantity="two">تم اختيار عنصرين (<xliff:g id="COUNT_1">%1$d</xliff:g>)</item> @@ -2345,4 +2357,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"للمتابعة، يحتاج تطبيق <b><xliff:g id="APP">%s</xliff:g></b> إلى الوصول إلى كاميرا الجهاز."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"تفعيل"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"الخصوصية في جهاز الاستشعار"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 0a9ef97c2ad8..28af8a3b24e0 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"কোৱল ৱাই-ফাই"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> বেকআপ কলিং"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফৰৱাৰ্ড কৰা নহ\'ল"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ছেকেণ্ডৰ পাছত"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই।"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ছেন্সৰটো সাময়িকভাৱে অক্ষম হৈ আছে।"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> আঙুলি"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"অব্যাহত ৰাখিবলৈ আপোনাৰ ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ফিংগাৰপ্ৰিণ্ট আইকন"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"মুখমণ্ডল আৰু চিনাক্ত কৰিব নোৱাৰি। আকৌ চেষ্টা কৰক।"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"একে ধৰণৰ হৈছে, অনুগ্ৰহ কৰি আপোনাৰ প’জটো সলনি কৰক।"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"আপোনাৰ মূৰটো সামান্য কমকৈ ঘূৰাওক।"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"আপোনাৰ মূৰটো সামান্য কমকৈ ঘূৰাওক।"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"আপোনাৰ মূৰটো সামান্য কমকৈ ঘূৰাওক।"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"আপোনাৰ মুখখন ঢাকি ৰখা বস্তুবোৰ আঁতৰাওক।"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"ক’লা বাৰডালকে ধৰি আপোনাৰ স্ক্রীণৰ ওপৰৰ অংশ চাফা কৰক"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"অসুবিধা নিদিবৰ কনফিগাৰেশ্বনক পঢ়িবলৈ আৰু সালসলনি কৰিবলৈ এপটোক অনুমতি দিয়ে।"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"চোৱাৰ অনুমতিৰ ব্যৱহাৰ আৰম্ভ কৰক"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ধাৰকক কোনো এপৰ বাবে অনুমতিৰ ব্যৱহাৰ আৰম্ভ কৰিবলৈ দিয়ে। সাধাৰণ এপ্সমূহৰ বাবে কেতিয়াও প্ৰয়োজন হ’ব নালাগে।"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"এটা উচ্চ ছেম্পলিঙৰ হাৰত ছেন্সৰৰ ডেটা এক্সেছ কৰে"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"এপ্টোক ২০০ হাৰ্টজতকৈ অধিক হাৰত ছেন্সৰৰ ডেটাৰ নমুনা ল’বলৈ অনুমতি দিয়ে"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"পাছৱর্ডৰ নিয়ম ছেট কৰক"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"স্ক্ৰীণ লক পাছৱৰ্ড আৰু পিনৰ দৈর্ঘ্য আৰু কি কি আখৰ ব্যৱহাৰ কৰিব পাৰে তাক নিয়ন্ত্ৰণ কৰক।"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"স্ক্ৰীণ আনলক কৰা প্ৰয়াসবোৰ পৰ্যবেক্ষণ কৰিব পাৰে"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"শ্বৰ্টকাট ব্যৱহাৰ কৰক"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"ৰং বিপৰীতকৰণ"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"ৰং শুধৰণী"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কীসমূহ ধৰি ৰাখক। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অন কৰা হ\'ল।"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধৰি ৰাখিছিল। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অফ কৰা হ\'ল।"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ব্যৱহাৰ কৰিবলৈ দুয়োটা ভলিউম বুটাম তিনি ছেকেণ্ডৰ বাবে হেঁচি ৰাখক"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS অনুৰোধ ভিডিঅ\' কললৈ সলনি কৰা হ’ল"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS অনুৰোধ USSD অনুৰোধলৈ সলনি কৰা হ’ল"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"নতুন SS অনুৰোধলৈ সলনি কৰা হ’ল"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ফিশ্বিঙৰ সতৰ্কবাৰ্তা"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"সতৰ্ক কৰা হ’ল"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"বিস্তাৰ কৰক"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"সৰ্বাধিক মাত্ৰালৈ বঢ়াওক"</string> <string name="close_button_text" msgid="10603510034455258">"বন্ধ কৰক"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"উত্তৰ দিয়ক"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"প্ৰত্যাখ্যান কৰক"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"কল কাটি দিয়ক"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"অন্তৰ্গামী কল"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"চলি থকা কল"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"এটা অন্তৰ্গামী কলৰ পৰীক্ষা কৰি থকা হৈছে"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টা বাছনি কৰা হ’ল</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টা বাছনি কৰা হ’ল</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"অব্যাহত ৰাখিবলৈ <b><xliff:g id="APP">%s</xliff:g></b>এ আপোনাৰ ডিভাইচৰ কেমেৰা এক্সেছ কৰাৰ আৱশ্যক।"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"অন কৰক"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ছেন্সৰ সম্পৰ্কীয় গোপনীয়তাৰ নীতি"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index e91e2b740543..8459403ff291 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -325,7 +325,7 @@ <string name="permgroupdesc_sensors" msgid="2610631290633747752">"Həyati əlamətlər haqqında sensor dataya daxil olun"</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">"Toxunaraq Kəşf et funksiyasını yandırın"</string> + <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Toxunuşla öyrənmə funksiyasını aktiv edin"</string> <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Tıklanan hissələr səsləndiriləcək və ekran jestlərlə idarə oluna biləcək."</string> <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Yazdığınız mətni izləyin"</string> <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Kredit kartı nömrələri və parollar kimi şəxsi məlumatlar daxildir."</string> @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda barmaq izi sensoru yoxdur."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor müvəqqəti deaktivdir."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Barmaq <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Davam etmək üçün barmaq izinizi istifadə edin"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Barmaq izi ikonası"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Üzü artıq tanımaq olmur. Yenidən cəhd edin."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Digəri ilə oxşardır, pozanızı dəyişin."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Başınızı bir az döndərin."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Başınızı bir az döndərin."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Başınızı bir az döndərin."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Üzünüzü gizlədən maneələri kənarlaşdırın."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Qara panel daxil olmaqla, ekranın yuxarısını təmizləyin"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Tətbiqə \"Narahat Etməyin\" konfiqurasiyasını oxumağa və yazmağa icazə verin."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Baxış icazəsinin istifadəsinə başlayın"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Sahibinə tətbiqin icazədən istifadəsinə başlamağa imkan verir. Adi tətbiqlər üçün heç vaxt tələb edilmir."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"sensor datasına yüksək ölçmə sürəti ilə giriş etmək"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Tətbiqə sensor datasını 200 Hz-dən yüksək sürətlə ölçməyə imkan verir"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Parol qaydalarını təyin edin"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Ekran kilidinin parolu və PINlərində icazə verilən uzunluq və simvollara nəzarət edin."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Ekranı kiliddən çıxarmaq üçün edilən cəhdlərə nəzarət edin"</string> @@ -1629,7 +1633,7 @@ <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"Android TV cihazını kiliddən çıxarmaq üçün <xliff:g id="NUMBER">%d</xliff:g> dəfə yanlış cəhd etdiniz. Android TV cihazınız defolt fabrik dəyərlərinə sıfırlanacaq."</string> <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Siz telefonun kilidini açmaq üçün <xliff:g id="NUMBER">%d</xliff:g> yanlış cəhd etmisiniz. Telefon artıq defolt zavod halına sıfırlanacaq."</string> <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"Siz kilidi açmaq üçün şablonu <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə səhv çəkdiniz. <xliff:g id="NUMBER_1">%2$d</xliff:g> daha uğursuz cəhddən sonra planşetinizin kilidini e-poçt hesabınızla açmaq tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə ərzində bir daha yoxlayın."</string> - <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Kiliddən çıxarma modelini <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış çəkdiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> yanlış cəhddən sonra Android TV cihazını e-poçt hesabınızla kiliddən çıxarmağınız tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə sonra yenidən cəhd edin."</string> + <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Kiliddən çıxarma modelini <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış çəkdiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> yanlış cəhddən sonra Android TV cihazını e-poçt hesabınızla kiliddən çıxarmağınız tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə sonra cəhd edin."</string> <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Siz artıq modeli <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış daxil etmisiniz.<xliff:g id="NUMBER_1">%2$d</xliff:g> dəfə də yanlış daxil etsəniz, telefonun kilidinin açılması üçün elektron poçt ünvanınız tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə ərzində yenidən cəhd edin."</string> <string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" - "</string> <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Yığışdır"</string> @@ -1637,10 +1641,10 @@ <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Əlçatımlılıq Qısayolu istifadə edilsin?"</string> <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Qısayol aktiv olduqda, hər iki səs düyməsinə 3 saniyə basıb saxlamaqla əlçatımlılıq funksiyası başladılacaq."</string> <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Əlçatımlılıq funksiyaları üçün qısayol aktiv edilsin?"</string> - <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyaları aktiv olur. Bu, cihazınızın işləmə qaydasını dəyişə bilər.\n\nCari funksiyalar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAyarlar və Əlçatımlılıq bölməsində seçilmiş funksiyaları dəyişə bilərsiniz."</string> + <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyaları aktiv olur. Cihazınızın işləmə qaydasını dəyişə bilər.\n\nCari funksiyalar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAyarlar və Əlçatımlılıq bölməsində seçilmiş funksiyaları dəyişə bilərsiniz."</string> <string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string> <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"<xliff:g id="SERVICE">%1$s</xliff:g> qısayolu aktiv edilsin?"</string> - <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyası olan <xliff:g id="SERVICE">%1$s</xliff:g> aktiv olur. Bu, cihazınızın işləmə qaydasını dəyişə bilər.\n\nAyarlar və Əlçatımlılıq bölməsində bu qısayolu başqa bir funksiyata dəyişə bilərsiniz."</string> + <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyası olan <xliff:g id="SERVICE">%1$s</xliff:g> aktiv olur. Cihazınızın işləmə qaydasını dəyişə bilər.\n\nAyarlar və Əlçatımlılıq bölməsində bu qısayolu başqa bir funksiyaya dəyişə bilərsiniz."</string> <string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktiv edin"</string> <string name="accessibility_shortcut_off" msgid="3651336255403648739">"Aktiv etməyin"</string> <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"AKTİV"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Qısayol İstifadə edin"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Rəng İnversiyası"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Rəng korreksiyası"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Səs səviyyəsi düymələrinə basıb saxlayın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiv edildi."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Səs səviyyəsi düymələrinə basılaraq saxlanıb. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> deaktiv edilib."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> istifadə etmək üçün hər iki səs düyməsini üç saniyə basıb saxlayın"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS sorğusu video zəngə dəyişdirildi"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS sorğusu USSD sorğusuna dəyişdirildi"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Yeni SS sorğusuna dəyişdirildi"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Fişinq siqnalı"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"İş profili"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Xəbərdarlıq edildi"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Genişləndirin"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Böyüdün"</string> <string name="close_button_text" msgid="10603510034455258">"Qapadın"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Cavab verin"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"İmtina edin"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Dəstəyi asın"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Gələn zəng"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Davam edən zəng"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Gələn zəng göstərilir"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> seçilib</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> seçilib</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Davam etmək üçün <b><xliff:g id="APP">%s</xliff:g></b> tətbiqi cihazın kamerasına giriş tələb edir."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktiv edin"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensor Məxfiliyi"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index a490a6ce2b7d..a7f6433b4977 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -582,6 +582,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Nastavite pomoću otiska prsta"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otiska prsta"</string> @@ -608,7 +609,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Više ne može da se prepozna lice. Probajte ponovo."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Previše je slično, promenite pozu."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Malo manje pomerite glavu."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Malo manje pomerite glavu."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Malo manje pomerite glavu."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Uklonite sve što vam zaklanja lice."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Očistite gornji deo ekrana, uključujući crnu traku"</string> @@ -687,6 +689,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Dozvoljava aplikaciji da čita i upisuje konfiguraciju podešavanja Ne uznemiravaj."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"početak korišćenja dozvole za pregled"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dozvoljava vlasniku da započne korišćenje dozvole za aplikaciju. Nikada ne bi trebalo da bude potrebna za uobičajene aplikacije."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora pri velikoj brzini uzorkovanja"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Dozvoljava aplikaciji da uzima uzorak podataka senzora pri brzini većoj od 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Podešavanje pravila za lozinku"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontroliše dužinu i znakove dozvoljene u lozinkama i PIN-ovima za zaključavanje ekrana."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Nadgledajte pokušaje otključavanja ekrana"</string> @@ -1686,6 +1690,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Koristi prečicu"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcija boja"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite i zadržite oba tastera za jačinu zvuka tri sekunde da biste koristili <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1898,6 +1904,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS zahtev je promenjen u video poziv"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS zahtev je promenjen u USSD zahtev"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Promenjeno je u novi SS zahtev"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Upozorenje o „pecanju“"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Poslovni profil"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Obavešteno"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Proširi"</string> @@ -1911,6 +1918,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Uvećaj"</string> <string name="close_button_text" msgid="10603510034455258">"Zatvori"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Odgovori"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Odbij"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini vezu"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Dolazni poziv"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Poziv je u toku"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Proverava se dolazni poziv"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one">Izabrana je <xliff:g id="COUNT_1">%1$d</xliff:g> stavka</item> <item quantity="few">Izabrane su <xliff:g id="COUNT_1">%1$d</xliff:g> stavke</item> @@ -2242,4 +2255,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"<b><xliff:g id="APP">%s</xliff:g></b> zahteva pristup kameri uređaja radi nastavljanja."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Uključi"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privatnost senzora"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 80f67d07a0d2..1022cef7fbc8 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -150,8 +150,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Толькі Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Рэзервовае капіраванне выклікаў праз аператара \"<xliff:g id="SPN">%s</xliff:g>\""</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не пераадрасоўваецца"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> праз <xliff:g id="TIME_DELAY">{2}</xliff:g> с."</string> @@ -586,6 +585,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На гэтай прыладзе няма сканера адбіткаў пальцаў."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчык часова выключаны."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Палец <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Каб працягнуць, выкарыстоўвайце свой адбітак пальца"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок адбіткаў пальцаў"</string> @@ -612,7 +612,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Не ўдаецца распазнаць твар. Паўтарыце спробу."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Не бачна розніцы. Памяняйце позу."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Вы занадта моцна павярнулі галаву."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Галава не ў цэнтры."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Вы занадта моцна павярнулі галаву."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Прыміце ўсё, што закрывае ваш твар."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Ачысціце ад бруду верхнюю частку экрана, у тым ліку чорную панэль"</string> @@ -691,6 +692,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дазваляе праграме чытаць і выконваць запіс у канфігурацыю рэжыму «Не турбаваць»."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"запусціць выкарыстанне дазволаў на прагляд"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дазваляе трымальніку запусціць выкарыстанне дазволаў праграмай. Не патрэбна для звычайных праграм."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"атрымліваць даныя датчыка з высокай частатой дыскрэтызацыі"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Праграма зможа распазнаваць даныя датчыка з частатой звыш 200 Гц"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Устанавіць правілы паролю"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Кіраваць даўжынёй і сімваламі, дазволенымі пры ўводзе пароляў і PIN-кодаў блакіроўкі экрана."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Сачыць за спробамі разблакіроўкі экрана"</string> @@ -791,7 +794,7 @@ <string name="eventTypeAnniversary" msgid="4684702412407916888">"Гадавіна"</string> <string name="eventTypeOther" msgid="530671238533887997">"Іншае"</string> <string name="emailTypeCustom" msgid="1809435350482181786">"Карыстальніцкая"</string> - <string name="emailTypeHome" msgid="1597116303154775999">"Хатні"</string> + <string name="emailTypeHome" msgid="1597116303154775999">"Асабістая"</string> <string name="emailTypeWork" msgid="2020095414401882111">"Працоўная"</string> <string name="emailTypeOther" msgid="5131130857030897465">"Іншая"</string> <string name="emailTypeMobile" msgid="787155077375364230">"Мабільны"</string> @@ -1709,6 +1712,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Выкарыстоўваць камбінацыю хуткага доступу"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія колеру"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Карэкцыя колеру"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Клавішы гучнасці ўтрымліваліся націснутымі. Уключана служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Клавішы гучнасці ўтрымліваліся націснутымі. Служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" выключана."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Каб карыстацца сэрвісам \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", націсніце і ўтрымлівайце на працягу трох секунд абедзве клавішы гучнасці"</string> @@ -1930,6 +1935,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-запыт заменены на відэавыклік"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-запыт заменены на USSD-запыт"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Зроблена замена на новы SS-запыт"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Абвестка пра фішынг"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Працоўны профіль"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"З гукам"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Разгарнуць"</string> @@ -1943,6 +1949,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Разгарнуць"</string> <string name="close_button_text" msgid="10603510034455258">"Закрыць"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Адказаць"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Адхіліць"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Завяршыць"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Уваходны выклік"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Бягучы выклік"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Фільтраванне ўваходнага выкліку"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> выбраны</item> <item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> выбрана</item> @@ -2277,4 +2289,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Каб працягнуць, дайце праграме <b><xliff:g id="APP">%s</xliff:g></b> доступ да камеры прылады."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Уключыць"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Прыватнасць інфармацыі з датчыка"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 20342e07953d..ef20bade3275 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Това устройство няма сензор за отпечатъци."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорът е временно деактивиран."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Пръст <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Използвайте отпечатъка си, за да продължите"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона за отпечатък"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Лицето не бе разпознато. Опитайте отново."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Позата ви е сходна с предишна. Моля, променете я."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Не завъртайте главата си толкова много."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Не завъртайте главата си толкова много."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Не завъртайте главата си толкова много."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Премахнете всичко, което закрива лицето ви."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Почистете горната част на екрана си, включително черната лента"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Предоставя на приложението достъп за четене и запис до конфигурацията на „Не безпокойте“."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"стартиране на прегледа на използваните разрешения"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Разрешава на притежателя да стартира прегледа на използваните разрешения за дадено приложение. Нормалните приложения би трябвало никога да не се нуждаят от това."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"осъществяване на достъп до данните от сензорите при висока скорост на семплиране"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Разрешава на приложението да семплира данните от сензорите със скорост, по-висока от 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Задаване на правила за паролата"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролира дължината и разрешените знаци за паролите и ПИН кодовете за заключване на екрана."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Наблюдаване на опитите за отключване на екрана"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Използване на пряк път"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Инвертиране на цветовете"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Коригиране на цветовете"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Задържахте бутоните за силата на звука. Услугата <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е включена."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Задържахте бутоните за силата на звука. Услугата <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е изключена."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"За да използвате <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, натиснете двата бутона за силата на звука и ги задръжте за 3 секунди"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS заявката е променена на видеообаждане"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS заявката е променена на USSD заявка"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Променено на нова SS заявка"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Сигнал за фишинг"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Служебен потребителски профил"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Сигналът е изпратен"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Разгъване"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Увеличаване"</string> <string name="close_button_text" msgid="10603510034455258">"Затваряне"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"„<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>“: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Отговор"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Отхвърляне"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Затваряне"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Входящо обаждане"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Текущо обаждане"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Преглежда се входящо обаждане"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other">Избрахте <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="one">Избрахте <xliff:g id="COUNT_0">%1$d</xliff:g></item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"За да продължите, <b><xliff:g id="APP">%s</xliff:g></b> се нуждае от достъп до камерата на устройството ви."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Включване"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Поверителност на сензорните данни"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 51d09b27afe6..0e30c046e2f8 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"শুধুমাত্র ওয়াই-ফাই"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ব্যাক-আপ কলিং"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফরওয়ার্ড করা হয়নি"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> সেকেন্ড পরে"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইসে আঙ্গুলের ছাপ নেওয়ার সেন্সর নেই।"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"সেন্সর অস্থায়ীভাবে বন্ধ করা আছে।"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"আঙ্গুল <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"চালিয়ে যেতে আঙ্গুলের ছাপ ব্যবহার করুন"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"আঙ্গুলের ছাপ আইকন"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"আর মুখ চিনতে পারবেন না। আবার চেষ্টা করুন।"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"একই ধরনের দেখতে, একটু অন্যদিকে ঘুরে দাঁড়ান।"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"আপনার মাথাটি নিচের দিকে সামান্য নামান।"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"আপনার মাথাটি নিচের দিকে সামান্য নামান।"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"আপনার মাথাটি সামান্য ঘোরান।"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"আপনার ফেসকে আড়াল করে এমন সব কিছু সরিয়ে দিন।"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"ব্ল্যাক বার সহ আপনার স্ক্রিনের উপরের অংশ মুছে ফেলুন"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"অ্যাপটিকে \'বিরক্ত করবে না\' কনফিগারেশন পড়া এবং লেখার অনুমতি দেয়।"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"দেখার অনুমতি কাজে লাগানো শুরু করুন"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"কোনও অ্যাপের কোনও নির্দিষ্ট অনুমতির ব্যবহার শুরু করার ক্ষেত্রে হোল্ডারকে সাহায্য করে। সাধারণ অ্যাপের জন্য এটির পরিবর্তন হওয়ার কথা নয়।"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"হাই স্যাম্পলিং রেটে সেন্সর ডেটা অ্যাক্সেস করুন"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz-এর বেশি রেটে অ্যাপকে স্যাম্পল সেন্সর ডেটার জন্য অনুমতি দিন"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"পাসওয়ার্ড নিয়মগুলি সেট করে"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"স্ক্রিন লক করার পাসওয়ার্ডগুলিতে অনুমতিপ্রাপ্ত অক্ষর এবং দৈর্ঘ্য নিয়ন্ত্রণ করে৷"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"স্ক্রিন আনলক করার প্রচেষ্টাগুলির উপরে নজর রাখুন"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"শর্টকাট ব্যবহার করুন"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"রঙ উল্টানো"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"রঙ সংশোধন"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> চালু করা হয়েছে।"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> বন্ধ করা হয়েছে।"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ব্যবহার করতে ভলিউম কী বোতাম ৩ সেকেন্ডের জন্য চেপে ধরে রাখুন"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS অনুরোধ ভিডিও কলে পরিবর্তন করা হয়েছে"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS অনুরোধ USSD অনুরোধে পরিবর্তন করা হয়েছে"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"নতুন SS অনুরোধে পরিবর্তন করা হয়েছে"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ফিশিংয়ের সতর্কতা"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"কর্মস্থলের প্রোফাইল"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"সতর্ক করা হয়েছে"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"বড় করুন"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"বড় করুন"</string> <string name="close_button_text" msgid="10603510034455258">"বন্ধ করুন"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"উত্তর"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"বাতিল করুন"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"কল কেটে দেওয়া"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"ইনকামিং কল"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"চালু থাকা কল"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"ইনকামিং কল স্ক্রিনিং করা হচ্ছে"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টি নির্বাচন করা হয়েছে</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টি নির্বাচন করা হয়েছে</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"চালিয়ে যেতে, <b><xliff:g id="APP">%s</xliff:g></b> আপনার ডিভাইসের ক্যামেরা অ্যাক্সেস করতে চায়।"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"চালু করুন"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"সেন্সর গোপনীয়তা"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 989e2bb7b598..77b64b52e04a 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -582,6 +582,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Nastavite pomoću otiska prsta"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona za otisak prsta"</string> @@ -608,7 +609,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Više nije moguće prepoznati lice. Pokušajte opet."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Previše slično, promijenite položaj."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Malo manje zakrenite glavu."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Malo manje zakrenite glavu."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Malo manje zakrenite glavu."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Uklonite prepreke koje blokiraju vaše lice."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Očistite vrh ekrana, uključujući crnu traku"</string> @@ -687,6 +689,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Omogućava aplikaciji da čita i upisuje konfiguraciju načina rada Ne ometaj."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pokrenuti korištenje odobrenja za pregled"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dozvoljava vlasniku da pokrene korištenje odobrenja za aplikaciju. Ne bi trebalo biti potrebno za obične aplikacije."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora velikom brzinom uzorkovanja"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Dozvoljava aplikaciji da uzorkuje podatke senzora brzinom većom od 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Postavljanje pravila za lozinke"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolira dužinu i znakove koji su dozvoljeni u lozinkama za zaključavanje ekrana i PIN-ovima."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Prati pokušaje otključavanja ekrana"</string> @@ -1686,6 +1690,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Koristi prečicu"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Ispravka boja"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite obje tipke za podešavanje jačine zvuka i držite ih pritisnutim tri sekunde da koristite uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1898,6 +1904,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS zahtjev je promijenjen u video poziv"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS zahtjev je promijenjen u USSD zahtjev"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Promijenjeno u novi SS zahtjev"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Upozorenje o krađi identiteta"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil za posao"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Upozoreni"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Proširi"</string> @@ -1911,6 +1918,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Povećaj maksimalno"</string> <string name="close_button_text" msgid="10603510034455258">"Zatvori"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Odgovori"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Odbaci"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini vezu"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Dolazni poziv"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Poziv u toku"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtriranje dolaznog poziva"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> stavka je odabrana</item> <item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> stavke su odabrane</item> @@ -2242,4 +2255,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Da nastavite, aplikaciji <b><xliff:g id="APP">%s</xliff:g></b> je potreban pristup kameri vašeg uređaja."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Uključi"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privatnost senzora"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index e9c55d76c6ac..a226ac3bc3c2 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -538,10 +538,10 @@ <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_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 dactilars"</string> + <string name="permlab_manageFingerprint" msgid="7432667156322821178">"Gestionar el maquinari d\'empremtes digitals"</string> <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Permet que l\'aplicació invoqui mètodes per afegir i suprimir plantilles d\'empremtes dactilars que es puguin fer servir."</string> - <string name="permlab_useFingerprint" msgid="1001421069766751922">"Utilitzar el maquinari d\'empremtes dactilars"</string> - <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permet que l\'aplicació faci servir maquinari d\'empremtes dactilars per a l\'autenticació"</string> + <string name="permlab_useFingerprint" msgid="1001421069766751922">"Utilitzar el maquinari d\'empremtes digitals"</string> + <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permet que l\'aplicació faci servir maquinari d\'empremtes digitals per a l\'autenticació"</string> <string name="permlab_audioWrite" msgid="8501705294265669405">"modificar la teva col·lecció de música"</string> <string name="permdesc_audioWrite" msgid="8057399517013412431">"Permet que l\'aplicació modifiqui la teva col·lecció de música."</string> <string name="permlab_videoWrite" msgid="5940738769586451318">"modificar la teva col·lecció de vídeos"</string> @@ -567,7 +567,7 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"L\'empremta digital s\'ha autenticat"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Cara autenticada"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Cara autenticada; prem el botó per confirmar"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El maquinari per a empremtes dactilars no està disponible."</string> + <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El maquinari d\'empremtes digitals no està disponible."</string> <string name="fingerprint_error_no_space" msgid="6126456006769817485">"L\'empremta digital no es pot desar. Suprimeix-ne una."</string> <string name="fingerprint_error_timeout" msgid="2946635815726054226">"S\'ha esgotat el temps d\'espera per a l\'empremta digital. Torna-ho a provar."</string> <string name="fingerprint_error_canceled" msgid="540026881380070750">"S\'ha cancel·lat l\'operació d\'empremta digital."</string> @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aquest dispositiu no té sensor d\'empremtes dactilars."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor està desactivat temporalment."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dit <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Fes servir l\'empremta digital per continuar"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona d\'empremta digital"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Ja no es reconeix la teva cara. Torna-ho a provar."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"És massa semblant; canvia de postura."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"No giris tant el cap."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"No inclinis tant el cap."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"No giris tant el cap."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Suprimeix qualsevol cosa que amagui la teva cara."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Neteja la part superior de la pantalla, inclosa la barra negra"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet que l\'aplicació llegeixi la configuració No molestis i hi escrigui."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"comença a utilitzar el permís de visualització"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet que un propietari comenci a utilitzar el permís amb una aplicació. No s\'hauria de necessitar mai per a les aplicacions normals."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accedir a les dades del sensor a una freqüència de mostratge alta"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permet que l\'aplicació dugui a terme un mostratge de les dades del sensor a una freqüència superior a 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Definir les normes de contrasenya"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Permet controlar la longitud i el nombre de caràcters permesos a les contrasenyes i als PIN del bloqueig de pantalla."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisar els intents de desbloqueig de la pantalla"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilitza la drecera"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversió de colors"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Correcció de color"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S\'han mantingut premudes les tecles de volum. S\'ha activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S\'han mantingut premudes les tecles de volum. S\'ha desactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén premudes les dues tecles de volum durant 3 segons per fer servir <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"La sol·licitud SS s\'ha canviat per una videotrucada"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"La sol·licitud SS s\'ha canviat per una sol·licitud USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"S\'ha canviat a una nova sol·licitud SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerta de pesca de credencials"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de treball"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"S\'ha enviat una alerta"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Desplega"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximitza"</string> <string name="close_button_text" msgid="10603510034455258">"Tanca"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Respon"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Rebutja"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Penja"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Trucada entrant"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Trucada en curs"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"S\'està filtrant una trucada entrant"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other">Seleccionats: <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="one">Seleccionats: <xliff:g id="COUNT_0">%1$d</xliff:g></item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Per continuar, <b><xliff:g id="APP">%s</xliff:g></b> necessita accedir a la càmera del dispositiu."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Activa"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privadesa dels sensors"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 071bbfd69ce5..fdb2ff1b83f2 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -585,6 +585,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zařízení nemá snímač otisků prstů."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasně deaktivován."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Pokračujte přiložením prstu"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otisku prstů"</string> @@ -611,7 +612,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Obličej už nelze rozpoznat. Zkuste to znovu."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Příliš podobné, změňte výraz."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Natočte hlavu o něco méně."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Natočte hlavu o něco méně."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Natočte hlavu o něco méně."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Odstraňte vše, co vám zakrývá obličej."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Očistěte horní část obrazovky včetně černé části"</string> @@ -690,6 +692,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Umožňuje aplikaci číst a zapisovat konfiguraci režimu Nerušit."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"zahájení zobrazení využití oprávnění"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umožňuje přístup zahájit využití oprávnění jiné aplikace. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"přístup k datům ze senzorů s vyšší vzorkovací frekvencí"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Umožňuje aplikaci vzorkovat data ze senzorů s frekvencí vyšší než 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Nastavit pravidla pro heslo"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Ovládání délky a znaků povolených v heslech a kódech PIN zámku obrazovky."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Sledovat pokusy o odemknutí obrazovky"</string> @@ -1708,6 +1712,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Použít zkratku"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Převrácení barev"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Oprava barev"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> byla vypnuta."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Chcete-li používat službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, tři sekundy podržte stisknutá obě tlačítka hlasitosti"</string> @@ -1929,6 +1935,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Požadavek SS byl změněn na videohovor"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Požadavek SS byl změněn na požadavek USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Změněno na nový požadavek SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Upozornění na phishing"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Pracovní profil"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Upozorněno"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Rozbalit"</string> @@ -1942,6 +1949,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximalizovat"</string> <string name="close_button_text" msgid="10603510034455258">"Zavřít"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Přijmout"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Odmítnout"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Zavěsit"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Příchozí hovor"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Probíhající hovor"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Prověřování příchozího hovoru"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> položky</item> <item quantity="many"><xliff:g id="COUNT_1">%1$d</xliff:g> položky</item> @@ -2276,4 +2289,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Než budete pokračovat, udělte aplikaci <b><xliff:g id="APP">%s</xliff:g></b> přístup k fotoaparátu na zařízení."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Zapnout"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Ochrana soukromí – senzor"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index b760f5b1c16e..dddc07037201 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -148,7 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Kun Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Alternativ løsning til opkald leveret af <xliff:g id="SPN">%s</xliff:g>"</string> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g>-opkald via alternativt SIM"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderestillet"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> efter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string> @@ -581,6 +581,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enhed har ingen fingeraftrykslæser."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidigt deaktiveret."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Fingeraftryk <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Brug dit fingeraftryk for at fortsætte"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon for fingeraftryk"</string> @@ -607,7 +608,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Ansigtet kan ikke længere genkendes. Prøv igen."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Det minder for meget om et andet. Skift stilling."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Du skal ikke dreje hovedet så meget."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Du skal ikke dreje hovedet så meget."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Du skal ikke dreje hovedet så meget."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Hvis noget skjuler dit ansigt, skal du fjerne det."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Rengør toppen af din skærm, inkl. den sorte bjælke"</string> @@ -686,6 +688,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Giver appen tilladelse til at læse og redigere konfigurationen af Forstyr ikke."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start brugen at tilladelsesvisning"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Tillader, at brugeren kan bruge en tilladelse for en app. Dette bør aldrig være nødvendigt for almindelige apps."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"få adgang til sensordata ved høj samplingfrekvens"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Tillader, at appen kan sample sensordata ved en højere frekvens end 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Angiv regler for adgangskoder"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Tjek længden samt tilladte tegn i adgangskoder og pinkoder til skærmlåsen."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Overvåg forsøg på oplåsning af skærm"</string> @@ -1666,6 +1670,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Brug genvej"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Ombytning af farver"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Korriger farve"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er aktiveret."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er deaktiveret."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Hold begge lydstyrkeknapper nede i tre sekunder for at bruge <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1869,6 +1875,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-anmodningen blev ændret til et videoopkald"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-anmodningen blev ændret til en USSD-anmodning"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Ændret til en SS-anmodning"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishingadvarsel"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Arbejdsprofil"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Underrettet"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Udvid"</string> @@ -1882,6 +1889,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maksimér"</string> <string name="close_button_text" msgid="10603510034455258">"Luk"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Besvar"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Afvis"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Læg på"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Indgående opkald"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Igangværende opkald"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Et indgående opkald screenes"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>valgt</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> valgt</item> @@ -2210,4 +2223,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"<b><xliff:g id="APP">%s</xliff:g></b> skal have adgang til din enheds kamera, før den kan fortsætte."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktivér"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Beskyttelse af sensoroplysninger"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index f8066e2bfedf..472f1d216f15 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Nur WLAN"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g>-Anruf über Ersatz-SIM"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nicht weitergeleitet"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> nach <xliff:g id="TIME_DELAY">{2}</xliff:g> Sekunden."</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dieses Gerät hat keinen Fingerabdrucksensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Der Sensor ist vorübergehend deaktiviert."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Mithilfe deines Fingerabdrucks fortfahren"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerabdruck-Symbol"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Gesicht wird nicht mehr erkannt. Erneut versuchen."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Zu ähnlich. Bitte dreh deinen Kopf etwas."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Dreh den Kopf etwas weniger zur Seite."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Neig den Kopf etwas weniger stark."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Neig den Kopf etwas weniger stark."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Entferne alles, was dein Gesicht verdeckt."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Reinige den oberen Teil deines Bildschirms, einschließlich der schwarzen Leiste"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ermöglicht der App Lese- und Schreibzugriff auf die \"Bitte nicht stören\"-Konfiguration"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Mit der Verwendung der Anzeigeberechtigung beginnen"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ermöglicht dem Inhaber, die Berechtigungsnutzung für eine App zu beginnen. Sollte für normale Apps nie benötigt werden."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Sensordaten mit hoher Frequenz auslesen"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Erlaubt der App, Sensordaten mit einer Frequenz von mehr als 200 Hz auszulesen"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Passwortregeln festlegen"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Zulässige Länge und Zeichen für Passwörter für die Displaysperre festlegen"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Versuche zum Entsperren des Displays überwachen"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Verknüpfung verwenden"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Farbumkehr"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Farbkorrektur"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist aktiviert."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist deaktiviert."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Halten Sie beide Lautstärketasten drei Sekunden lang gedrückt, um <xliff:g id="SERVICE_NAME">%1$s</xliff:g> zu verwenden"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-Anfrage wurde in Videoanruf geändert"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-Anfrage wurde in USSD-Anfrage geändert"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"In neue SS-Anfrage geändert"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing-Warnung"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Arbeitsprofil"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Gewarnt"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Maximieren"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximieren"</string> <string name="close_button_text" msgid="10603510034455258">"Schließen"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Annehmen"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Ablehnen"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Auflegen"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Eingehender Anruf"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Aktueller Anruf"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Screening für eingehenden Anruf"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ausgewählt</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ausgewählt</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Zum Fortfahren benötigt <b><xliff:g id="APP">%s</xliff:g></b> Zugriff auf die Kamera deines Geräts."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktivieren"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Datenschutz für Sensoren"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 4f3c8a95a469..22b3401cf89b 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Ο αισθητήρας απενεργοποιήθηκε προσωρινά."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Δάχτυλο <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Χρησιμοποιήστε το δακτυλικό αποτύπωμά σας για να συνεχίσετε"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Εικονίδιο δακτυλικών αποτυπωμάτων"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Αδύνατη η αναγνώριση του προσώπου. Επανάληψη."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Πολύ παρόμοιο, αλλάξτε την πόζα σας."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Στρέψτε λιγότερο το κεφάλι σας."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Στρέψτε λιγότερο το κεφάλι σας."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Στρέψτε λιγότερο το κεφάλι σας."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Απομακρύνετε οτιδήποτε κρύβει το πρόσωπό σας."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Καθαρίστε το επάνω μέρος της οθόνης σας, συμπεριλαμβανομένης της μαύρης γραμμής"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Επιτρέπει στην εφαρμογή την εγγραφή και τη σύνταξη διαμόρφωσης για τη λειτουργία \"Μην ενοχλείτε\"."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"έναρξη χρήσης άδειας προβολής"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Επιτρέπει στον κάτοχο να ξεκινήσει τη χρήση της άδειας για μια εφαρμογή. Δεν απαιτείται ποτέ για κανονικές εφαρμογές."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"πρόσβαση σε δεδομένα αισθητήρα με υψηλό ρυθμό δειγματοληψίας"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Επιτρέπει στην εφαρμογή τη δειγματοληψία των δεδομένων αισθητήρα με ρυθμό μεγαλύτερο από 200 Hz."</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Ορισμός κανόνων κωδικού πρόσβασης"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Ελέγξτε την έκταση και τους επιτρεπόμενους χαρακτήρες σε κωδικούς πρόσβασης κλειδώματος οθόνης και PIN."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Παρακολούθηση προσπαθειών ξεκλειδώματος οθόνης"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Χρήση συντόμευσης"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Αντιστροφή χρωμάτων"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Διόρθωση χρωμάτων"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Τα πλήκτρα έντασης είναι πατημένα. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ενεργοποιήθηκε."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Τα πλήκτρα έντασης είναι πατημένα. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>: απενεργοποιημένο"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Πατήστε παρατεταμένα και τα δύο κουμπιά έντασης ήχου για τρία δευτερόλεπτα, ώστε να χρησιμοποιήσετε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Το αίτημα SS τροποποιήθηκε σε βιντεοκλήση"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Το αίτημα SS τροποποιήθηκε σε αίτημα USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Τροποποιήθηκε σε νέο αίτημα SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Ειδοποίηση ηλεκτρονικού ψαρέματος (phishing)"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Προφίλ εργασίας"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Ειδοποιήθηκε"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Ανάπτυξη"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Μεγιστοποίηση"</string> <string name="close_button_text" msgid="10603510034455258">"Κλείσιμο"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Απάντηση"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Απόρριψη"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Τερματισμός"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Εισερχόμενη κλήση"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Κλήση σε εξέλιξη"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Διαλογή εισερχόμενης κλήσης"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other">Επιλέχτηκαν <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="one">Επιλέχτηκε <xliff:g id="COUNT_0">%1$d</xliff:g></item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Για να συνεχίσετε, η εφαρμογή <b><xliff:g id="APP">%s</xliff:g></b> χρειάζεται πρόσβαση στην κάμερα της συσκευής σας."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Ενεργοποίηση"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Απόρρητο αισθητήρα"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 9f3bc7de2385..f2cbb75f6580 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string> @@ -605,7 +606,7 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"No longer able to recognise face. Try again."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Too similar, please change your pose."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Turn your head a little less."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Turn your head a little less."</string> + <string name="face_acquired_tilt_too_extreme" msgid="8618210742620248049">"Tilt your head a little less."</string> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Turn your head a little less."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Remove anything hiding your face."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Clean the top of your screen, including the black bar"</string> @@ -684,6 +685,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Control the length and the characters allowed in screen lock passwords and PINs."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string> @@ -1664,6 +1667,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string> + <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduce brightness"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1871,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS request changed to video call"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS request changed to USSD request"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Changed to new SS request"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing alert"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Work profile"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerted"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expand"</string> @@ -1880,6 +1885,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximise"</string> <string name="close_button_text" msgid="10603510034455258">"Close"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang up"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"On-going call"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item> @@ -2208,4 +2219,6 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"To continue, <b><xliff:g id="APP">%s</xliff:g></b> needs access to your device’s camera."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Turn on"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensor privacy"</string> + <string name="splash_screen_view_icon_description" msgid="180638751260598187">"Application icon"</string> + <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Application branding image"</string> </resources> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 766e372927d6..f440c8ac2263 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string> @@ -605,7 +606,7 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"No longer able to recognise face. Try again."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Too similar, please change your pose."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Turn your head a little less."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Turn your head a little less."</string> + <string name="face_acquired_tilt_too_extreme" msgid="8618210742620248049">"Tilt your head a little less."</string> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Turn your head a little less."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Remove anything hiding your face."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Clean the top of your screen, including the black bar"</string> @@ -684,6 +685,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Control the length and the characters allowed in screen lock passwords and PINs."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string> @@ -1664,6 +1667,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Colour inversion"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string> + <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduce brightness"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1871,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS request changed to video call"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS request changed to USSD request"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Changed to new SS request"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing alert"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Work profile"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerted"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expand"</string> @@ -1880,6 +1885,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximise"</string> <string name="close_button_text" msgid="10603510034455258">"Close"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang up"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"On-going call"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item> @@ -2208,4 +2219,6 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"To continue, <b><xliff:g id="APP">%s</xliff:g></b> needs access to your device’s camera."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Turn on"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensor privacy"</string> + <string name="splash_screen_view_icon_description" msgid="180638751260598187">"Application icon"</string> + <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Application branding image"</string> </resources> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 16da2116ed40..93881e992d84 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string> @@ -605,7 +606,7 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"No longer able to recognise face. Try again."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Too similar, please change your pose."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Turn your head a little less."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Turn your head a little less."</string> + <string name="face_acquired_tilt_too_extreme" msgid="8618210742620248049">"Tilt your head a little less."</string> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Turn your head a little less."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Remove anything hiding your face."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Clean the top of your screen, including the black bar"</string> @@ -684,6 +685,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Control the length and the characters allowed in screen lock passwords and PINs."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string> @@ -1664,6 +1667,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Colour correction"</string> + <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduce brightness"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1871,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS request changed to video call"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS request changed to USSD request"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Changed to new SS request"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing alert"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Work profile"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerted"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expand"</string> @@ -1880,6 +1885,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximise"</string> <string name="close_button_text" msgid="10603510034455258">"Close"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang up"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"On-going call"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item> @@ -2208,4 +2219,6 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"To continue, <b><xliff:g id="APP">%s</xliff:g></b> needs access to your device’s camera."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Turn on"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensor privacy"</string> + <string name="splash_screen_view_icon_description" msgid="180638751260598187">"Application icon"</string> + <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Application branding image"</string> </resources> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 150830ef027d..a7fa30ebc2d9 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string> @@ -605,7 +606,7 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"No longer able to recognise face. Try again."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Too similar, please change your pose."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Turn your head a little less."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Turn your head a little less."</string> + <string name="face_acquired_tilt_too_extreme" msgid="8618210742620248049">"Tilt your head a little less."</string> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Turn your head a little less."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Remove anything hiding your face."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Clean the top of your screen, including the black bar"</string> @@ -684,6 +685,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Control the length and the characters allowed in screen lock passwords and PINs."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string> @@ -1664,6 +1667,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Colour Inversion"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Color correction"</string> + <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduce brightness"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1871,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS request changed to video call"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS request changed to USSD request"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Changed to new SS request"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing alert"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Work profile"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerted"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expand"</string> @@ -1880,6 +1885,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximise"</string> <string name="close_button_text" msgid="10603510034455258">"Close"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang up"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"On-going call"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item> @@ -2208,4 +2219,6 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"To continue, <b><xliff:g id="APP">%s</xliff:g></b> needs access to your device’s camera."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Turn on"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensor privacy"</string> + <string name="splash_screen_view_icon_description" msgid="180638751260598187">"Application icon"</string> + <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Application branding image"</string> </resources> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index e952cac93ca6..ce9a915f9c2a 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use your fingerprint to continue"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string> @@ -605,7 +606,7 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"No longer able to recognize face. Try again."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Too similar, please change your pose."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Turn your head a little less."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Turn your head a little less."</string> + <string name="face_acquired_tilt_too_extreme" msgid="8618210742620248049">"Tilt your head a little less."</string> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Turn your head a little less."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Remove anything hiding your face."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Clean the top of your screen, including the black bar"</string> @@ -684,6 +685,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Allows the app to read and write Do Not Disturb configuration."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start view permission usage"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Allows the holder to start the permission usage for an app. Should never be needed for normal apps."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"access sensor data at a high sampling rate"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Allows the app to sample sensor data at a rate greater than 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Set password rules"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Control the length and the characters allowed in screen lock passwords and PINs."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitor screen unlock attempts"</string> @@ -1664,6 +1667,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Use Shortcut"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Color Inversion"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Color Correction"</string> + <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Reduce brightness"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Press and hold both volume keys for three seconds to use <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1871,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS request changed to video call"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS request changed to USSD request"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Changed to new SS request"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing alert"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Work profile"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerted"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expand"</string> @@ -1880,6 +1885,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximize"</string> <string name="close_button_text" msgid="10603510034455258">"Close"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Answer"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Decline"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Hang Up"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Incoming call"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Ongoing call"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Screening an incoming call"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item> @@ -2208,4 +2219,6 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"To continue, <b><xliff:g id="APP">%s</xliff:g></b> needs access to your device’s camera."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Turn on"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensor Privacy"</string> + <string name="splash_screen_view_icon_description" msgid="180638751260598187">"Application icon"</string> + <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Application branding image"</string> </resources> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index c5ff193bc6a2..01d0c7e06c73 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas digitales."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Se inhabilitó temporalmente el sensor."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utiliza tu huella digital para continuar"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícono de huella digital"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Ya no se reconoce el rostro. Vuelve a intentarlo."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Es muy similar a la anterior. Haz otra pose."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Gira la cabeza un poco menos."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Gira la cabeza un poco menos."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Gira la cabeza un poco menos."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Quítate cualquier objeto que te cubra el rostro."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Limpia la parte superior de la pantalla, incluida la barra negra"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite que la aplicación lea y modifique la configuración de la función No interrumpir."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de vista"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que el propietario inicie el uso de permisos para una app. No debería requerirse para apps normales."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Acceder a los datos del sensor a una tasa de muestreo alta"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que la app tome una muestra de los datos del sensor a una tasa superior a 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Establecer reglas de contraseña"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Controlar la longitud y los caracteres permitidos en las contraseñas y los PIN para el bloqueo de pantalla."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisa los intentos para desbloquear la pantalla"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar acceso directo"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de color"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de color"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Como mantuviste presionadas las teclas de volumen, se activó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se presionaron las teclas de volumen. Se desactivó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén presionadas ambas teclas de volumen durante tres segundos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Se cambió la solicitud SS por una videollamada"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Se cambió la solicitud SS por una solicitud USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Se cambió a una nueva solicitud SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerta de suplantación de identidad (phishing)"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de trabajo"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerta enviada"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expandir"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximizar"</string> <string name="close_button_text" msgid="10603510034455258">"Cerrar"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Responder"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Rechazar"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Colgar"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Llamada entrante"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Llamada en curso"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando una llamada entrante"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> elementos seleccionados</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> elemento seleccionado</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Para continuar, <b><xliff:g id="APP">%s</xliff:g></b&gt necesita acceso a la cámara del dispositivo."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Activar"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacidad del sensor"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 108c3dbeb954..28ff5fb61b22 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas digitales."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor está inhabilitado en estos momentos."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Usa tu huella digital para continuar"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icono de huella digital"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"No puede reconocer tu cara. Vuelve a intentarlo."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Se parece mucha a la anterior. Pon otra cara."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Gira la cabeza un poco menos."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Gira la cabeza un poco menos."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"No gires tanto la cabeza."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Retira cualquier objeto que te tape la cara."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Limpia la parte superior de la pantalla, incluida la barra de color negro"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite que la aplicación lea y modifique la configuración de No molestar."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de visualización"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que el titular inicie el uso de permisos de una aplicación. Las aplicaciones normales no deberían necesitar nunca este permiso."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acceder a datos de sensores a una frecuencia de muestreo alta"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que la aplicación consulte datos de sensores a una frecuencia superior a 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Establecimiento de reglas de contraseña"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Controla la longitud y los caracteres permitidos en los PIN y en las contraseñas de bloqueo de pantalla."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Supervisar los intentos de desbloqueo de pantalla"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar acceso directo"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de color"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de color"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Al mantener pulsadas las teclas de volumen, se ha activado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se han mantenido pulsadas las teclas de volumen. Se ha desactivado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Para utilizar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, mantén pulsadas ambas teclas de volumen durante 3 segundos"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Se ha cambiado la solicitud de SS a una videollamada"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Se ha cambiado la solicitud de SS a una solicitud de USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Se ha cambiado a una nueva solicitud de SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerta de phishing"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de trabajo"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Con sonido"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Mostrar"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximizar"</string> <string name="close_button_text" msgid="10603510034455258">"Cerrar"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Responder"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Rechazar"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Colgar"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Llamada entrante"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Llamada en curso"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando una llamada entrante"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> seleccionados</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> seleccionado</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Para continuar, <b><xliff:g id="APP">%s</xliff:g></b> necesita tener acceso a la cámara del dispositivo."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Activar"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacidad del sensor"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 70862d481b65..cb2bcdc3d463 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Selles seadmes pole sõrmejäljeandurit."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Andur on ajutiselt keelatud."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Sõrmejälg <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Jätkamiseks kasutage sõrmejälge"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Sõrmejälje ikoon"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Nägu ei õnnestu enam tuvastada. Proovige uuesti."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Liiga sarnane, palun muutke oma asendit."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Pöörake oma pead veidi vähem."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Pöörake oma pead veidi vähem."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Pöörake oma pead veidi vähem."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Eemaldage kõik, mis varjab teie nägu."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Puhastage ekraani ülaosa, sh musta värvi riba"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Võimaldab rakendusel lugeda ja kirjutada funktsiooni Mitte segada seadistusi."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"vaatamisloa kasutamise alustamine"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Võimaldab omanikul rakenduse puhul alustada loa kasutamist. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"juurdepääs anduri andmetele kõrgel diskreetimissagedusel"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Võimaldab rakendusel anduri andmeid diskreetida sagedusel, mis on suurem kui 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Parooli reeglite määramine"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Juhitakse ekraaniluku paroolide ja PIN-koodide pikkusi ning lubatud tähemärkide seadeid."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Ekraani avamiskatsete jälgimine"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kasuta otseteed"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Värvide ümberpööramine"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Värvide korrigeerimine"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati sisse."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati välja."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kasutamiseks hoidke kolm sekundit all mõlemat helitugevuse klahvi"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-taotlus muudeti videokõneks"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-taotlus muudeti USSD-taotluseks"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Muudeti uueks SS-taotluseks"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Andmepüügihoiatus"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Tööprofiil"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Teavitatud"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Laienda"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maksimeeri"</string> <string name="close_button_text" msgid="10603510034455258">"Sule"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Vasta"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Keeldu"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Lõpeta kõne"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Sissetulev kõne"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Käimasolev kõne"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Sissetuleva kõne filtreerimine"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> on valitud</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> on valitud</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Jätkamiseks vajab rakendus <b><xliff:g id="APP">%s</xliff:g></b> juurdepääsu teie seadme kaamerale."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Lülita sisse"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Anduri privaatsus"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index bc691a5c37e2..f95c2796557c 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -93,7 +93,7 @@ <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Datu-konexioaren egoera"</string> <string name="notification_channel_sms" msgid="1243384981025535724">"SMS mezuak"</string> <string name="notification_channel_voice_mail" msgid="8457433203106654172">"Erantzungailuko mezuak"</string> - <string name="notification_channel_wfc" msgid="9048240466765169038">"Wi-Fi bidezko deiak"</string> + <string name="notification_channel_wfc" msgid="9048240466765169038">"Wifi bidezko deiak"</string> <string name="notification_channel_sim" msgid="5098802350325677490">"SIMaren egoera"</string> <string name="notification_channel_sim_high_prio" msgid="642361929452850928">"SIM txartelaren lehentasun handiko jakinarazpenak"</string> <string name="peerTtyModeFull" msgid="337553730440832160">"Beste gailuak TTY osagarria FULL moduan erabiltzea eskatu du"</string> @@ -122,23 +122,23 @@ <string name="roamingText11" msgid="5245687407203281407">"Ibiltaritzari buruzko jakinarazpena aktibatuta"</string> <string name="roamingText12" msgid="673537506362152640">"Ibiltaritzari buruzko jakinarazpena desaktibatuta"</string> <string name="roamingTextSearching" msgid="5323235489657753486">"Zerbitzu bila"</string> - <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Ezin izan dira konfiguratu Wi‑Fi bidezko deiak"</string> + <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Ezin izan dira konfiguratu wifi bidezko deiak"</string> <string-array name="wfcOperatorErrorAlertMessages"> - <item msgid="468830943567116703">"Wi-Fi bidez deiak egiteko eta mezuak bidaltzeko, eskatu operadoreari zerbitzu hori gaitzeko. Ondoren, aktibatu Wi-Fi bidezko deiak Ezarpenak atalean. (Errore-kodea: <xliff:g id="CODE">%1$s</xliff:g>)"</item> + <item msgid="468830943567116703">"Wifi bidez deiak egiteko eta mezuak bidaltzeko, eskatu operadoreari zerbitzu hori gaitzeko. Ondoren, aktibatu Wifi bidezko deiak Ezarpenak atalean. (Errore-kodea: <xliff:g id="CODE">%1$s</xliff:g>)"</item> </string-array> <string-array name="wfcOperatorErrorNotificationMessages"> - <item msgid="4795145070505729156">"Arazo bat izan da Wi‑Fi bidezko deiak zure operadorearekin erregistratzean: <xliff:g id="CODE">%1$s</xliff:g>"</item> + <item msgid="4795145070505729156">"Arazo bat izan da wifi bidezko deiak zure operadorearekin erregistratzean: <xliff:g id="CODE">%1$s</xliff:g>"</item> </string-array> <!-- no translation found for wfcSpnFormat_spn (2982505428519096311) --> <skip /> - <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi bidezko deiak"</string> + <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"<xliff:g id="SPN">%s</xliff:g> operadorearen wifi bidezko deiak"</string> <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"<xliff:g id="SPN">%s</xliff:g> operadorearen wifi bidezko deiak"</string> <string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"WLAN bidezko deia"</string> <string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"<xliff:g id="SPN">%s</xliff:g> WLAN bidezko deia"</string> <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"<xliff:g id="SPN">%s</xliff:g> wifia"</string> <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Wi-Fi bidezko deiak | <xliff:g id="SPN">%s</xliff:g>"</string> <string name="wfcSpnFormat_spn_vowifi" msgid="6865214948822061486">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string> - <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Wi-Fi bidezko deiak"</string> + <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Wifi bidezko deiak"</string> <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"Wifia"</string> <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Wi-Fi bidezko deiak"</string> <string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string> @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wifi-sarea soilik"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> operadorearen deietarako ordezko aukera"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ez da desbideratu"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> zenbakira <xliff:g id="TIME_DELAY">{2}</xliff:g> segundotan"</string> @@ -252,7 +251,7 @@ <string name="global_action_logout" msgid="6093581310002476511">"Amaitu saioa"</string> <string name="global_action_screenshot" msgid="2610053466156478564">"Pantaila-argazkia"</string> <string name="bugreport_title" msgid="8549990811777373050">"Akatsen txostena"</string> - <string name="bugreport_message" msgid="5212529146119624326">"Gailuaren uneko egoerari buruzko informazioa bilduko da, mezu elektroniko gisa bidaltzeko. Minutu batzuk igaroko dira akatsen txostena sortzen hasten denetik bidaltzeko prest egon arte. Itxaron, mesedez."</string> + <string name="bugreport_message" msgid="5212529146119624326">"Gailuaren oraingo egoerari buruzko informazioa bilduko da, mezu elektroniko gisa bidaltzeko. Minutu batzuk igaroko dira akatsen txostena sortzen hasten denetik bidaltzeko prest egon arte. Itxaron, mesedez."</string> <string name="bugreport_option_interactive_title" msgid="7968287837902871289">"Txosten dinamikoa"</string> <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Aukera hau erabili beharko zenuke ia beti. Txostenaren jarraipena egin ahal izango duzu eta arazoari buruzko xehetasunak eman ahal izango dituzu. Baliteke gutxitan erabili behar izaten diren atalak ez agertzea, denbora aurrezteko."</string> <string name="bugreport_option_full_title" msgid="7681035745950045690">"Txosten osoa"</string> @@ -452,7 +451,7 @@ <string name="permdesc_camera" msgid="5240801376168647151">"Aplikazioak abian den bitartean erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko."</string> <string name="permlab_backgroundCamera" msgid="7549917926079731681">"Argazkiak atera eta bideoak grabatu atzeko planoan."</string> <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Aplikazioak edonoiz erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko."</string> - <string name="permlab_systemCamera" msgid="3642917457796210580">"onartu aplikazio edo zerbitzu bati sistemako kamerak atzitzea argazkiak eta bideoak ateratzeko"</string> + <string name="permlab_systemCamera" msgid="3642917457796210580">"eman sistemako kamerak atzitzeko baimena aplikazio edo zerbitzu bati argazkiak ateratzeko eta bideoak grabatzeko"</string> <string name="permdesc_systemCamera" msgid="5938360914419175986">"Pribilegioa duen edo sistemakoa den aplikazio honek edonoiz erabil dezake kamera argazkiak ateratzeko eta bideoak grabatzeko. Halaber, android.permission.CAMERA baimena izan behar du aplikazioak."</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"eman jakinarazpenak jasotzeko baimena aplikazioari edo zerbitzuari kamerak ireki edo ixten direnean."</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Kamera ireki edo itxi dela (eta zer aplikaziorekin) dioten jakinarazpenak jaso ditzake aplikazio honek."</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Gailu honek ez du hatz-marken sentsorerik."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sentsorea aldi baterako desgaitu da."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> hatza"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Aurrera egiteko, erabili hatz-marka"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Hatz-markaren ikonoa"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Ez dugu ezagutzen aurpegi hori. Saiatu berriro."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Jarrera berdintsuegia da. Alda ezazu."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Biratu burua pixka bat gutxiago."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Biratu burua pixka bat gutxiago."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Biratu burua pixka bat gutxiago."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Kendu aurpegia estaltzen dizuten gauzak."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Garbitu pantailaren goialdea, barra beltza barne"</string> @@ -674,7 +675,7 @@ <string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"atzitu DRM ziurtagiriak"</string> <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"DRM ziurtagiriak hornitzea eta erabiltzeko baimena ematen die aplikazioei. Aplikazio normalek ez lukete beharko."</string> <string name="permlab_handoverStatus" msgid="7620438488137057281">"Jaso Android Beam transferentzien egoera"</string> - <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Uneko Android Beam transferentziei buruzko informazioa jasotzeko baimena ematen die aplikazioei"</string> + <string name="permdesc_handoverStatus" msgid="3842269451732571070">"Oraingo Android Beam transferentziei buruzko informazioa jasotzeko baimena ematen die aplikazioei"</string> <string name="permlab_removeDrmCertificates" msgid="710576248717404416">"kendu DRM ziurtagiriak"</string> <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"DRM ziurtagiriak kentzea baimentzen die aplikazioei. Aplikazio normalek ez lukete beharko."</string> <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"lotu operadorearen mezularitza-zerbitzuari"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ez molestatzeko moduaren konfigurazioa irakurtzeko eta bertan idazteko baimena ematen die aplikazioei."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"hasi ikusteko baimena erabiltzen"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Aplikazioaren baimena erabiltzen hasteko baimena ematen die titularrei. Aplikazio normalek ez lukete beharko."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"atzitu sentsoreen datuen laginak abiadura handian"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Aplikazioak 200 Hz-tik gorako abiaduran hartu ahal izango ditu sentsoreen datuen laginak"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Ezarri pasahitzen arauak"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolatu pantaila blokeoaren pasahitzen eta PINen luzera eta onartutako karaktereak."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Gainbegiratu pantaila desblokeatzeko saiakerak"</string> @@ -1197,7 +1200,7 @@ <string name="screen_compat_mode_scale" msgid="8627359598437527726">"Eskala"</string> <string name="screen_compat_mode_show" msgid="5080361367584709857">"Erakutsi beti"</string> <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Gaitu hori berriro Sistemaren ezarpenak > Aplikazioak > Deskargatutakoak."</string> - <string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak ez du onartzen uneko pantailaren tamaina eta espero ez bezala joka lezake."</string> + <string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak ez du onartzen pantailaren tamaina, eta baliteke espero ez bezala jokatzea."</string> <string name="unsupported_display_size_show" msgid="980129850974919375">"Erakutsi beti"</string> <string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"Android sistema eragilearen bertsio bateraezin baterako dago egina <xliff:g id="APP_NAME">%1$s</xliff:g>; beraz, espero ez bezala funtziona lezake. Baliteke aplikazioaren bertsio eguneratuago bat eskuragarri egotea."</string> <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Erakutsi beti"</string> @@ -1227,9 +1230,9 @@ <string name="new_app_description" msgid="1958903080400806644">"Gorde gabe itxiko da <xliff:g id="OLD_APP">%1$s</xliff:g>"</string> <string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> prozesuak memoria-muga gainditu du"</string> <string name="dump_heap_ready_notification" msgid="2302452262927390268">"Prest dago <xliff:g id="PROC">%1$s</xliff:g> memoria-iraulketaren txostena"</string> - <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Sortu da uneko memoria-iraulketaren txostena. Sakatu partekatzeko."</string> - <string name="dump_heap_title" msgid="4367128917229233901">"Uneko memoria-iraulketaren txostena partekatu nahi duzu?"</string> - <string name="dump_heap_text" msgid="1692649033835719336">"<xliff:g id="PROC">%1$s</xliff:g> prozesuak memoria-muga (<xliff:g id="SIZE">%2$s</xliff:g>) gainditu du. Uneko memoria-iraulketaren txostena sortu da, garatzailearekin parteka dezazun. Kontuz: baliteke txosten horrek aplikazioak atzi dezakeen informazio pertsonala izatea."</string> + <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Sortu da memoria-iraulketaren txostena. Sakatu partekatzeko."</string> + <string name="dump_heap_title" msgid="4367128917229233901">"Memoria-iraulketaren txostena partekatu nahi duzu?"</string> + <string name="dump_heap_text" msgid="1692649033835719336">"<xliff:g id="PROC">%1$s</xliff:g> prozesuak memoria-muga (<xliff:g id="SIZE">%2$s</xliff:g>) gainditu du. Memoria-iraulketaren txostena sortu da, garatzailearekin parteka dezazun. Kontuz: baliteke txosten horrek aplikazioak atzi dezakeen informazio pertsonala izatea."</string> <string name="dump_heap_system_text" msgid="6805155514925350849">"<xliff:g id="PROC">%1$s</xliff:g> prozesuak bere memoria-muga (<xliff:g id="SIZE">%2$s</xliff:g>) gainditu du. Memoria-iraulketaren txosten bat duzu erabilgarri, hura partekatu nahi baduzu ere. Kontuz: baliteke txosten horrek prozesuak atzi dezakeen kontuzko informazio pertsonala izatea eta datu horien barnean zuk idatzitakoak egotea, besteak beste."</string> <string name="dump_heap_ready_text" msgid="5849618132123045516">"<xliff:g id="PROC">%1$s</xliff:g> prozesuaren memoria-iraulketaren txosten bat duzu erabilgarri, hura partekatu nahi baduzu ere. Kontuz: baliteke txosten horrek prozesuak atzi dezakeen kontuzko informazio pertsonala izatea eta datu horien barnean zuk idatzitakoak egotea, besteak beste."</string> <string name="sendText" msgid="493003724401350724">"Aukeratu testurako ekintza"</string> @@ -1357,7 +1360,7 @@ <string name="alert_windows_notification_message" msgid="6538171456970725333">"Ez baduzu nahi <xliff:g id="NAME">%s</xliff:g> zerbitzuak eginbide hori erabiltzea, sakatu hau ezarpenak ireki eta aukera desaktibatzeko."</string> <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Desaktibatu"</string> <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"<xliff:g id="NAME">%s</xliff:g> egiaztatzen…"</string> - <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"Uneko edukia berrikusten"</string> + <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"Edukia berrikusten"</string> <string name="ext_media_new_notification_title" msgid="3517407571407687677">"Euskarri berria: <xliff:g id="NAME">%s</xliff:g>"</string> <string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> ez da funtzionatzen ari"</string> <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Sakatu konfiguratzeko"</string> @@ -1638,7 +1641,7 @@ <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Erabilerraztasun-lasterbidea erabili nahi duzu?"</string> <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Lasterbidea aktibatuta dagoenean, bi bolumen-botoiak hiru segundoz sakatuta abiaraziko da erabilerraztasun-eginbidea."</string> <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Erabilerraztasun-eginbideetarako lasterbidea aktibatu nahi duzu?"</string> - <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Eduki sakatuta bolumen-botoiak segundo batzuez erabilerraztasun-eginbideak aktibatzeko. Hori eginez gero, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nUneko eginbideak:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nHautatutako eginbideak aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."</string> + <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Eduki sakatuta bolumen-botoiak segundo batzuez erabilerraztasun-eginbideak aktibatzeko. Hori eginez gero, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nEginbideak:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nHautatutako eginbideak aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."</string> <string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string> <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"<xliff:g id="SERVICE">%1$s</xliff:g> zerbitzuaren lasterbidea aktibatu nahi duzu?"</string> <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Eduki sakatuta bolumen-botoiak segundo batzuez <xliff:g id="SERVICE">%1$s</xliff:g> izeneko erabilerraztasun-eginbidea aktibatzeko. Honen bidez, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nLasterbide hau beste eginbide batengatik aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Erabili lasterbidea"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Koloreen alderantzikatzea"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Koloreen zuzenketa"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktibatu egin da."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desaktibatu egin da."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> erabiltzeko, eduki sakatuta bi bolumen-botoiak hiru segundoz"</string> @@ -1675,7 +1680,7 @@ <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Eginbide batetik bestera aldatzeko, pasatu bi hatz pantailaren behealdetik gora eta eduki sakatuta une batez."</string> <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Eginbide batetik bestera aldatzeko, pasatu hiru hatz pantailaren behealdetik gora eta eduki sakatuta une batez."</string> <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Lupa"</string> - <string name="user_switched" msgid="7249833311585228097">"Uneko erabiltzailea: <xliff:g id="NAME">%1$s</xliff:g>."</string> + <string name="user_switched" msgid="7249833311585228097">"Erabiltzailea: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzailera aldatzen…"</string> <string name="user_logging_out_message" msgid="7216437629179710359">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzailearen saioa amaitzen…"</string> <string name="owner_name" msgid="8713560351570795743">"Jabea"</string> @@ -1775,7 +1780,7 @@ <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Idatzi administratzailearen PIN kodea"</string> <string name="restr_pin_enter_pin" msgid="373139384161304555">"Idatzi PINa"</string> <string name="restr_pin_incorrect" msgid="3861383632940852496">"Okerra"</string> - <string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"Uneko PINa"</string> + <string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"Oraingo PINa"</string> <string name="restr_pin_enter_new_pin" msgid="3267614461844565431">"PIN berria"</string> <string name="restr_pin_confirm_pin" msgid="7143161971614944989">"Berretsi PIN berria"</string> <string name="restr_pin_create_pin" msgid="917067613896366033">"Konfiguratu debekuak aldatu ahal izateko idatzi beharko den PIN kodea"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS eskaera bideo-deira aldatu da"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS eskaera USSD eskaerara aldatu da"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"SS eskaera berrira aldatu da"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing-alerta"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Work profila"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Egin du soinua"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Zabaldu"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximizatu"</string> <string name="close_button_text" msgid="10603510034455258">"Itxi"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Erantzun"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Baztertu"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Amaitu deia"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Jasotako deia"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Deia abian da"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Jasotako dei bat bistaratzen"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> hautatuta</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> hautatuta</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Aurrera egiteko, gailuaren kamera atzitzeko baimena behar du <b><xliff:g id="APP">%s</xliff:g></b> aplikazioak."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktibatu"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sentsoreen pribatutasuna"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 6a0e45446341..714b5e4635ad 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"این دستگاه حسگر اثر انگشت ندارد."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"حسگر بهطور موقت غیرفعال است."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"انگشت <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"برای ادامه، از اثر انگشتتان استفاده کنید"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"نماد اثر انگشت"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"دیگر چهره را تشخیص نمیدهد. دوباره امتحان کنید."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"بسیار شبیه قبلی است، لطفاً قیافه دیگری بگیرید."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"سرتان را کمی صاف بگیرید."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"سرتان را کمی صاف بگیرید."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"سرتان را کمی صاف بگیرید."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"هرچیزی را که حائل چهرهتان است بردارید."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"بالای صفحه و همچنین نوار مشکی را تمیز کنید."</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"به برنامه امکان میدهد پیکربندی «مزاحم نشوید» را بخواند و بنویسد."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"شروع مشاهده استفاده از مجوز"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"به دارنده اجازه شروع استفاده از مجوز را برای برنامه میدهد. هرگز برای برنامههای معمول نیاز نیست."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"دسترسی به دادههای حسگر با نرخ نمونهبرداری بالا"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"به برنامه اجازه میدهد دادههای حسگر را با نرخ بیشاز ۲۰۰ هرتز نمونهبرداری کند"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"تنظیم قوانین گذرواژه"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"کنترل طول و نوع نویسههایی که در گذرواژه و پین قفل صفحه مجاز است."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"پایش تلاشهای باز کردن قفل صفحه"</string> @@ -1440,7 +1444,7 @@ <string name="notification_listener_binding_label" msgid="2702165274471499713">"شنونده اعلان"</string> <string name="vr_listener_binding_label" msgid="8013112996671206429">"شنونده VR"</string> <string name="condition_provider_service_binding_label" msgid="8490641013951857673">"ارائهدهنده وضعیت"</string> - <string name="notification_ranker_binding_label" msgid="432708245635563763">"سرویس رتبهبندی اعلان"</string> + <string name="notification_ranker_binding_label" msgid="432708245635563763">"سرویس ردهبندی اعلان"</string> <string name="vpn_title" msgid="5906991595291514182">"VPN فعال شد"</string> <string name="vpn_title_long" msgid="6834144390504619998">"VPN را <xliff:g id="APP">%s</xliff:g> فعال کرده است"</string> <string name="vpn_text" msgid="2275388920267251078">"برای مدیریت شبکه ضربه بزنید."</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"استفاده از میانبر"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"وارونگی رنگ"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"تصحیح رنگ"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> روشن شد."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> خاموش شد."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"برای استفاده از <xliff:g id="SERVICE_NAME">%1$s</xliff:g>، هر دو کلید صدا را فشار دهید و سه ثانیه نگه دارید"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"درخواست SS به تماس تصویری تغییر کرد"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"درخواست SS به درخواست USSD تغییر کرد"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"به درخواست SS جدید تغییر کرد"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"هشدار رمزگیری"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"نمایه کاری"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"هشدار ارسال شد"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"بزرگ کردن"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"بزرگ کردن"</string> <string name="close_button_text" msgid="10603510034455258">"بستن"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"پاسخ"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"رد کردن"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"قطع تماس"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"تماس ورودی"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"تماس درحال انجام"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"درحال غربال کردن تماس ورودی"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> انتخاب شد</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> انتخاب شد</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"برای ادامه دادن، <b><xliff:g id="APP">%s</xliff:g></b> باید به دوربین دستگاه دسترسی داشته باشد."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"روشن کردن"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"حریمخصوصی حسگر"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 8e5d9b44759a..ef872e98f6c3 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Laitteessa ei ole sormenjälkitunnistinta."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tunnistin poistettu väliaikaisesti käytöstä."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Sormi <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Jatka sormenjäljen avulla"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Sormenjälkikuvake"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Ei enää tunnista kasvoja. Yritä uudelleen."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Liian samanlainen, vaihda asentoa."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Käännä päätä vähän vähemmän."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Käännä päätä vähän vähemmän."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Käännä päätä vähän vähemmän."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Poista esteet kasvojesi edestä."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Puhdista näytön yläreuna, mukaan lukien musta palkki"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Sallii sovelluksen lukea ja muokata Älä häiritse -tilan asetuksia."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"aloita katseluoikeuksien käyttö"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Antaa luvanhaltijan käynnistää sovelluksen käyttöoikeuksien käytön. Ei tavallisten sovelluksien käyttöön."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"saada pääsyn anturidataan suuremmalla näytteenottotaajuudella"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Sallii sovelluksen ottaa anturidatasta näytteitä yli 200 Hz:n taajuudella"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Asentaa salasanasäännöt"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Hallinnoida ruudun lukituksen salasanoissa ja PIN-koodeissa sallittuja merkkejä ja niiden pituutta."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Tarkkailla näytön avaamisyrityksiä"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Käytä pikanäppäintä"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Käänteiset värit"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Värinkorjaus"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin päälle."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin pois päältä."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Voit käyttää palvelua <xliff:g id="SERVICE_NAME">%1$s</xliff:g> painamalla molempia äänenvoimakkuuspainikkeita kolmen sekunnin ajan"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-pyyntö vaihdettu videopuheluksi"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-pyyntö vaihdettu USSD-pyynnöksi"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Vaihdettu uudeksi SS-pyynnöksi"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Varoitus tietojenkalastelusta"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Työprofiili"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Hälytti"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Laajenna"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Suurenna"</string> <string name="close_button_text" msgid="10603510034455258">"Sulje"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Vastaa"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Hylkää"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Lopeta puhelu"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Saapuva puhelu"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Käynnissä oleva puhelu"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Seulotaan saapuvaa puhelua"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> valittu</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> valittu</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Jotta voit jatkaa, <b><xliff:g id="APP">%s</xliff:g></b> tarvitsee pääsyn laitteesi kameraan."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Laita päälle"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Anturin tietosuoja"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 849f9594729f..7cc316077b1f 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi seulement"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Méthode d\'appel secondaire avec <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Cet appareil ne possède pas de capteur d\'empreintes digitales."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Le capteur a été désactivé temporairement."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utilisez votre empreinte digitale pour continuer"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icône d\'empreinte digitale"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Ce visage ne sera plus reconnu. Réessayez."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Trop similaire. Changez de pose."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Tournez un peu moins votre tête."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Tournez un peu moins votre tête."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Tournez un peu moins votre tête."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Retirez tout ce qui pourrait couvrir votre visage."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Nettoyez le haut de l\'écran, y compris la barre noire"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet à l\'application de consulter et de modifier la configuration du mode Ne pas déranger."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"démarrer l\'affichage de l\'usage des autorisations"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet au détenteur de démarrer l\'usage des autorisations pour une application. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accéder aux données des capteurs à un taux d’échantillonnage élevé"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permet à l’application d’échantillonner les données des capteurs à une fréquence supérieure à 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Définir les règles du mot de passe"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Gérer le nombre et le type de caractères autorisés dans les mots de passe et les NIP de verrouillage de l\'écran."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Gérer les tentatives de déverrouillage de l\'écran"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utiliser le raccourci"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume maintenues enfoncées. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume maintenues enfoncées. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Maintenez enfoncées les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"La demande SS a été remplacée par une demande d\'appel vidéo"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"La demande SS a été remplacée par une demande USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"La demande a été remplacée par une nouvelle demande SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerte d\'hameçonnage"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil professionnel"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerté"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Développer"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Agrandir"</string> <string name="close_button_text" msgid="10603510034455258">"Fermer"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g> : <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Répondre"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Refuser"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Raccrocher"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Appel entrant"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Appel en cours"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrer un appel entrant"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> élément sélectionné</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> éléments sélectionnés</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Pour continuer, vous devez accorder l\'accès à l\'appareil photo de votre appareil à l\'application <b><xliff:g id="APP">%s</xliff:g></b>."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Activer"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Confidentialité des capteurs"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 60138c47d40e..a3c0badee9b4 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi uniquement"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Appels de secours via <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Capteur temporairement désactivé."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utilisez votre empreinte digitale pour continuer"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icône d\'empreinte digitale"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Impossible de reconnaître le visage. Réessayez."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Ressemble à un visage existant, changez de pose."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Tournez un peu moins la tête."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Tournez un peu moins la tête."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Tournez un peu moins la tête."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Retirez tout ce qui cache votre visage."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Nettoyez la partie supérieure de l\'écran, y compris la barre noire"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permet à l\'application de consulter et de modifier la configuration du mode Ne pas déranger."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"activer l\'utilisation de l\'autorisation d\'affichage"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permet à l\'application autorisée d\'activer l\'utilisation de l\'autorisation pour une application. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"accéder aux données des capteurs à un taux d\'échantillonnage élevé"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Autorise l\'appli à échantillonner les données des capteurs à un taux supérieur à 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Définir les règles du mot de passe"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Gérer le nombre et le type de caractères autorisés dans les mots de passe et les codes d\'accès de verrouillage de l\'écran"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Gérer les tentatives de déverrouillage de l\'écran"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utiliser le raccourci"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversion des couleurs"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Correction des couleurs"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Appuyez de manière prolongée sur les deux touches de volume pendant trois secondes pour utiliser <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Requête SS transformée en appel vidéo"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Requête SS transformée en requête USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Remplacement par une nouvelle requête SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerte de hameçonnage"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil professionnel"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alerte envoyée"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Développer"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Agrandir"</string> <string name="close_button_text" msgid="10603510034455258">"Fermer"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g> : <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Répondre"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Refuser"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Raccrocher"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Appel entrant"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Appel en cours"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrer un appel entrant"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> élément sélectionné</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> éléments sélectionnés</item> @@ -2057,7 +2069,7 @@ <string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Retour"</string> <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Applications récentes"</string> <string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Notifications"</string> - <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Configuration rapide"</string> + <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Réglages rapides"</string> <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Boîte de dialogue Marche/Arrêt"</string> <string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"Verrouiller l\'écran"</string> <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Capture d\'écran"</string> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Pour continuer, <b><xliff:g id="APP">%s</xliff:g></b> a besoin d\'accéder à l\'appareil photo de votre appareil."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Activer"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Confidentialité du capteur"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index eadac9edad74..72a799ede92f 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo non ten sensor de impresión dixital."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Desactivouse o sensor temporalmente."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utiliza a túa impresión dixital para continuar"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona de impresión dixital"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Xa non se pode recoñecer a cara. Téntao de novo."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"É moi similar. Cambia a pose."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Xira a cabeza un pouco menos."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Xira a cabeza un pouco menos."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Xira a cabeza un pouco menos."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Quita todo o que oculte a túa cara."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Limpa a parte superior da pantalla, incluída a barra de cor negra"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite á aplicación ler e escribir a configuración do modo Non molestar."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso de permiso de vista"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite ao propietario iniciar o uso de permisos dunha aplicación. As aplicacións normais non deberían precisalo nunca."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acceder aos datos dos sensores usando unha taxa de mostraxe alta"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que a aplicación recompile mostras dos datos dos sensores cunha taxa superior a 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Establecer as normas de contrasinal"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Controla a lonxitude e os caracteres permitidos nos contrasinais e nos PIN de bloqueo da pantalla."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Controlar os intentos de desbloqueo da pantalla"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar atallo"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversión de cor"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Corrección de cor"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume premidas. Activouse o servizo <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Desactivouse <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Mantén premidas as teclas do volume durante tres segudos para usar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"A solicitude SS transformouse nunha videochamada"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"A solicitude SS transformouse nunha solicitude USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Transformouse nunha nova solicitude SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerta de phishing"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de traballo"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Con son"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Despregar"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximizar"</string> <string name="close_button_text" msgid="10603510034455258">"Pechar"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Resposta"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Rexeitar"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Colgar"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada entrante"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada en curso"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando chamada entrante"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other">Seleccionáronse <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="one">Seleccionouse <xliff:g id="COUNT_0">%1$d</xliff:g></item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Para continuar, <b><xliff:g id="APP">%s</xliff:g></b> precisa acceder á cámara do dispositivo."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Activar"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacidade do sensor"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 86416bce3f48..7a2527f50cba 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ફક્ત વાઇ-ફાઇ"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> બૅકઅપ કૉલિંગ"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ફોરવર્ડ કર્યો નથી"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="TIME_DELAY">{2}</xliff:g> સેકન્ડ પછી <xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"સેન્સર હંગામી રૂપે બંધ કર્યું છે."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"આંગળી <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ચાલુ રાખવા માટે તમારી ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ફિંગરપ્રિન્ટ આયકન"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"ચહેરો ઓળખી શકાતો નથી. ફરી પ્રયાસ કરો."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"ઘણી સમાનતા ધરાવે છે, કૃપા કરીને તમારો પોઝ બદલો."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"તમારું માથું થોડું ફેરવો."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"તમારું માથું થોડું ફેરવો."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"તમારું માથું થોડું ઓછું ફેરવો."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"તમારા ચહેરાને છુપાવતી કંઈપણ વસ્તુ દૂર કરો."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"કાળી પટ્ટી સહિત, તમારી સ્ક્રીનની ટોચ સાફ કરો"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"એપ્લિકેશનને ખલેલ પાડશો નહીં ગોઠવણી વાંચવા અને લખવાની મંજૂરી આપે છે."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"પરવાનગી વપરાશ જુઓને શરૂ કરો"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"કોઈ ઍપ માટે પરવાનગી વપરાશ શરૂ કરવાની ધારકને મંજૂરી આપે છે. સામાન્ય ઍપ માટે ક્યારેય જરૂર પડી ન શકે."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ઉચ્ચ સેમ્પ્લિંગ રેટ પર સેન્સરનો ડેટા ઍક્સેસ કરો"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"ઍપને 200 Hzથી વધુના દરે સેન્સરના ડેટાના નમૂનાની મંજૂરી આપે છે"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"પાસવર્ડ નિયમો સેટ કરો"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"સ્ક્રીન લૉક પાસવર્ડ અને પિનમાં મંજૂર લંબાઈ અને અક્ષરોને નિયંત્રિત કરો."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"સ્ક્રીનને અનલૉક કરવાના પ્રયત્નોનું નિયમન કરો"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"શૉર્ટકટનો ઉપયોગ કરો"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"વિપરીત રંગમાં બદલવું"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"રંગ સુધારણા"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ચાલુ કરી."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> બંધ કરી."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>નો ઉપયોગ કરવા માટે બન્ને વૉલ્યૂમ કીને ત્રણ સેકન્ડ સુધી દબાવી રાખો"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS વિનંતીને વીડિઓ કૉલમાં બદલવામાં આવી છે"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS વિનંતીને USSD વિનંતીમાં બદલવામાં આવી છે"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"નવી SS વિનંતીમાં બદલવામાં આવી છે"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ફિશિંગ અલર્ટ"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"ઑફિસની પ્રોફાઇલ"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"અલર્ટ કરેલ"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"વિસ્તૃત કરો"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"મહત્તમ કરો"</string> <string name="close_button_text" msgid="10603510034455258">"બંધ કરો"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"જવાબ"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"નકારો"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"સમાપ્ત કરો"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"ઇનકમિંગ કૉલ"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"ચાલુ કૉલ"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"ઇનકમિંગ કૉલનું સ્ક્રીનિંગ થાય છે"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> પસંદ કરી</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> પસંદ કરી</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ચાલુ રાખવા માટે, <b><xliff:g id="APP">%s</xliff:g></b>ને તમારા ડિવાઇસના કૅમેરાના ઍક્સેસની જરૂર છે."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ચાલુ કરો"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"સેન્સર પ્રાઇવસી સંબંધિત નોટિફિકેશન"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 5f0a8f89b926..f108aa7fdcd0 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -303,7 +303,7 @@ <string name="managed_profile_label" msgid="7316778766973512382">"प्रोफ़ाइल बदलकर वर्क प्रोफ़ाइल पर जाएं"</string> <string name="permgrouplab_contacts" msgid="4254143639307316920">"संपर्क"</string> <string name="permgroupdesc_contacts" msgid="9163927941244182567">"अपने संपर्कों को ऐक्सेस करने की"</string> - <string name="permgrouplab_location" msgid="1858277002233964394">"जगह"</string> + <string name="permgrouplab_location" msgid="1858277002233964394">"जगह की जानकारी"</string> <string name="permgroupdesc_location" msgid="1995955142118450685">"इस डिवाइस की जगह तक पहुंचने दें"</string> <string name="permgrouplab_calendar" msgid="6426860926123033230">"कैलेंडर"</string> <string name="permgroupdesc_calendar" msgid="6762751063361489379">"अपने कैलेंडर को ऐक्सेस करने"</string> @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेंसर कुछ समय के लिए बंद कर दिया गया है."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"फ़िंगरप्रिंट <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"जारी रखने के लिए फ़िंगरप्रिंट का इस्तेमाल करें"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फ़िंगरप्रिंट आइकॉन"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"अब चेहरे की पहचान नहीं कर पा रहा. फिर से कोशिश करें."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"चेहरा काफ़ी मिलता-जुलता है, कृपया अपना पोज़ बदलें."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"अपना सिर थोड़ा कम घुमाएं."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"अपना सिर थोड़ा कम घुमाएं."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"अपना सिर थोड़ा कम घुमाएं."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"आपके चेहरे को छिपाने वाली सभी चीज़ों को हटाएं."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"अपनी स्क्रीन के सबसे ऊपरी हिस्से को साफ़ करें, जिसमें काले रंग वाला बार भी शामिल है"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ऐप को परेशान न करें कॉन्फ़िगरेशन पढ़ने और लिखने देती है."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"देखने की अनुमतियां चालू करें"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"इस्तेमाल करने वाले को किसी ऐप्लिकेशन के लिए अनुमतियों का इस्तेमाल शुरू करने देता है. सामान्य ऐप्लिकेशन के लिए इसकी ज़रूरत कभी नहीं पड़ती."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"सेंसर डेटा को, नमूने लेने की तेज़ दर पर ऐक्सेस करें"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"यह अनुमति मिलने पर ऐप्लिकेशन, 200 हर्ट्ज़ से ज़्यादा की दर पर सेंसर डेटा का नमूना ले पाएगा"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियम सेट करना"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"स्क्रीन लॉक पासवर्ड और पिन की लंबाई और उनमें स्वीकृत वर्णों को नियंत्रित करना."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"स्क्रीन अनलॉक करने के की कोशिशों पर नज़र रखना"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"शॉर्टकट का उपयोग करें"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"रंग बदलने की सुविधा"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"रंग में सुधार करने की सुविधा"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को चालू कर दिया गया."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को बंद कर दिया गया."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> इस्तेमाल करने के लिए आवाज़ वाले दोनों बटन तीन सेकंड तक दबाकर रखें"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"एसएस कोड चलाने के अनुरोध को वीडियो कॉल में बदला गया"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"एसएस कोड चलाने के अनुरोध को यूएसएसडी कोड चलाने के अनुरोध में बदला गया"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"एसएस कोड चलाने के नए अनुरोध में बदला गया"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"फ़िशिंग से जुड़ी चेतावनी"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"वर्क प्रोफ़ाइल"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"अलर्ट किया गया"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"विस्तार करें"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"बड़ा करें"</string> <string name="close_button_text" msgid="10603510034455258">"बंद करें"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"जवाब दें"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"अस्वीकार करें"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"कॉल काटें"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"आने वाला (इनकमिंग) कॉल"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"पहले से जारी कॉल"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"इनकमिंग कॉल को स्क्रीन किया जा रहा है"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> चयनित</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> चयनित</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"जारी रखने के लिए, <b><xliff:g id="APP">%s</xliff:g></b> को आपके डिवाइस का कैमरा ऐक्सेस करने की ज़रूरत है."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"चालू करें"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"सेंसर से जुड़ी निजता के बारे में सूचना"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index e1ae1e24e0a1..91073d445ac6 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -582,6 +582,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor otiska prsta."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Nastavite pomoću otiska prsta"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otiska prsta"</string> @@ -608,7 +609,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Lice nije prepoznato. Pokušajte ponovo."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Previše slično, promijenite pozu."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Nagnite glavu malo manje."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Nagnite glavu malo manje."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Nagnite glavu malo manje."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Uklonite sve što vam zakriva lice."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Očistite vrh zaslona, uključujući crnu traku"</string> @@ -687,6 +689,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Omogućuje aplikaciji čitanje i pisanje konfiguracije opcije Ne ometaj."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pokrenuti upotrebu dopuštenja za pregled"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Dopušta nositelju pokretanje upotrebe dopuštenja za aplikaciju. Ne bi smjelo biti potrebno za uobičajene aplikacije."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pristup podacima senzora pri višoj brzini uzorkovanja"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Aplikaciji omogućuje uzorkovanje podataka senzora pri brzini većoj od 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Postavi pravila zaporke"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Upravlja duljinom i znakovima koji su dopušteni u zaporkama i PIN-ovima zaključavanja zaslona."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Nadziri pokušaje otključavanja zaslona"</string> @@ -1686,6 +1690,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Upotrijebi prečac"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija boja"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcija boje"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za glasnoću. Uključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za glasnoću. Isključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pritisnite i zadržite tipke za glasnoću na tri sekunde da biste koristili uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1898,6 +1904,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS zahtjev promijenjen je u videopoziv"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS zahtjev promijenjen je u USSD zahtjev"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Promijenjeno u novi SS zahtjev"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Upozorenje o krađi identiteta"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Radni profil"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Upozoreni"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Proširivanje"</string> @@ -1911,6 +1918,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maksimiziraj"</string> <string name="close_button_text" msgid="10603510034455258">"Zatvori"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Odgovori"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Odbij"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Dolazni poziv"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Poziv u tijeku"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtriranje dolaznog poziva"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> odabrana</item> <item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> odabrane</item> @@ -2242,4 +2255,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Da bi nastavila s radom, aplikacija <b><xliff:g id="APP">%s</xliff:g></b> treba pristupiti fotoaparatu vašeg uređaja."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Uključi"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privatnost senzora"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 3c04bec04151..32f33dfaea4e 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Az érzékelő átmenetileg le van tiltva."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. ujj"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"A folytatáshoz használja ujjlenyomatát"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ujjlenyomat ikon"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Már nem lehet felismerni az arcát. Próbálja újra."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Túlságosan hasonló, változtasson a pózon."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Kicsit kevésbé fordítsa el a fejét."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Kicsit kevésbé fordítsa el a fejét."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Kicsit kevésbé fordítsa el a fejét."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Távolítson el mindent, ami takarja az arcát."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Tisztítsa meg a képernyő tetejét, a fekete sávot is beleértve."</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Az alkalmazás olvashatja és szerkesztheti a „Ne zavarjanak” funkció beállításait."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"engedélyhasználat megtekintésének elindítása"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lehetővé teszi a felhasználó számára, hogy elindítsa az alkalmazás engedélyhasználatát. A normál alkalmazásoknak erre soha nincs szükségük."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"hozzáférés a szenzoradatokhoz nagy mintavételezési gyakorisággal"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Lehetővé teszi az alkalmazás számára, hogy 200 Hz-nél magasabb gyakorisággal vegyen mintát a szenzoradatokból"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Jelszavakkal kapcsolatos szabályok beállítása"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"A képernyőzár jelszavaiban és PIN kódjaiban engedélyezett karakterek és hosszúság vezérlése."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Képernyőzár-feloldási kísérletek figyelése"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Billentyűparancs használata"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Színek invertálása"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Színkorrekció"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> bekapcsolva."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kikapcsolva."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"A(z) <xliff:g id="SERVICE_NAME">%1$s</xliff:g> használatához tartsa lenyomva három másodpercig a két hangerőgombot"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Az SS-kérés módosítva videohívásra"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Az SS-kérés módosítva USSD-kérésre"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Új SS-kérésre módosítva"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Adathalászati figyelmeztetés"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Munkaprofil"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Értesítve"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Kibontás"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Teljes méret"</string> <string name="close_button_text" msgid="10603510034455258">"Bezárás"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Fogadás"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Elutasítás"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Befejezés"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Bejövő hívás"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Hívás folyamatban"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Bejövő hívás szűrése"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> kiválasztva</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> kiválasztva</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"A folytatáshoz a(z) <b><xliff:g id="APP">%s</xliff:g></b> alkalmazásnak hozzáférésre van szüksége az eszköze kamerájához."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Bekapcsolás"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Érzékelőkkel kapcsolatos adatvédelem"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index bdd238757bfb..cd1bbb11d468 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Այս սարքը չունի մատնահետքերի սկաներ։"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Տվիչը ժամանակավորապես անջատված է:"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Մատնահետք <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Շարունակելու համար անհրաժեշտ է ձեր մատնահետքը"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Մատնահետքի պատկերակ"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Չհաջողվեց ճանաչել դեմքը։ Նորից փորձեք:"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Շատ նման է նախորդին։ Փոխեք ձեր դիրքը։"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Գլուխն ուղիղ պահեք։"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Գլուխն ուղիղ պահեք։"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Գլուխն ուղիղ պահեք։"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Հեռացրեք այն ամենը, ինչը թաքցնում է ձեր երեսը:"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Մաքրեք էկրանի վերևի մասը, ներառյալ սև գոտին"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Թույլ է տալիս հավելվածին փոփոխել «Չանհանգստացնել» գործառույթի կազմաձևումը:"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"թույլտվությունների մասին տվյալների հասանելիություն"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Հավելվածին հասանելի կդառնան թույլտվությունների մասին տվյալները։ Այս թույլտվությունն անհրաժեշտ չէ սովորական հավելվածներին։"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"օգտագործել սենսորների տվյալները բարձր հաճախականության վրա"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Թույլ է տալիս հավելվածին փորձել սենսորների տվյալները 200 Հց-ից բարձր հաճախականության վրա"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Սահմանել գաղտնաբառի կանոնները"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Կառավարել էկրանի ապակողպման գաղտնաբառերի և PIN կոդերի թույլատրելի երկարությունն ու գրանշանները:"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Վերահսկել էկրանի ապակողպման փորձերը"</string> @@ -1438,7 +1442,7 @@ <string name="wallpaper_binding_label" msgid="1197440498000786738">"Պաստառ"</string> <string name="chooser_wallpaper" msgid="3082405680079923708">"Փոխել պաստառը"</string> <string name="notification_listener_binding_label" msgid="2702165274471499713">"Ծանուցման ունկնդիր"</string> - <string name="vr_listener_binding_label" msgid="8013112996671206429">"VR ունկնդրիչ"</string> + <string name="vr_listener_binding_label" msgid="8013112996671206429">"VR ունկնիր"</string> <string name="condition_provider_service_binding_label" msgid="8490641013951857673">"Պայմանների մատակարար"</string> <string name="notification_ranker_binding_label" msgid="432708245635563763">"Ծանուցումների դասակարգման ծառայություն"</string> <string name="vpn_title" msgid="5906991595291514182">"VPN-ը ակտիվացված է"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Օգտագործել դյուրանցումը"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Գունաշրջում"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Գունաշտկում"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը միացավ։"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունն անջատվեց։"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"«<xliff:g id="SERVICE_NAME">%1$s</xliff:g>» ծառայությունն օգտագործելու համար սեղմեք և 3 վայրկյան պահեք ձայնի ուժգնության երկու կոճակները"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS հարցումը փոխվել է տեսազանգի"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS հարցումը փոխվել է USSD հարցման"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Փոխվել է նոր SS հարցման"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Ֆիշինգի մասին զգուշացում"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Աշխատանքային պրոֆիլ"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Ուղարկվել է զգուշացում"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Ընդարձակել"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Մեծացնել"</string> <string name="close_button_text" msgid="10603510034455258">"Փակել"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>՝ <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Պատասխանել"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Մերժել"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Ավարտել"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Մուտքային զանգ"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Ընթացիկ զանգ"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Մուտքային զանգի զտում"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one">Ընտրված է՝ <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="other">Ընտրված է՝ <xliff:g id="COUNT_1">%1$d</xliff:g></item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Շարունակելու համար <b><xliff:g id="APP">%s</xliff:g></b> հավելվածին անհրաժեշտ է ձեր սարքի տեսախցիկի օգտագործման թույլտվություն։"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Միացնել"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Տվիչների գաղտնիություն"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index c84bc1318caf..f7fe38457a56 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Perangkat ini tidak memiliki sensor sidik jari."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor dinonaktifkan untuk sementara."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gunakan sidik jari untuk melanjutkan"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon sidik jari"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Tidak lagi dapat mengenali wajah. Coba lagi."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Terlalu mirip, ubah pose Anda."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Putar sedikit kepala Anda."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Putar sedikit kepala Anda."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Putar sedikit kepala Anda."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Singkirkan apa saja yang menutupi wajah Anda."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Bersihkan bagian atas layar, termasuk kotak hitam"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Mengizinkan aplikasi membaca dan menulis konfigurasi status Jangan Ganggu."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"mulai melihat penggunaan izin"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Memungkinkan pemegang memulai penggunaan izin untuk aplikasi. Tidak diperlukan untuk aplikasi normal."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"mengakses data sensor pada frekuensi pengambilan sampel yang tinggi"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Mengizinkan aplikasi mengambil sampel data sensor pada frekuensi yang lebih besar dari 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Setel aturan sandi"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Mengontrol panjang dan karakter yang diizinkan dalam sandi dan PIN kunci layar."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Pantau upaya pembukaan kunci layar"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gunakan Pintasan"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversi Warna"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Koreksi Warna"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> diaktifkan."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dinonaktifkan."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tekan dan tahan kedua tombol volume selama tiga detik untuk menggunakan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Permintaan SS diubah ke panggilan video"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Permintaan SS diubah ke permintaan USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Diubah ke permintaan SS baru"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Peringatan phishing"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil kerja"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Diingatkan"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Luaskan"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maksimalkan"</string> <string name="close_button_text" msgid="10603510034455258">"Tutup"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Jawab"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Tolak"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Tutup"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Panggilan masuk"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Panggilan sedang berlangsung"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Menyaring panggilan masuk"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dipilih</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dipilih</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Untuk melanjutkan, <b><xliff:g id="APP">%s</xliff:g></b> memerlukan akses ke kamera perangkat."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktifkan"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privasi Sensor"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 84611a2d44a9..5824c3a64843 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Þetta tæki er ekki með fingrafaralesara."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Slökkt tímabundið á skynjara."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Fingur <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Notaðu fingrafarið þitt til að halda áfram"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingrafaratákn"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Andlit þekkist ekki lengur. Reyndu aftur."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Of svipað. Stilltu þér öðruvísi upp."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Snúðu höfðinu aðeins minna."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Hallaðu höfðinu aðeins minna."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Snúðu höfðinu aðeins minna."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Fjarlægðu það sem kann að hylja andlitið."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Hreinsaðu efsta hluta skjásins þíns, þ.m.t. svörtu stikuna"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Leyfir forriti að lesa og skrifa í grunnstillingu „Ónáðið ekki“."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"heimildanotkun upphafsyfirlits"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Leyfir handhafa að byrja heimildanotkun fyrir forrit. Ætti aldrei að þurfa fyrir venjuleg forrit."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"aðgangur að skynjaragögnum með hárri upptökutíðni"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Leyfir forritinu að nota upptökutíðni yfir 200 Hz fyrir skynjaragögn"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Setja reglur um aðgangsorð"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Stjórna lengd og fjölda stafa í aðgangsorðum og PIN-númerum skjáláss."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Fylgjast með tilraunum til að taka skjáinn úr lás"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Nota flýtileið"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Umsnúningur lita"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Litaleiðrétting"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Hljóðstyrkstökkum haldið inni. Kveikt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Hljóðstyrkstökkum haldið inni. Slökkt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Haltu báðum hljóðstyrkstökkunum inni í þrjár sekúndur til að nota <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-beiðni breytt í myndsímtal"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-beiðni breytt í USSD-beiðni"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Breytt í nýja SS-beiðni"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Viðvörun um vefveiðar"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Vinnusnið"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Tilkynnt"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Stækka"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Stækka"</string> <string name="close_button_text" msgid="10603510034455258">"Loka"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Svara"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Hafna"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Leggja á"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Símtal berst"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Símtal í gangi"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Síar símtal sem berst"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> valið</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> valin</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Til að halda áfram þarf <b><xliff:g id="APP">%s</xliff:g></b> aðgang að myndavél tækisins."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Kveikja"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Persónuvernd skynjara"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 2fcc273880f7..d435a253ad22 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Solo Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Chiamate di backup <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: inoltro non effettuato"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> dopo <xliff:g id="TIME_DELAY">{2}</xliff:g> secondi"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Questo dispositivo non dispone di sensore di impronte."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensore temporaneamente disattivato."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dito <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utilizza la tua impronta per continuare"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona dell\'impronta"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Non è più possibile riconoscere il volto. Riprova."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Troppo simile; cambia posa."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Gira un po\' meno la testa."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Gira un po\' meno la testa."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Gira un po\' meno la testa."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Rimuovi tutto ciò che ti nasconde il viso."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Pulisci la parte superiore dello schermo, inclusa la barra nera"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Consente all\'app di leggere e modificare la configurazione della funzione Non disturbare."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"avvio dell\'uso dell\'autorizzazione di visualizzazione"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Consente al titolare di avviare l\'uso delle autorizzazioni per un\'app. Non dovrebbe essere mai necessaria per le normali applicazioni."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Accesso ai dati dei sensori a una frequenza di campionamento elevata"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Consente all\'app di campionare i dati dei sensori a una frequenza maggiore di 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Impostare regole per le password"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Controlla la lunghezza e i caratteri ammessi nelle password e nei PIN del blocco schermo."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorare tentativi di sblocco dello schermo"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usa scorciatoia"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversione dei colori"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Correzione del colore"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> attivato."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> disattivato."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tieni premuti entrambi i tasti del volume per tre secondi per utilizzare <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Richiesta SS modificata in videochiamata"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Richiesta SS modificata in richiesta USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Modificata in nuova richiesta SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Allerta phishing"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profilo di lavoro"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Avviso inviato"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Espandi"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Ingrandisci"</string> <string name="close_button_text" msgid="10603510034455258">"Chiudi"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Rispondi"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Rifiuta"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Riaggancia"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Chiamata in arrivo"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chiamata in corso"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Applicazione filtro a chiamata in arrivo"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> file selezionati</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> file selezionato</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Per continuare, l\'app <b><xliff:g id="APP">%s</xliff:g></b> deve accedere alla videocamera del dispositivo."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Attiva"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacy relativa ai sensori"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index f104d442d154..ee771df1b51c 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -585,6 +585,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"במכשיר זה אין חיישן טביעות אצבע."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"החיישן מושבת באופן זמני."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"אצבע <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"יש להשתמש בטביעת האצבע כדי להמשיך"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"סמל טביעת אצבע"</string> @@ -611,7 +612,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"כבר לא ניתן לזהות פנים. יש לנסות שוב."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"דומה מדי, יש לשנות תנוחה."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"עליך ליישר קצת את הראש."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"עליך ליישר קצת את הראש."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"עליך ליישר קצת את הראש."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"יש להסיר כל דבר שמסתיר את הפנים."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"עליך לנקות את החלק העליון של המסך, כולל הסרגל השחור"</string> @@ -690,6 +692,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"מאפשר לאפליקציה לקרוא ולכתוב את התצורה של \'נא לא להפריע\'."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"התחלת צפייה בהרשאות השימוש"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"מאפשרת לבעלים להפעיל את השימוש בהרשאות עבור אפליקציה מסוימת. הרשאה זו אף פעם לא נדרשת עבור אפליקציות רגילות."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"גישה לנתוני חיישנים בתדירות דגימה גבוהה"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"האפליקציה תוכל לדגום נתוני חיישנים בתדירות של מעל 200 הרץ"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"הגדר כללי סיסמה"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"קביעת האורך הנדרש והתווים המותרים בסיסמאות ובקודי הגישה של מסך הנעילה."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"מעקב אחר ניסיונות לביטול של נעילת המסך"</string> @@ -1708,6 +1712,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"השתמש בקיצור הדרך"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"היפוך צבעים"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"תיקון צבעים"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הופעל."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הושבת."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"יש ללחוץ לחיצה ארוכה על שני לחצני עוצמת הקול למשך שלוש שניות כדי להשתמש בשירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1929,6 +1935,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"בקשת SS שונתה לשיחת וידאו"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"בקשת SS שונתה לבקשת USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"היה שינוי לבקשת SS חדשה"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"התראה על פישינג"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"פרופיל עבודה"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"נשלחה התראה"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"הרחב"</string> @@ -1942,6 +1949,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"הגדל"</string> <string name="close_button_text" msgid="10603510034455258">"סגירה"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"תשובה"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"דחייה"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ניתוק"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"שיחה נכנסת"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"שיחה פעילה"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"סינון שיחה נכנסת"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="two">בחרת <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="many">בחרת <xliff:g id="COUNT_1">%1$d</xliff:g></item> @@ -2276,4 +2289,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"כדי להמשיך, האפליקציה <b><xliff:g id="APP">%s</xliff:g></b> צריכה גישה למצלמה של המכשיר שלך."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"הפעלה"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"פרטיות חיישנים"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 69822ac67fb5..b03f94ca5117 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -203,7 +203,7 @@ <string name="sensor_notification_service" msgid="7474531979178682676">"センサー通知サービス"</string> <string name="twilight_service" msgid="8964898045693187224">"トワイライト サービス"</string> <string name="offline_location_time_zone_detection_service_attribution" msgid="303754195048744816">"Time Zone Detector(未接続)"</string> - <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS Time Update Service"</string> + <string name="gnss_time_update_service" msgid="9039489496037616095">"GNSS 時間アップデートサービス"</string> <string name="factory_reset_warning" msgid="6858705527798047809">"デバイスのデータが消去されます"</string> <string name="factory_reset_message" msgid="2657049595153992213">"管理アプリを使用できません。デバイスのデータはこれから消去されます。\n\nご不明な点がある場合は、組織の管理者にお問い合わせください。"</string> <string name="printing_disabled_by" msgid="3517499806528864633">"「<xliff:g id="OWNER_APP">%s</xliff:g>」により印刷は無効にされています。"</string> @@ -538,10 +538,10 @@ <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"このアプリに画面ロックの複雑さレベル(高、中、低、なし)を認識することを許可します。複雑さレベルは、画面ロックの文字数の範囲やタイプを示すものです。アプリから一定レベルまで画面ロックを更新するよう推奨されることもありますが、ユーザーは無視したり別の操作を行ったりできます。画面ロックは平文で保存されないため、アプリが正確なパスワードを知ることはありません。"</string> <string name="permlab_useBiometric" msgid="6314741124749633786">"生体認証ハードウェアの使用"</string> <string name="permdesc_useBiometric" msgid="7502858732677143410">"生体認証ハードウェアを認証に使用することをアプリに許可します"</string> - <string name="permlab_manageFingerprint" msgid="7432667156322821178">"指紋ハードウェアの管理"</string> + <string name="permlab_manageFingerprint" msgid="7432667156322821178">"指紋認証ハードウェアの管理"</string> <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"使用する指紋テンプレートの追加や削除を行う方法の呼び出しをアプリに許可します。"</string> - <string name="permlab_useFingerprint" msgid="1001421069766751922">"指紋ハードウェアの使用"</string> - <string name="permdesc_useFingerprint" msgid="412463055059323742">"指紋ハードウェアを認証に使用することをアプリに許可します"</string> + <string name="permlab_useFingerprint" msgid="1001421069766751922">"指紋認証ハードウェアの使用"</string> + <string name="permdesc_useFingerprint" msgid="412463055059323742">"指紋認証ハードウェアを認証に使用することをアプリに許可します"</string> <string name="permlab_audioWrite" msgid="8501705294265669405">"音楽コレクションの変更"</string> <string name="permdesc_audioWrite" msgid="8057399517013412431">"音楽コレクションの変更をアプリに許可します。"</string> <string name="permlab_videoWrite" msgid="5940738769586451318">"動画コレクションの変更"</string> @@ -567,7 +567,7 @@ <string name="fingerprint_authenticated" msgid="2024862866860283100">"指紋認証を完了しました"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"顔を認証しました"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"顔を認証しました。[確認] を押してください"</string> - <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"指紋ハードウェアは使用できません。"</string> + <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"指紋認証ハードウェアは使用できません。"</string> <string name="fingerprint_error_no_space" msgid="6126456006769817485">"指紋を保存できません。既存の指紋を削除してください。"</string> <string name="fingerprint_error_timeout" msgid="2946635815726054226">"指紋の読み取りがタイムアウトになりました。もう一度お試しください。"</string> <string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋の操作をキャンセルしました。"</string> @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"このデバイスには指紋認証センサーがありません。"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"センサーが一時的に無効になっています。"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"指紋 <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"続行するには指紋認証を使用してください"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指紋アイコン"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"顔を認識できなくなりました。もう一度お試しください。"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"似すぎています。ポーズを変えてください。"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"顔の向きを少し戻してください。"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"顔の向きを少し戻してください。"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"顔の向きを少し戻してください。"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"顔を隠しているものをすべて外してください"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"黒いバーを含め、画面の上部をきれいにしてください"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"サイレント モード設定の読み取りと書き込みをアプリに許可します。"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"表示権限の使用の開始"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"アプリの権限使用の開始を所有者に許可します。通常のアプリでは不要です。"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"高サンプリング レートでセンサーデータにアクセスする"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz を超えるレートでセンサーデータをサンプリングすることをアプリに許可します"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"パスワードルールの設定"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"画面ロックのパスワードとPINの長さと使用できる文字を制御します。"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"画面ロック解除試行の監視"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ショートカットを使用"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"色反転"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"色補正"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が ON になりました。"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が OFF になりました。"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> を使用するには、音量大と音量小の両方のボタンを 3 秒間長押ししてください"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS リクエストはビデオ通話に変更されました"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS リクエストは USSD リクエストに変更されました"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"新しい SS リクエストに変更されました"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"フィッシングに関する警告"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"仕事用プロファイル"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"アラートとして送信済み"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"展開"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"最大化"</string> <string name="close_button_text" msgid="10603510034455258">"閉じる"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"応答"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"拒否"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"通話終了"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"着信"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"通話中"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"着信をスクリーニング中"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>件選択済み</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g>件選択済み</item> @@ -2060,9 +2073,9 @@ <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"電源ダイアログ"</string> <string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"ロック画面"</string> <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"スクリーンショット"</string> - <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"画面上のユーザー補助のショートカット"</string> - <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"画面上のユーザー補助のショートカットの選択メニュー"</string> - <string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ユーザー補助のショートカット"</string> + <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"画面上のユーザー補助機能のショートカット"</string> + <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"画面上のユーザー補助機能のショートカットの選択メニュー"</string> + <string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"ユーザー補助機能のショートカット"</string> <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> のキャプション バーです。"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> は RESTRICTED バケットに移動しました。"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"続行するには、<b><xliff:g id="APP">%s</xliff:g></b> にデバイスのカメラへのアクセスを許可する必要があります。"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ON にする"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"センサー プライバシー"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 6a7d563c2dc7..c25bd5757526 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"სენსორი დროებით გათიშულია."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"თითი <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"გასაგრძელებლად გამოიყენეთ თქვენი თითის ანაბეჭდი"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"თითის ანაბეჭდის ხატულა"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"სახის ამოცნობა ვეღარ ხერხდება. ცადეთ ხელახლა."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"მეტისმეტად მსგავსია. გთხოვთ, შეცვალოთ პოზა."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"თავი ცოტა ნაკლებად მიაბრუნეთ."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"თავი ცოტა ნაკლებად მიაბრუნეთ."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"თავი ცოტა ნაკლებად მიაბრუნეთ."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"მოაშორეთ ყველაფერი, რაც სახეს გიფარავთ."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"გაწმინდეთ ეკრანის ზედა ნაწილი, შავი ზოლის ჩათვლით."</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"საშუალებას აძლევს აპს, წაიკითხოს და დაწეროს კონფიგურაცია „არ შემაწუხოთ“."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ნახვის ნებართვის გამოყენების დაწყება"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"მფლობელს საშუალებას აძლევს, დაიწყოს აპის ნებართვის გამოყენება. ჩვეულებრივი აპებისთვის არასოდეს უნდა იყოს საჭირო."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"სენსორის მონაცემებზე წვდომა სემპლინგის მაღალი სიხშირით"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"საშუალებას აძლევს აპს, მიიღოს სენსორის მონაცემების ნიმუშები 200 ჰც-ზე მეტი სიხშირით"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"პაროლის წესების დაყენება"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"აკონტროლეთ ეკრანის ბლოკირების პაროლებისა და PIN-ების სიმბოლოების სიგრძე."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"ეკრანის განბლოკვის მცდელობების მონიტორინგი"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"მალსახმობის გამოყენება"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"ფერთა ინვერსია"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"ფერთა კორექცია"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ხანგრძლივად დააჭირეთ ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ჩართულია."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ხანგრძლივად დააჭირეთ ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> გამორთულია."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> რომ გამოიყენოთ, დააჭირეთ ხმის ორივე ღილაკზე 3 წამის განმავლობაში"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS მოთხოვნა შეიცვალა ვიდეოზარის მოთხოვნით"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS მოთხოვნა შეიცვალა USSD მოთხოვნით"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"შეიცვალა ახალი SS მოთხოვნით"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"გაფრთხილება ფიშინგის შესახებ"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"სამსახურის პროფილი"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"გაფრთხილებით"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"გაშლა"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"მაქსიმალური ზომა"</string> <string name="close_button_text" msgid="10603510034455258">"დახურვა"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"პასუხი"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"უარყოფა"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"გათიშვა"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"შემომავალი ზარი"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"მიმდინარე ზარი"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"შემომავალი ზარების გაცხრილვა"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> შერჩეული</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> შერჩეული</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"გასაგრძელებლად <b><xliff:g id="APP">%s</xliff:g></b>-ს თქვენი მოწყობილობის კამერაზე წვდომა სჭირდება."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ჩართვა"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"სენსორის კონფიდენციალურობა"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index aec2312c0a21..68edfeac6eb1 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Тек Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> Қосалқы қоңырау шалу"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Басқа нөмірге бағытталмады"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секундтан кейін"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бұл құрылғыда саусақ ізін оқу сканері жоқ."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик уақытша өшірулі."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> саусағы"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Жалғастыру үшін саусақ ізін пайдаланыңыз."</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Саусақ ізі белгішесі"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Енді бет анықтау мүмкін емес. Әрекетті қайталаңыз."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Алдыңғысына тым ұқсас, басқаша қалыпта түсіңіз."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Басыңызды түзурек ұстаңыз."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Басыңызды түзурек ұстаңыз."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Басыңызды кішкене бұрыңыз."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Бетіңізді жауып тұрған нәрсені алып тастаңыз."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Экранның жоғарғы жағын, сонымен қатар қара жолақты өшіріңіз."</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Қолданбаға «Мазаламау» конфигурациясын оқу және жазу мүмкіндігін береді."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"рұқсаттарды пайдалану туралы деректерді көру"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Пайдаланушы қолданбаға берілетін рұқсаттарды басқара алады. Ондай рұқсаттар әдеттегі қолданбаларға керек емес."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"жоғары дискретизация жиілігіндегі датчик деректерін пайдалану"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Қолданбаға жиілігі 200 Гц-тен жоғары датчик деректерінің үлгісін таңдауға рұқсат береді."</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Құпия сөз ережелерін тағайындау"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Экран бекітпесінің құпия сөздерінің және PIN кодтарының ұзындығын және оларда рұқсат етілген таңбаларды басқару."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Экран құлпын ашу әркеттерін бақылау"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Төте жолды пайдалану"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Түстер инверсиясы"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Түсті түзету"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Пайдаланушы дыбыс деңгейі пернелерін басып ұстап тұрды. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қосулы."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дыбыс деңгейі пернелерін басып тұрған соң, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өшірілді."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін пайдалану үшін дыбыс деңгейін реттейтін екі түймені де 3 секунд басып тұрыңыз"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS сұрауы бейне қоңырауға өзгертілді"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS сұрауы USSD сұрауына өзгертілді"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Жаңа SS сұрауына өзгертілді"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Фишинг ескертуі"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Жұмыс профилі"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Ескертілді"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Жаю"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Жазу"</string> <string name="close_button_text" msgid="10603510034455258">"Жабу"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Жауап"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Қабылдамау"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Тұтқаны қою"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Кіріс қоңырау"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Қоңырау"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Келген қоңырауды сүзу"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> таңдалды</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> таңдалды</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Жалғастыру үшін <b><xliff:g id="APP">%s</xliff:g></b> қолданбасы құрылғыңыздың камерасына рұқсат алу керек."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Қосу"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Датчикке қатысты құпиялылық"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index d1400b9d17ff..b3516d5c353c 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ឧបករណ៍នេះមិនមានឧបករណ៍ចាប់ស្នាមម្រាមដៃទេ។"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"បានបិទឧបករណ៍ចាប់សញ្ញាជាបណ្តោះអាសន្ន។"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ម្រាមដៃ <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ប្រើស្នាមម្រាមដៃរបស់អ្នក ដើម្បីបន្ត"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"រូបស្នាមម្រាមដៃ"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"មិនអាចសម្គាល់មុខបានទៀតទេ។ សូមព្យាយាមម្ដងទៀត។"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"ស្រដៀងគ្នាពេក សូមផ្លាស់ប្ដូរកាយវិការរបស់អ្នក។"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"ងាកក្បាលរបស់អ្នកតិចជាងមុនបន្តិច។"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"ងាកក្បាលរបស់អ្នកតិចជាងមុនបន្តិច។"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"ងាកក្បាលរបស់អ្នកបន្តិចទៀត។"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"យកអ្វីដែលបាំងមុខរបស់អ្នកចេញ។"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"សម្អាតផ្នែកខាងលើនៃអេក្រង់របស់អ្នក រួមទាំងរបារខ្មៅ"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"អនុញ្ញាតឲ្យកម្មវិធីអាន និងសរសេរការកំណត់រចនាសម្ព័ន្ធមុខងារ កុំរំខាន។"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ចាប់ផ្ដើមមើលការប្រើប្រាស់ការអនុញ្ញាត"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"អនុញ្ញាតឱ្យម្ចាស់ចាប់ផ្ដើមការប្រើប្រាស់ការអនុញ្ញាតសម្រាប់កម្មវិធី។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ចូលប្រើទិន្នន័យឧបករណ៍ចាប់សញ្ញានៅអត្រាសំណាកខ្ពស់"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"អនុញ្ញាតឱ្យកម្មវិធីធ្វើសំណាកទិន្នន័យឧបករណ៍ចាប់សញ្ញានៅអត្រាលើសពី 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"កំណត់ក្បួនពាក្យសម្ងាត់"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"គ្រប់គ្រងប្រវែង និងតួអក្សរដែលអនុញ្ញាតឲ្យប្រើក្នុងពាក្យសម្ងាត់ និងលេខសម្ងាត់ចាក់សោអេក្រង់។"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"តាមដានការព្យាយាមដោះសោអេក្រង់"</string> @@ -1108,7 +1112,7 @@ <string name="cut" msgid="2561199725874745819">"កាត់"</string> <string name="copy" msgid="5472512047143665218">"ចម្លង"</string> <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"មិនអាចចម្លងទៅអង្គចងចាំទេ"</string> - <string name="paste" msgid="461843306215520225">"បិទភ្ជាប់"</string> + <string name="paste" msgid="461843306215520225">"ដាក់ចូល"</string> <string name="paste_as_plain_text" msgid="7664800665823182587">"បិទភ្ជាប់ជាអត្ថបទធម្មតា"</string> <string name="replace" msgid="7842675434546657444">"ជំនួស..."</string> <string name="delete" msgid="1514113991712129054">"លុប"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ប្រើប្រាស់ផ្លូវកាត់"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"បញ្ច្រាសពណ៌"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"ការកែពណ៌"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"បានសង្កត់គ្រាប់ចុចកម្រិតសំឡេងជាប់។ បានបើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"បានសង្កត់គ្រាប់ចុចកម្រិតសំឡេងជាប់។ បានបិទ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"ចុចគ្រាប់ចុចកម្រិតសំឡេងទាំងពីរឱ្យជាប់រយៈពេលបីវិនាទី ដើម្បីប្រើ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"សំណើ SS ត្រូវបានប្ដូរទៅការហៅជាវីដេអូ"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"សំណើ SS ត្រូវបានប្ដូរទៅសំណើ USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"បានប្ដូរទៅសំណើ SS ថ្មី"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ការជូនដំណឹងអំពីការដាក់នុយ"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"ប្រវត្តិរូបការងារ"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"បានជូនដំណឹង"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ពង្រីក"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"ពង្រីក"</string> <string name="close_button_text" msgid="10603510034455258">"បិទ"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>៖ <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"ឆ្លើយ"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"បដិសេធ"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ដាក់ចុះ"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"ការហៅចូល"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"ការហៅដែលកំពុងដំណើរការ"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"កំពុងពិនិត្យការហៅចូល"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other">បានជ្រើស <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="one">បានជ្រើស <xliff:g id="COUNT_0">%1$d</xliff:g></item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ដើម្បីបន្ត <b><xliff:g id="APP">%s</xliff:g></b> ត្រូវការសិទ្ធិចូលប្រើកាមេរ៉ារបស់ឧបករណ៍អ្នក។"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"បើក"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ឯកជនភាពឧបករណ៍ចាប់សញ្ញា"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 32e3c4d98578..22bdd772856a 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ಈ ಸಾಧನವು ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಹೊಂದಿಲ್ಲ."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ಸೆನ್ಸಾರ್ ಅನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ಫಿಂಗರ್ <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಬಳಸಿ"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಐಕಾನ್"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"ಮುಖ ಗುರುತಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"ತುಂಬಾ ಸಮಾನ, ನಿಮ್ಮ ಪೋಸ್ ಬದಲಾಯಿಸಿ."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"ನಿಮ್ಮ ತಲೆಯನ್ನು ಹೆಚ್ಚು ತಿರುಗಿಸಬೇಡಿ."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"ನಿಮ್ಮ ತಲೆಯನ್ನು ಹೆಚ್ಚು ತಿರುಗಿಸಬೇಡಿ."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"ನಿಮ್ಮ ತಲೆಯನ್ನು ಸ್ವಲ್ಪ ಕಡಿಮೆ ತಿರುಗಿಸಿ."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"ನಿಮ್ಮ ಮುಖವನ್ನು ಮರೆಮಾಡುವ ಯಾವುದನ್ನಾದರೂ ತೆಗೆದುಹಾಕಿ."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"ಬ್ಲ್ಯಾಕ್ ಬಾರ್ ಸೇರಿದಂತೆ ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ನ ಮೇಲ್ಭಾಗವನ್ನು ತೆರವುಗೊಳಿಸಿ"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಕಾನ್ಫಿಗರೇಶನ್ ಅನ್ನು ಓದಲು ಮತ್ತು ಬರೆಯಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ವೀಕ್ಷಣಾ ಅನುಮತಿಯ ಬಳಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿ"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ಆ್ಯಪ್ಗಾಗಿ ಅನುಮತಿ ಬಳಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಲು ಹೊಂದಿರುವವರಿಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಸಾಮಾನ್ಯ ಆ್ಯಪ್ಗಳಿಗೆ ಎಂದಿಗೂ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ಹೆಚ್ಚಿನ ನಮೂನೆ ದರದಲ್ಲಿ ಸೆನ್ಸಾರ್ ಡೇಟಾ ಪ್ರವೇಶಿಸಿ"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz ಗಿಂತಲೂ ಹೆಚ್ಚಿನ ವೇಗದಲ್ಲಿ ಸೆನ್ಸಾರ್ ಡೇಟಾದ ಮಾದರಿ ಪರೀಕ್ಷಿಸಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಿ"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"ಪಾಸ್ವರ್ಡ್ ನಿಮಯಗಳನ್ನು ಹೊಂದಿಸಿ"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"ಪರದೆ ಲಾಕ್ನಲ್ಲಿನ ಪಾಸ್ವರ್ಡ್ಗಳು ಮತ್ತು ಪಿನ್ಗಳ ಅನುಮತಿಸಲಾದ ಅಕ್ಷರಗಳ ಪ್ರಮಾಣವನ್ನು ನಿಯಂತ್ರಿಸಿ."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"ಪರದೆಯ ಅನ್ಲಾಕ್ ಪ್ರಯತ್ನಗಳನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಿ"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ಶಾರ್ಟ್ಕಟ್ ಬಳಸಿ"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"ಬಣ್ಣ ವಿಲೋಮ"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಲಾಗಿದೆ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಬಳಸಲು ಎರಡೂ ಧ್ವನಿ ಕೀಗಳನ್ನು ಮೂರು ಸೆಕೆಂಡ್ಗಳ ಕಾಲ ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS ವಿನಂತಿಯನ್ನು ವೀಡಿಯೊ ಕರೆಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS ವಿನಂತಿಯನ್ನು USSD ವಿನಂತಿಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"ಹೊಸ SS ವಿನಂತಿಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ಫಿಶಿಂಗ್ ಕುರಿತು ಎಚ್ಚರಿಕೆ"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"ಎಚ್ಚರಿಕೆ ನೀಡಲಾಗಿದೆ"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ವಿಸ್ತೃತಗೊಳಿಸಿ"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"ಹಿಗ್ಗಿಸು"</string> <string name="close_button_text" msgid="10603510034455258">"ಮುಚ್ಚು"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"ಉತ್ತರಿಸಿ"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"ನಿರಾಕರಿಸಿ"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ಹ್ಯಾಂಗ್ ಅಪ್"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"ಒಳಬರುವ ಕರೆ"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಕರೆ"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"ಒಳಬರುವ ಕರೆಯನ್ನು ಸ್ಕ್ರೀನ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ಮುಂದುವರಿಯಲು, <b><xliff:g id="APP">%s</xliff:g></b> ಗೆ ನಿಮ್ಮ ಸಾಧನದ ಕ್ಯಾಮರಾದ ಪ್ರವೇಶದ ಅಗತ್ಯವಿದೆ."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ಆನ್ ಮಾಡಿ"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ಸೆನ್ಸರ್ ಗೌಪ್ಯತೆ"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 34074312e70d..52f0a7c9c2ae 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"기기에 지문 센서가 없습니다."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"센서가 일시적으로 사용 중지되었습니다."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"손가락 <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"계속하려면 지문을 사용하세요."</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"지문 아이콘"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"더 이상 얼굴을 인식할 수 없습니다. 다시 시도하세요."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"너무 비슷합니다. 다른 포즈를 취해 보세요."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"고개를 조금 덜 돌려 보세요."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"고개를 조금 덜 돌려 보세요."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"고개를 조금 덜 돌려 보세요."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"얼굴이 가려지지 않도록 해 주세요."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"검은색 바를 포함한 화면 상단을 청소하세요."</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"앱에서 방해 금지 모드 설정을 읽고 작성하도록 허용합니다."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"권한 사용 보기 시작"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"앱의 권한 사용을 시작하려면 보유자를 허용하세요. 일반 앱에는 필요하지 않습니다."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"더 높은 샘플링 레이트로 센서 데이터 액세스"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"앱에서 200Hz보다 빠른 속도로 센서 데이터를 샘플링하도록 허용합니다."</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"비밀번호 규칙 설정"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"화면 잠금 비밀번호와 PIN에 허용되는 길이와 문자 수를 제어합니다."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"화면 잠금 해제 시도 모니터링"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"단축키 사용"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"색상 반전"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"색상 보정"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 서비스를 사용하려면 두 볼륨 키를 3초 동안 길게 누르세요"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS 요청이 화상 통화로 변경됨"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS 요청이 USSD 요청으로 변경됨"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"새 SS 요청으로 변경됨"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"피싱 알림"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"직장 프로필"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"알림 전송됨"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"펼치기"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"최대화"</string> <string name="close_button_text" msgid="10603510034455258">"닫기"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"답변"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"거절"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"전화 끊기"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"수신 전화"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"진행 중인 통화"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"수신 전화 검사 중"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>개 선택됨</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g>개 선택됨</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"계속하려면 <b><xliff:g id="APP">%s</xliff:g></b>에서 기기 카메라에 액세스해야 합니다."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"사용"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"센서 개인정보 보호"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 9a84d0a985e6..4d8888ebdb9a 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -298,7 +298,7 @@ <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Батареянын кубаты жана трафиктин көлөмү жөнүндө билүү үчүн таптап коюңуз"</string> <string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string> <string name="safeMode" msgid="8974401416068943888">"Коопсуз режим"</string> - <string name="android_system_label" msgid="5974767339591067210">"Android тутуму"</string> + <string name="android_system_label" msgid="5974767339591067210">"Android системасы"</string> <string name="user_owner_label" msgid="8628726904184471211">"Жеке профилге которулуу"</string> <string name="managed_profile_label" msgid="7316778766973512382">"Жумуш профилине которулуу"</string> <string name="permgrouplab_contacts" msgid="4254143639307316920">"Байланыштар"</string> @@ -314,7 +314,7 @@ <string name="permgrouplab_microphone" msgid="2480597427667420076">"Микрофон"</string> <string name="permgroupdesc_microphone" msgid="1047786732792487722">"аудио жаздыруу"</string> <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Кыймыл-аракет"</string> - <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"кыймыл-аракетиңизге мүмкүнчүлүк алат"</string> + <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"кыймыл-аракеттериңизге көз салып турганга мүмкүнчүлүк алат"</string> <string name="permgrouplab_camera" msgid="9090413408963547706">"Камера"</string> <string name="permgroupdesc_camera" msgid="7585150538459320326">"сүрөт жана видео тартууга"</string> <string name="permgrouplab_calllog" msgid="7926834372073550288">"Чалуулар тизмеси"</string> @@ -332,7 +332,7 @@ <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Көрүнүштү чоңойтуп кичирейтет"</string> <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Экрандагы сүрөттү тууралап жайгаштырып, өлчөмүн өзгөртөт."</string> <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Жаңсоолорду аткаруу"</string> - <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Таптап, серпип, чымчып жана башка жаңсоолорду аткара алат."</string> + <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Таптап, сүрүп, чымчып жана башка жаңсоолорду аткара алат."</string> <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Манжа изинин жаңсоолору"</string> <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Түзмөктөгү манжа изинин сенсорунда жасалган жаңсоолорду жаздырып алат."</string> <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Скриншот тартып алуу"</string> @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бул түзмөктө манжа изинин сенсору жок."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сенсор убактылуу өчүрүлгөн."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>-манжа"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Улантуу үчүн манжаңыздын изин колдонуңуз"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Манжа изинин сүрөтчөсү"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Жүз таанылган жок. Кайталап көрүңүз."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Мурункуга окшош болуп калды, башкача туруңуз."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Башыңызды бир аз гана эңкейтиңиз."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Башыңызды бир аз гана эңкейтиңиз."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Башыңызды бир аз гана эңкейтиңиз."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Жүзүңүздү жашырып турган нерселерди алып салыңыз."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Экраныңыздын жогору жагын, анын ичинде тилкени да тазалаңыз"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Колдонмого \"Тынчымды алба\" режиминин конфигурациясын окуу жана жазуу мүмкүнчүлүгүн берет."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"уруксаттын колдонулушун көрүп баштоо"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Колдонмонун пайдаланылышына уруксат берүүгө мүмкүнчүлүк берет. Кадимки колдонмолорго эч качан талап кылынбашы керек."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"үлгүнү жаздыруу ылдамдыгы жогору болгон сенсор дайындарынын үлгүсүнө мүмкүнчүлүк алуу"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Колдонмолорго сенсор дайындарынын үлгүсү 200 Герцтен жогору болгон үлгүлөрдү алууга уруксат берет"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Сырсөз эрежелерин коюу"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Экран кулпусунун сырсөздөрү менен PIN\'дерине уруксат берилген узундук менен белгилерди көзөмөлдөө."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Экран кулпусун ачуу аракеттерин көзөмөлдөө"</string> @@ -886,7 +890,7 @@ <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Сиз телефонду бөгөттөн чыгарууга <xliff:g id="NUMBER">%d</xliff:g> жолу туура эмес аракет кылдыңыз. Телефон баштапкы абалына келтирилет."</string> <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"<xliff:g id="NUMBER">%d</xliff:g> секунддан кийин кайталаңыз."</string> <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Сүрөт үлгүсүн унутуп калдыңызбы?"</string> - <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Каттоо эсеби менен кулпусун ачуу"</string> + <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Аккаунт менен кулпусун ачуу"</string> <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Өтө көп үлгү киргизүү аракети болду"</string> <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Бөгөттөн чыгарыш үчүн, Google эсебиңиз менен кириңиз."</string> <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"Колдонуучунун аты (электрондук почта)"</string> @@ -1203,7 +1207,7 @@ <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Жаңыртууну издөө"</string> <string name="smv_application" msgid="3775183542777792638">"<xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу (<xliff:g id="PROCESS">%2$s</xliff:g> процесси) өз алдынча иштеткен StrictMode саясатын бузду."</string> <string name="smv_process" msgid="1398801497130695446">"<xliff:g id="PROCESS">%1$s</xliff:g> процесси өзүнүн мажбурланган StrictMode саясатын бузуп койду."</string> - <string name="android_upgrading_title" product="default" msgid="7279077384220829683">"Телефон жаңыртылууда…"</string> + <string name="android_upgrading_title" product="default" msgid="7279077384220829683">"Телефон жаңырууда…"</string> <string name="android_upgrading_title" product="tablet" msgid="4268417249079938805">"Планшет жаңыртылууда…"</string> <string name="android_upgrading_title" product="device" msgid="6774767702998149762">"Түзмөк жаңыртылууда…"</string> <string name="android_start_title" product="default" msgid="4036708252778757652">"Телефон күйгүзүлүүдө…"</string> @@ -1213,7 +1217,7 @@ <string name="android_upgrading_fstrim" msgid="3259087575528515329">"Сактагыч ыңгайлаштырылууда."</string> <string name="android_upgrading_notification_title" product="default" msgid="3509927005342279257">"Тутумду жаңыртуу аяктоодо…"</string> <string name="app_upgrading_toast" msgid="1016267296049455585">"<xliff:g id="APPLICATION">%1$s</xliff:g> жаңыртылууда..."</string> - <string name="android_upgrading_apk" msgid="1339564803894466737">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ичинен <xliff:g id="NUMBER_0">%1$d</xliff:g> колдонмо ыңгайлаштырылууда."</string> + <string name="android_upgrading_apk" msgid="1339564803894466737">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ичинен <xliff:g id="NUMBER_0">%1$d</xliff:g> колдонмо оптималдаштырылууда."</string> <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> даярдалууда."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Колдонмолорду иштетип баштоо"</string> <string name="android_upgrading_complete" msgid="409800058018374746">"Жүктөлүүдө"</string> @@ -1664,12 +1668,14 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Кыска жолду колдонуу"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Түстү инверсиялоо"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Түсүн тууралоо"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> күйгүзүлдү."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өчүрүлдү."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын колдонуу үчүн үнүн чоңойтуп/кичирейтүү баскычтарын үч секунд коё бербей басып туруңуз"</string> <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Атайын мүмкүнчүлүктөр баскычын таптаганыңызда иштей турган функцияны тандаңыз:"</string> - <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Атайын мүмкүнчүлүктөр жаңсоосу үчүн функцияны тандаңыз (эки манжаңыз менен экрандын ылдый жагынан өйдө карай сүрүңүз):"</string> - <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Атайын мүмкүнчүлүктөр жаңсоосу аркылуу иштетиле турган функцияны тандаңыз (үч манжаңыз менен экрандын ылдый жагынан өйдө карай сүрүңүз):"</string> + <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Атайын мүмкүнчүлүктөр жаңсоосу үчүн функцияны тандаңыз (эки манжаңыз менен экранды ылдыйдан өйдө сүрүңүз):"</string> + <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Атайын мүмкүнчүлүктөр жаңсоосу аркылуу иштетиле турган функцияны тандаңыз (үч манжаңыз менен экранды ылдыйдан өйдө сүрүңүз):"</string> <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Функцияларды которуштуруу үчүн, Атайын мүмкүнчүлүктөр баскычын басып, кармап туруңуз."</string> <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Функцияларды которуштуруу үчүн, эки манжаңыз менен өйдө сүрүп, кармап туруңуз."</string> <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Башка функцияга которулуу үчүн үч манжаңыз менен экранды өйдө сүрүп, кармап туруңуз."</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS сурамы видео чалууга өзгөртүлдү"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS сурамы USSD сурамына өзгөртүлдү"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Жаңы SS сурамына өзгөртүлдү"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Фишинг жөнүндө эскертүү"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Жумуш профили"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Эскертилди"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Жайып көрсөтүү"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Чоңойтуу"</string> <string name="close_button_text" msgid="10603510034455258">"Жабуу"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Жооп берүү"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Четке кагуу"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Чалууну бүтүрүү"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Кирүүчү чалуу"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Учурдагы чалуу"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Кирүүчү чалууну иргөө"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> тандалды</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> тандалды</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Улантуу үчүн <b><xliff:g id="APP">%s</xliff:g></b> колдонмосуна түзмөгүңүздүн камерасын пайдаланууга уруксат беришиңиз керек."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Күйгүзүү"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Сенсордун купуялыгы"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml index 9e87a47219f3..42c2c6912d81 100644 --- a/core/res/res/values-land/dimens.xml +++ b/core/res/res/values-land/dimens.xml @@ -78,4 +78,5 @@ <dimen name="chooser_preview_width">480dp</dimen> + <dimen name="toast_y_offset">24dp</dimen> </resources> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 9977f9fca122..87c2cebe78fa 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi ເທົ່ານັ້ນ"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ການໂທສຳຮອງ"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ບໍ່ຖືກສົ່ງຕໍ່"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> ຫຼັງຈາກ <xliff:g id="TIME_DELAY">{2}</xliff:g> ວິນາທີ"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ປິດການເຮັດວຽກຂອງເຊັນເຊີໄວ້ຊົ່ວຄາວແລ້ວ."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ນີ້ວມື <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ໃຊ້ລາຍນີ້ວມືຂອງທ່ານເພື່ອສືບຕໍ່"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ໄອຄອນລາຍນິ້ວມື"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"ບໍ່ສາມາດຈຳແນກໃບໜ້າໄດ້ອີກຕໍ່ໄປ. ກະລຸນາລອງໃໝ່."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"ຄ້າຍກັນເກີນໄປ, ກະລຸນາປ່ຽນທ່າຂອງທ່ານ."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"ອຽງຫົວຂອງທ່ານໜ້ອຍໜຶ່ງ."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"ອຽງຫົວຂອງທ່ານໜ້ອຍໜຶ່ງ."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"ອຽງຫົວຂອງທ່ານໜ້ອຍໜຶ່ງ."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"ນຳສິ່ງທີ່ກີດຂວາງໃບໜ້າທ່ານອອກ."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"ທຳຄວາມສະອາດສ່ວນເທິງສຸດຂອງໜ້າຈໍທ່ານ, ຮວມທັງແຖບດຳນຳ"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ອະນຸຍາດໃຫ້ແອັບອ່ານ ແລະຂຽນການກນຳດຄ່າ ບໍ່ລົບກວນ."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ເລີ່ມການໃຊ້ສິດອະນຸຍາດການເບິ່ງ"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ອະນຸຍາດໃຫ້ຜູ້ຖືເລີ່ມການໃຊ້ສິດອະນຸຍາດສຳລັບແອັບໃດໜຶ່ງໄດ້. ແອັບປົກກະຕິບໍ່ຄວນຕ້ອງໃຊ້."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ເຂົ້າເຖິງຂໍ້ມູນເຊັນເຊີໃນອັດຕາຕົວຢ່າງສູງ"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"ອະນຸຍາດໃຫ້ແອັບສຸ່ມຕົວຢ່າງຂໍ້ມູນເຊັນເຊີໃນອັດຕາທີ່ຫຼາຍກວ່າ 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"ຕັ້ງຄ່າກົດຂອງລະຫັດຜ່ານ"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"ຄວບຄຸມຄວາມຍາວ ແລະຕົວອັກສອນທີ່ອະນຸຍາດໃຫ້ຢູ່ໃນລະຫັດລັອກໜ້າຈໍ ແລະ PIN."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"ຕິດຕາມການພະຍາຍາມປົດລັອກໜ້າຈໍ"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ໃຊ້ປຸ່ມລັດ"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"ການປີ້ນສີ"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"ການແກ້ໄຂຄ່າສີ"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ກົດປຸ່ມລະດັບສຽງຄ້າງໄວ້. ເປີດໃຊ້ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ແລ້ວ."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ກົດປຸ່ມລະດັບສຽງຄ້າງໄວ້. ປິດ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ໄວ້ແລ້ວ."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"ກົດປຸ່ມສຽງທັງສອງພ້ອມກັນຄ້າງໄວ້ສາມວິນາທີເພື່ອໃຊ້ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"ປ່ຽນການຮ້ອງຂໍ SS ເປັນການໂທວິດີໂອແລ້ວ"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"ປ່ຽນຄຳຂໍ SS ເປັນຄຳຂໍ USSD ແລ້ວ"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"ປ່ຽນຄຳຂໍ SS ໃໝ່ແລ້ວ"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ການແຈ້ງເຕືອນການຫຼອກເອົາຂໍ້ມູນ"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"ເຕືອນແລ້ວ"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ຂະຫຍາຍ"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"ຂະຫຍາຍອອກ"</string> <string name="close_button_text" msgid="10603510034455258">"ປິດ"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"ຮັບສາຍ"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"ປະຕິເສດ"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ວາງສາຍ"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"ສາຍໂທເຂົ້າ"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"ສາຍໂທອອກ"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"ກຳລັງກວດສອບສາຍໂທເຂົ້າ"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ຖືກເລືອກແລ້ວ</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ຖືກເລືອກແລ້ວ</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ເພື່ອດຳເນີນການຕໍ່, <b><xliff:g id="APP">%s</xliff:g></b> ຕ້ອງການສິດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບຂອງອຸປະກອນທ່ານ."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ເປີດໃຊ້"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ຄວາມເປັນສ່ວນຕົວເຊັນເຊີ"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 5f72e07a6959..2c3742baeae6 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -585,6 +585,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šiame įrenginyje nėra kontrolinio kodo jutiklio."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Jutiklis laikinai išjungtas."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> pirštas"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Naudokite kontrolinį kodą, kad galėtumėte tęsti"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Piršto antspaudo piktograma"</string> @@ -611,7 +612,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Nebegalima atpažinti veido. Bandykite dar kartą."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Per daug panašu, pakeiskite veido išraišką."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Nesukite tiek galvos."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Nesukite tiek galvos."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Nesukite tiek galvos."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Patraukite viską, kas užstoja jūsų veidą."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Išvalykite ekrano viršų, įskaitant juodą juostą"</string> @@ -690,6 +692,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Leidžiama programai skaityti ir rašyti „Do Not Disturb“ konfigūraciją."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"pradėti peržiūrėti leidimo naudojimą"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Leidžia savininkui pradėti naudoti programos leidimą. Įprastoms programoms to neturėtų prireikti."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"pasiekti jutiklių duomenis dideliu skaitmeninimo dažniu"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Programai leidžiama skaitmeninti jutiklių duomenis didesniu nei 200 Hz dažniu"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Nustatyti slaptažodžio taisykles"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Valdykite, kokio ilgio ekrano užrakto slaptažodžius ir PIN kodus galima naudoti."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Stebėti bandymus atrakinti ekraną"</string> @@ -1708,6 +1712,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Naudoti spartųjį klavišą"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Spalvų inversija"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Spalvų taisymas"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ įjungta."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ išjungta."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Jei norite naudoti „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“, paspauskite abu garsumo klavišus ir palaikykite tris sekundes"</string> @@ -1929,6 +1935,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS užklausa pakeista į vaizdo skambutį"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS užklausa pakeista į USSD užklausą"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Pakeista į naują SS užklausą"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Įspėjimas apie sukčiavimą"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Darbo profilis"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Įspėjimas išsiųstas"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Išskleisti"</string> @@ -1942,6 +1949,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Padidinti"</string> <string name="close_button_text" msgid="10603510034455258">"Uždaryti"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Atsakyti"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Atmesti"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Baigti pok."</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Gaunamasis skambutis"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Vykstantis skambutis"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Gaunamojo skambučio tikrinimas"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one">Pasir. <xliff:g id="COUNT_1">%1$d</xliff:g> elem.</item> <item quantity="few">Pasir. <xliff:g id="COUNT_1">%1$d</xliff:g> elem.</item> @@ -2276,4 +2289,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Kad būtų galima tęsti, <b><xliff:g id="APP">%s</xliff:g></b> reikalinga prieiga prie įrenginio fotoaparato."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Įjungti"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Jutiklių privatumas"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 37e417fa6a56..8d6ce865ec7c 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -582,6 +582,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šajā ierīcē nav pirksta nospieduma sensora."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensors ir īslaicīgi atspējots."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. pirksts"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Lai turpinātu, izmantojiet pirksta nospiedumu"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Pirksta nospieduma ikona"</string> @@ -608,7 +609,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Seju vairs nevar atpazīt. Mēģiniet vēlreiz."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Pārāk līdzīgi. Lūdzu, mainiet pozu."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Pagrieziet galvu nedaudz mazāk."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Pagrieziet galvu nedaudz mazāk."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Pagrieziet galvu nedaudz mazāk."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Noņemiet visu, kas aizsedz jūsu seju."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Notīriet ekrāna augšdaļu, tostarp melno joslu."</string> @@ -687,6 +689,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ļauj lietotnei lasīt un rakstīt režīma “Netraucēt” konfigurāciju."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Datu skatīšana par izmantojamajām atļaujām"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ļauj atļaujas īpašniekam sākt lietotnes atļauju izmantošanu. Parastām lietotnēm tas nekad nav nepieciešams."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"piekļuve sensoru datiem, izmantojot augstu iztveršanas frekvenci"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Ļauj lietotnei iztvert sensoru datus, izmantojot frekvenci, kas ir augstāka par 200 Hz."</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Paroles kārtulu iestatīšana"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolēt ekrāna bloķēšanas paroļu un PIN garumu un tajos atļautās rakstzīmes."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Ekrāna atbloķēšanas mēģinājumu pārraudzīšana"</string> @@ -1686,6 +1690,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Izmantot saīsni"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Krāsu inversija"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Krāsu korekcija"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika ieslēgts."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika izslēgts."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Lai izmantotu pakalpojumu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, nospiediet abus skaļuma taustiņus un turiet tos trīs sekundes."</string> @@ -1898,6 +1904,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS pieprasījums mainīts uz videozvanu"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS pieprasījums mainīts uz USSD pieprasījumu"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Mainīts uz jaunu SS pieprasījumu"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Brīdinājums par pikšķerēšanu"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Darba profils"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Brīdināts"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Izvērst"</string> @@ -1911,6 +1918,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maksimizēt"</string> <string name="close_button_text" msgid="10603510034455258">"Aizvērt"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Atbildēt"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Noraidīt"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Pārtraukt"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Ienākošais zvans"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Pašreizējais zvans"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Ienākošā zvana filtrēšana"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="zero"><xliff:g id="COUNT_1">%1$d</xliff:g> atlasīti</item> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> atlasīts</item> @@ -2242,4 +2255,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Lai turpinātu, lietotnei <b><xliff:g id="APP">%s</xliff:g></b> nepieciešama piekļuve jūsu ierīces kamerai."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Ieslēgt"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensoru konfidencialitāte"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-mcc310-mnc950-si/strings.xml b/core/res/res/values-mcc310-mnc950-si/strings.xml new file mode 100644 index 000000000000..26fe4ac265a3 --- /dev/null +++ b/core/res/res/values-mcc310-mnc950-si/strings.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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="mmcc_imsi_unknown_in_hlr" msgid="615419724607901560">"SIM MM#2 ප්රතිපාදනය නොකරයි"</string> + <string name="mmcc_illegal_ms" msgid="7801541624846497489">"SIM MM#3 ඉඩ නොදේ"</string> + <string name="mmcc_illegal_me" msgid="7066936962628406316">"දුරකථනය MM#6 ඉඩ නොදේ"</string> +</resources> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 5666cc23f2be..af3ce0065ff5 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Уредов нема сензор за отпечатоци."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорот е привремено оневозможен."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Користете го отпечатокот за да продолжите"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона за отпечатоци"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Ликот не се препознава. Обидете се повторно."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Премногу слично, сменете ја позата."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Не вртете ја главата толку многу."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Не вртете ја главата толку многу."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Не вртете ја главата толку многу."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Отстранете ги работите што ви го покриваат лицето."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Исчистете го врвот на екранот, вклучувајќи ја црната лента"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дозволува апликацијата да чита и пишува конфигурација Не вознемирувај."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"започнете со користење на дозволата за приказ"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дозволува сопственикот да почне со користење на дозволата за апликација. Не треба да се користи за стандардни апликации."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"пристапува до податоците со висока фреквенција на семпл"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Дозволува апликацијата да пристапува до податоците од сензорите со фреквенција на семпл поголема од 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Постави правила за лозинката"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролирај ги должината и знаците што се дозволени за лозинки и PIN-броеви за отклучување екран."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Следи ги обидите за отклучување на екранот"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи кратенка"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија на бои"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција на бои"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е вклучена."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е исклучена."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Притиснете ги и задржете ги двете копчиња за јачина на звукот во траење од три секунди за да користите <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Барањето SS е изменето во видео повик"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Барањето SS е изменето во барање USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Променето на ново барање SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Предупредување за фишинг"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Работен профил"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Предупредено"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Прошири"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Зголеми"</string> <string name="close_button_text" msgid="10603510034455258">"Затвори"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Одговори"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Одбиј"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Спушти"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Дојдовен повик"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Тековен повик"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Проверка на дојдовен повик"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> е избрана</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> се избрани</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"За да продолжи, на <b><xliff:g id="APP">%s</xliff:g></b> ѝ е потребен пристап до камерата на уредот."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Вклучи"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Приватност на сензорот"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index e0d4b0179624..aaab7b59837b 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"വൈഫൈ മാത്രം"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ബാക്കപ്പ് കോളിംഗ്"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: കൈമാറിയില്ല"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> നിമിഷത്തിനുശേഷം <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസറില്ല."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"സെൻസർ താൽക്കാലികമായി പ്രവർത്തനരഹിതമാക്കി."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"കൈവിരൽ <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"തുടരുന്നതിന് നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ഫിംഗർപ്രിന്റ് ഐക്കൺ"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"ഇനി മുഖം തിരിച്ചറിയാനാവില്ല. വീണ്ടും ശ്രമിക്കൂ."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"വളരെയധികം സമാനത, നിങ്ങളുടെ പോസ് മാറ്റുക."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"നിങ്ങളുടെ തല ഇത്ര തിരിക്കേണ്ട."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"നിങ്ങളുടെ തല ഇത്ര തിരിക്കേണ്ട."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"നിങ്ങളുടെ തല ഇത്ര തിരിക്കേണ്ട."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"നിങ്ങളുടെ മുഖം മറയ്ക്കുന്നത് എല്ലാം നീക്കം ചെയ്യൂ."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"കറുപ്പ് ബാർ ഉൾപ്പെടെ നിങ്ങളുടെ സ്ക്രീനിന്റെ മുകൾഭാഗം വൃത്തിയാക്കുക"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"\'ശല്യപ്പെടുത്തരുത്\' കോൺഫിഗറേഷൻ വായിക്കുന്നതിനും എഴുതുന്നതിനും ആപ്പിനെ അനുവദിക്കുന്നു."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"അനുമതി ഉപയോഗം കാണാൻ ആരംഭിക്കുക"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ഒരു ആപ്പിനുള്ള അനുമതി ഉപയോഗം ആരംഭിക്കാൻ ഹോൾഡറിനെ അനുവദിക്കുന്നു. സാധാരണ ആപ്പുകൾക്ക് ഒരിക്കലും ആവശ്യമില്ല."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ഉയർന്ന സാംപ്ലിംഗ് റേറ്റിൽ സെൻസർ ഡാറ്റ ആക്സസ് ചെയ്യുക"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz-നേക്കാൾ ഉയർന്ന റേറ്റിൽ സെൻസർ ഡാറ്റ സാമ്പിൾ ചെയ്യാൻ ആപ്പിനെ അനുവദിക്കുന്നു"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"പാസ്വേഡ് നിയമങ്ങൾ സജ്ജീകരിക്കുക"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"സ്ക്രീൻ ലോക്ക് പാസ്വേഡുകളിലും PIN-കളിലും അനുവദിച്ചിരിക്കുന്ന ദൈർഘ്യവും പ്രതീകങ്ങളും നിയന്ത്രിക്കുക."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"സ്ക്രീൻ അൺലോക്ക് ശ്രമങ്ങൾ നിരീക്ഷിക്കുക"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"കുറുക്കുവഴി ഉപയോഗിക്കുക"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"വർണ്ണ വിപര്യയം"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"നിറം ക്രമീകരിക്കൽ"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"വോളിയം കീകൾ പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓണാക്കി."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"വോളിയം കീകൾ അമർത്തിപ്പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓഫാക്കി."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഉപയോഗിക്കാൻ, രണ്ട് വോളിയം കീകളും മൂന്ന് സെക്കൻഡ് അമർത്തിപ്പിടിക്കുക"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS അഭ്യർത്ഥന, വീഡിയോ കോളിലേക്ക് മാറ്റി"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS അഭ്യർത്ഥന, USSD അഭ്യർത്ഥനയിലേക്ക് മാറ്റി"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"പുതിയ SS അഭ്യർത്ഥനയിലേക്ക് മാറ്റി"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ഫിഷിംഗ് മുന്നറിയിപ്പ്"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"മുന്നറിയിപ്പ് നൽകി"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"വികസിപ്പിക്കുക"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"വലുതാക്കുക"</string> <string name="close_button_text" msgid="10603510034455258">"അവസാനിപ്പിക്കുക"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"മറുപടി നൽകുക"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"നിരസിക്കുക"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"കോൾ നിർത്തുക"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"ഇൻകമിംഗ് കോൾ"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"സജീവമായ കോൾ"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"ഇൻകമിംഗ് കോൾ സ്ക്രീൻ ചെയ്യൽ"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> എണ്ണം തിരഞ്ഞെടുത്തു</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> എണ്ണം തിരഞ്ഞെടുത്തു</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"തുടരാൻ, <b><xliff:g id="APP">%s</xliff:g></b> ആപ്പിന് നിങ്ങളുടെ ഉപകരണത്തിന്റെ ക്യാമറയിലേക്ക് ആക്സസ് നൽകേണ്ടതുണ്ട്."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ഓണാക്കുക"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"സെൻസർ സ്വകാര്യത"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 891ac47191e1..c69928546143 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -325,7 +325,7 @@ <string name="permgroupdesc_sensors" msgid="2610631290633747752">"таны биеийн байдлын талаарх мэдрэгч бүхий өгөгдөлд нэвтрэх"</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> + <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Хүрэлтээр сонсохыг асаах"</string> <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Товшсон зүйлсийг чангаар хэлэх ба дэлгэцийг дохио ашиглан таних боломжтой."</string> <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Бичсэн текстээ ажиглах"</string> <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Кредит картын дугаар болон нууц үг зэрэг хувийн датаг агуулж байна."</string> @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Энэ төхөөрөмжид хурууны хээ мэдрэгч алга."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Мэдрэгчийг түр хугацаанд идэвхгүй болгосон."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Хурууны хээ <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Үргэлжлүүлэхийн тулд хурууны хээгээ ашиглаарай"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Хурууны хээний дүрс"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Царайг таних боломжгүй боллоо. Дахин оролдоно уу."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Хэт адилхан байгаа тул байрлалаа өөрчилнө үү."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Толгойгоо арай багаар эргүүлнэ үү."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Толгойгоо арай багаар эргүүлнэ үү."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Толгойгоо арай багаар эргүүлнэ үү."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Таны нүүрийг далдалж буй аливаа зүйлийг хасна уу."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Хар хэсэг зэрэг дэлгэцийнхээ дээд хэсгийг цэвэрлэнэ үү"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Апп-д Бүү саад бол тохируулгыг уншиж, бичихийг зөвшөөрөх"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"зөвшөөрлийн ашиглалтыг харж эхлэх"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Эзэмшигчид аппын зөвшөөрлөө ашиглаж эхлэхийг зөвшөөрдөг. Энгийн аппуудад шаардлагагүй."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"түүврийн өндөр хувиар мэдрэгчийн өгөгдөлд хандах"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Аппад 200 Гц-ээс их хувиар мэдрэгчийн өгөгдлийг түүвэрлэх боломжийг олгодог"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Нууц үгний дүрмийг тохируулах"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Дэлгэц түгжих нууц үг болон ПИН кодны урт болон нийт тэмдэгтийн уртыг хянах."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Дэлгэцийн түгжээг тайлах оролдлогыг хянах"</string> @@ -1002,9 +1006,9 @@ <string name="searchview_description_clear" msgid="1989371719192982900">"Асуулгыг цэвэрлэх"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"Асуулгыг илгээх"</string> <string name="searchview_description_voice" msgid="42360159504884679">"Дуут хайлт"</string> - <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Хүрч хайх функцийг идэвхтэй болгох уу?"</string> - <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> нь Хүрч танихыг идэвхжүүлэхийг шаардаж байна. Хүрч таних идэвхжсэн үед та хуруун доороо юу байгааг сонсох, тайлбарыг харах боломжтой ба таблеттайгаа дохиогоор харилцах боломжтой."</string> - <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> нь Хүрч танихыг идэвхжүүлэхийг шаардаж байна. Хүрч таних идэвхжсэн тохиолдолд та хуруун доороо юу байгааг сонсох, тайлбарыг харах боломжтой ба утастайгаа дохиогоор харилцах боломжтой."</string> + <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Хүрэлтээр сонсохыг идэвхжүүлэх үү?"</string> + <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> нь Хүрэлтээр сонсохыг идэвхжүүлэхийг шаардаж байна. Хүрэлтээр сонсох идэвхжсэн үед та хуруун доороо юу байгааг сонсох, тайлбарыг харах боломжтой ба таблеттайгаа дохиогоор харилцах боломжтой."</string> + <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> нь Хүрэлтээр сонсохыг идэвхжүүлэхийг шаардаж байна. Хүрэлтээр сонсох идэвхжсэн тохиолдолд та хуруун доороо юу байгааг сонсох, тайлбарыг харах боломжтой ба утастайгаа дохиогоор харилцах боломжтой."</string> <string name="oneMonthDurationPast" msgid="4538030857114635777">"1 сарын өмнө"</string> <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"1 сарын өмнө"</string> <plurals name="last_num_days" formatted="false" msgid="687443109145393632"> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Товчлол ашиглах"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Өнгө хувиргалт"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Өнгөний засвар"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаалаа."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г унтраалаа."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г ашиглахын тулд дууны түвшнийг ихэсгэх, багасгах түлхүүрийг 3 секундийн турш зэрэг дарна уу"</string> @@ -1867,11 +1873,12 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS хүсэлтийг видео дуудлага болгон өөрчилсөн"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS хүсэлтийг USSD хүсэлт болгон өөрчилсөн"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Шинэ SS хүсэлт болгон өөрчилсөн"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Фишинг сэрэмжлүүлэг"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Ажлын профайл"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Мэдэгдсэн"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Дэлгэх"</string> <string name="expand_button_content_description_expanded" msgid="7484217944948667489">"Буулгах"</string> - <string name="expand_action_accessibility" msgid="1947657036871746627">"унтраах/асаах өргөтгөл"</string> + <string name="expand_action_accessibility" msgid="1947657036871746627">"асаах/унтраах өргөтгөл"</string> <string name="usb_midi_peripheral_name" msgid="490523464968655741">"Андройд USB Peripheral Port"</string> <string name="usb_midi_peripheral_manufacturer_name" msgid="7557148557088787741">"Android"</string> <string name="usb_midi_peripheral_product_name" msgid="2836276258480904434">"USB Peripheral Port"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Томруулах"</string> <string name="close_button_text" msgid="10603510034455258">"Хаах"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Хариулах"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Татгалзах"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Таслах"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Ирсэн дуудлага"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Дуудлага хийгдэж байна"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Ирсэн дуудлагыг харуулж байна"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> сонгосон</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> сонгосон</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Үргэлжлүүлэхийн тулд <b><xliff:g id="APP">%s</xliff:g></b> таны төхөөрөмжийн камерт хандах шаардлагатай."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Асаах"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Мэдрэгчийн нууцлал"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index d57c4c1d0d05..a31951a3f5ac 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"केवळ वाय-फाय"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> बॅकअप कॉलिंग"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: फॉरवर्ड केला नाही"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकंदांनंतर <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"या डिव्हाइसमध्ये फिंगरप्रिंट सेन्सर नाही."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेन्सर तात्पुरता बंद केला आहे."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> बोट"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"पुढे सुरू ठेवण्यासाठी तुमची फिंगरप्रिंट वापरा"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फिंगरप्रिंट आयकन"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"चेहरा ओळखू शकत नाही. पुन्हा प्रयत्न करा."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"एकाच प्रकारची पोझ देत आहात कृपया तुमची पोझ बदला."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"तुमचे डोके थोडे कमी फिरवा."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"तुमचे डोके थोडे कमी फिरवा."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"तुमचे डोके थोडे कमी फिरवा."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"तुमचा चहेरा लपवणारे काहीही काढून टाका."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"ब्लॅक बार सह तुमच्या स्क्रीनची वरची बाजू साफ करा"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका कॉंफिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी ॲपला अनुमती देते."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"व्ह्यू परवानगी वापर सुरू करा"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"धारकास अॅपसाठी परवानगी वापरणे सुरू करण्याची अनुमती देते. सामान्य अॅप्ससाठी कधीही आवश्यकता नसते."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"उच्च नमुना दराने सेन्सर डेटा अॅक्सेस करते"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"ॲपला २०० Hz पेक्षा जास्त दराने सेन्सर डेटाचा नमुना तयार करण्याची अनुमती देते"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियम सेट करा"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"स्क्रीन लॉक पासवर्ड आणि पिन मध्ये अनुमती दिलेले लांबी आणि वर्ण नियंत्रित करा."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"स्क्रीन अनलॉक प्रयत्नांचे परीक्षण करा"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"शॉर्टकट वापरा"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"रंगांची उलटापालट"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"रंग सुधारणा"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू केला आहे."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केले आहे."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> वापरण्यासाठी दोन्ही व्हॉल्युम की तीन सेकंद दाबा आणि धरून ठेवा"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS विनंती व्हिडिओ कॉलवर बदलली"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS विनंती USSD विनंतीवर बदलली"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"नवीन SS विनंतीवर बदलली"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"फिशिंगशी संबंधित सूचना"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"कार्य प्रोफाईल"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"सूचना दिल्या"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"विस्तृत करा"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"मोठे करा"</string> <string name="close_button_text" msgid="10603510034455258">"बंद करा"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"उत्तर द्या"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"नकार द्या"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"कॉल बंद करा"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"इनकमिंग कॉल"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"सुरू असलेला कॉल"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"इनकमिंग कॉल स्क्रीन करत आहे"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> निवडले</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> निवडला</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"पुढे सुरू ठेवण्यासाठी, <b><xliff:g id="APP">%s</xliff:g></b> ला तुमच्या डिव्हाइसचा कॅमेरा अॅक्सेस करण्याची आवश्यकता आहे."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"सुरू करा"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"सेन्सरशी संबंधित गोपनीयतेबाबत सूचना"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index ec0aa5d6fae6..5e5b29f8e4e0 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -325,7 +325,7 @@ <string name="permgroupdesc_sensors" msgid="2610631290633747752">"akses data penderia tentang tanda vital anda"</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 Jelajah melalui Sentuhan"</string> + <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Hidupkan Teroka melalui Sentuhan"</string> <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Item yang diketik akan dituturkan dengan lantang dan skrin boleh dijelajah menggunakan gerak isyarat."</string> <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Perhatikan teks yang anda taip"</string> <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Termasuk data peribadi seperti nombor kad kredit dan kata laluan."</string> @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Peranti ini tiada penderia cap jari."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Penderia dilumpuhkan sementara."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gunakan cap jari anda untuk teruskan"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon cap jari"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Tidak lagi dapat mengecam wajah. Cuba lagi."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Terlalu serupa, sila ubah lagak gaya anda."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Pusingkan kepala anda kurang sedikit."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Pusingkan kepala anda kurang sedikit."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Pusingkan kepala anda kurang sedikit."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Alih keluar apa saja yang melindungi wajah anda."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Bersihkan bahagian atas skrin anda, termasuk bar hitam"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Membenarkan apl membaca dan menulis konfigurasi Jangan Ganggu."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"mulakan lihat penggunaan kebenaran"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Membenarkan pemegang memulakan penggunaan kebenaran untuk apl. Tidak sekali-kali diperlukan untuk apl biasa."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"akses data penderia pada data pensampelan yang tinggi"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Membenarkan apl mengambil sampel data penderia pada kadar yang lebih besar daripada 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Tetapkan peraturan kata laluan"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Mengawal panjang dan aksara yang dibenarkan dalam kata laluan dan PIN kunci skrin."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Pantau percubaan buka kunci skrin"</string> @@ -1002,9 +1006,9 @@ <string name="searchview_description_clear" msgid="1989371719192982900">"Pertanyaan jelas"</string> <string name="searchview_description_submit" msgid="6771060386117334686">"Serah pertanyaan"</string> <string name="searchview_description_voice" msgid="42360159504884679">"Carian suara"</string> - <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Dayakan Jelajah melalui Sentuhan?"</string> - <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ingin mendayakan Jelajah melalui Sentuhan. Apabila Jelajah melalui Sentuhan didayakan, anda boleh mendengar atau melihat penerangan tentang apa di bawah jari anda atau melakukan gerak isyarat untuk berinteraksi dengan tablet."</string> - <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ingin mendayakan Jelajah melalui Sentuhan. Apabila Jelajah melalui Sentuhan didayakan, anda boleh mendengar atau melihat penerangan tentang apa di bawah jari anda atau melakukan gerak isyarat untuk berinteraksi dengan telefon."</string> + <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Dayakan Teroka melalui Sentuhan?"</string> + <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ingin mendayakan Teroka melalui Sentuhan. Apabila Teroka melalui Sentuhan didayakan, anda boleh mendengar atau melihat penerangan tentang apa-apa di bawah jari anda atau melakukan gerak isyarat untuk berinteraksi dengan tablet."</string> + <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> ingin mendayakan Teroka melalui Sentuhan. Apabila Teroka melalui Sentuhan didayakan, anda boleh mendengar atau melihat penerangan tentang apa-apa di bawah jari anda atau melakukan gerak isyarat untuk berinteraksi dengan telefon."</string> <string name="oneMonthDurationPast" msgid="4538030857114635777">"1 bulan yang lalu"</string> <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Sebelum 1 bulan yang lalu"</string> <plurals name="last_num_days" formatted="false" msgid="687443109145393632"> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gunakan Pintasan"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Penyongsangan Warna"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Pembetulan Warna"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dihidupkan."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dimatikan."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tekan dan tahan kedua-dua kekunci kelantangan selama tiga saat untuk menggunakan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Permintaan SS ditukar kepada panggilan video"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Permintaan SS ditukar kepada permintaan USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Bertukar kepada permintaan SS baharu"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Amaran pancingan data"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil kerja"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Dimaklumkan"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Kembangkan"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maksimumkan"</string> <string name="close_button_text" msgid="10603510034455258">"Tutup"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Jawapan"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Tolak"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Tamatkan Panggilan"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Panggilan masuk"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Panggilan sedang berlangsung"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Menyaring panggilan masuk"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dipilih</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dipilih</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Untuk meneruskan proses, <b><xliff:g id="APP">%s</xliff:g></b> memerlukan akses kepada kamera peranti anda."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Hidupkan"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privasi Penderia"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 7b4a430fc1bb..137c138cba97 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ဤစက်တွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ။"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"အာရုံခံကိရိယာကို ယာယီပိတ်ထားသည်။"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"လက်ချောင်း <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ရှေ့ဆက်ရန် သင့်လက်ဗွေကို သုံးပါ"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"လက်ဗွေ သင်္ကေတ"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"မျက်နှာ မမှတ်သားနိုင်တော့ပါ။ ထပ်စမ်းကြည့်ပါ။"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"ဆင်တူနေသည်၊ အမူအရာ ပြောင်းပါ။"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"ခေါင်းကို သိပ်မလှည့်ပါနှင့်။"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"ခေါင်းကို သိပ်မလှည့်ပါနှင့်။"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"ခေါင်းကို သိပ်မလှည့်ပါနှင့်။"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"သင့်မျက်နှာကို ကွယ်နေသည့်အရာအားလုံး ဖယ်ပါ။"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"အနက်ရောင်ဘားအပါအဝင် ဖန်သားပြင်ထိပ်ကို သန့်ရှင်းရေး လုပ်ပါ"</string> @@ -648,8 +650,8 @@ <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"အက်ပ်အား အသုံးပြုသူက ခေါ်ဆိုမှုအဝင် မျက်နှာပြင် ဘယ်အချိန်မှာ ဘယ်လို မြင်ရမှာကို ထိန်းချုပ်ခွင့်ပေးရန်"</string> <string name="permlab_bind_connection_service" msgid="5409268245525024736">"တယ်လီဖုန်း ဝန်ဆောင်မှုများနှင့် အပြန်အလှန် တုံ့ပြန်မှု"</string> <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"အက်ပ်အား ခေါ်ဆိုမှုများ လုပ်ခြင်း/လက်ခံခြင်း ပြုလုပ်နိုင်ရန် တယ်လီဖုန်း ဝန်ဆောင်မှုများနှင့် အပြန်အလှန် တုံ့ပြန်မှုကို ခွင့်ပြုသည်။"</string> - <string name="permlab_control_incall_experience" msgid="6436863486094352987">"အသုံးပြုသူ အတွက် ခေါ်ဆိုမှုအဝင် လုပ်ကိုင်ပုံကို စီစဉ်ပေးခြင်း"</string> - <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"အက်ပ်အား အသုံးပြုသူ အတွက် ခေါ်ဆိုမှုအဝင် လုပ်ကိုင်ပုံကို စီစဉ်ခွင့် ပြုသည်။"</string> + <string name="permlab_control_incall_experience" msgid="6436863486094352987">"အဝင်ခေါ်ဆိုမှုအတွက် အသုံးပြုသူ၏ နှစ်သက်မှုကို ခွင့်ပြုခြင်း"</string> + <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"အဝင်ခေါ်ဆိုမှုအတွက် အသုံးပြုသူ၏ နှစ်သက်မှုကို ပံ့ပိုးပေးရန် အက်ပ်အား ခွင့်ပြုသည်။"</string> <string name="permlab_readNetworkUsageHistory" msgid="8470402862501573795">"ရာဇဝင်အလိုက် ကွန်ယက်သုံစွဲမှုအား ဖတ်ခြင်း"</string> <string name="permdesc_readNetworkUsageHistory" msgid="1112962304941637102">"အက်ပ်အား အထူး ကွန်ရက်များ နှင့် အက်ပ်များ အတွက် ကွန်ရက် အသုံးပြုမှု မှတ်တမ်းကို ဖတ်ကြားခွင့် ပြုသည်။"</string> <string name="permlab_manageNetworkPolicy" msgid="6872549423152175378">"ကွန်ယက်မူဝါဒအား စီမံခြင်း"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"မနှောင့်ယှက်ရန် ချိန်ညှိမှုကို အပ်ဖ်များ ဖတ်ခြင်း ပြင်ခြင်းပြုလုပ်နိုင်ရန် ခွင့်ပြုမည်။"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"အစမြင်ကွင်း ခွင့်ပြုချက် အသုံးပြုမှု"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"အက်ပ်တစ်ခုအတွက် ခွင့်ပြုချက်စတင်အသုံးပြုမှုကို ကိုင်ဆောင်သူအား ခွင့်ပြုသည်။ ပုံမှန်အက်ပ်များအတွက် ဘယ်သောအခါမျှ မလိုအပ်ပါ။"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"နမူနာနှုန်းမြင့်သော အာရုံခံစနစ်ဒေတာကို သုံးပါ"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"၂၀၀ Hz နှုန်းထက်ပိုများသော အာရုံခံစနစ်ဒေတာကို နမူနာယူရန် အက်ပ်အား ခွင့်ပြုပါ"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"စကားဝှက်စည်းမျဥ်းကိုသတ်မှတ်ရန်"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"မျက်နှာပြင်သော့ခတ်သည့် စကားဝှက်များနှင့် PINများရှိ ခွင့်ပြုထားသည့် စာလုံးအရေအတွက်နှင့် အက္ခရာများအား ထိန်းချုပ်ရန်။"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"မျက်နှာပြင်လော့ခ်ဖွင့်ရန် ကြိုးပမ်းမှုများကို စောင့်ကြည့်ပါ"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ဖြတ်လမ်းလင့်ခ်ကို သုံးရန်"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"အရောင် ပြောင်းပြန်လှန်ခြင်း"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"အရောင်ပြင်ဆင်ခြင်း"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ဖွင့်လိုက်သည်။"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ပိတ်လိုက်သည်။"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ကို သုံးရန် အသံအတိုးအလျှော့ ခလုတ်နှစ်ခုလုံးကို သုံးစက္ကန့်ကြာ ဖိထားပါ"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS တောင်းဆိုမှုကို ဗီဒီယိုခေါ်ဆိုမှုသို့ ပြောင်းထားသည်"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS တောင်းဆိုမှုကို USSD တောင်းဆိုမှုအဖြစ် ပြောင်းထားသည်"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"SS တောင်းဆိုမှုအသစ်သို့ ပြောင်းထားသည်"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"အယောင်ဆောင်ဖြားယောင်းခြင်း သတိပေးချက်"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"အလုပ်ကိုယ်ရေးအချက်အလက်"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"သတိပေးထားသည်"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ချဲ့ရန်"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"အများဆုံး လုပ်ပေးရန်"</string> <string name="close_button_text" msgid="10603510034455258">"ပိတ်ရန်"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>− <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"ဖုန်းကိုင်ရန်"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"ငြင်းပယ်ရန်"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ဖုန်းချရန်"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"အဝင်ခေါ်ဆိုမှု"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"လက်ရှိခေါ်ဆိုမှု"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"အဝင်ခေါ်ဆိုမှုကို စစ်ဆေးနေသည်"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ရွေးချယ်ပြီးပါပြီ</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ရွေးချယ်ပြီးပါပြီ</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ဆက်လက်လုပ်ဆောင်ရန် <b><xliff:g id="APP">%s</xliff:g></b> က သင့်စက်၏ ကင်မရာကို အသုံးပြုခွင့်ရရန် လိုအပ်သည်။"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ဖွင့်ရန်"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"အာရုံခံကိရိယာ လုံခြုံရေး"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 496818426714..de71c39a05d0 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enheten har ikke fingeravtrykkssensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidig slått av."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Bruk fingeravtrykket ditt for å fortsette"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon for fingeravtrykk"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Kan ikke gjenkjenne ansiktet lenger. Prøv igjen."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"For likt – endre posituren din."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Vri hodet ditt litt mindre."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Vri hodet ditt litt mindre."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Vri hodet ditt litt mindre."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Fjern alt som skjuler ansiktet ditt."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Rengjør den øverste delen av skjermen, inkludert den svarte linjen"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Lar appen lese og skrive konfigurasjon av Ikke forstyrr."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"start visning av bruk av tillatelser"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lar innehaveren starte bruk av tillatelser for en app. Dette skal aldri være nødvendig for vanlige apper."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"tilgang til sensordata ved høy samplingfrekvens"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Lar appen samle inn sensordata ved en hastighet som er høyere enn 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Angi passordregler"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrollerer tillatt lengde og tillatte tegn i passord og PIN-koder for opplåsing av skjermen."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Overvåk forsøk på å låse opp skjermen"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Bruk snarveien"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Fargeinvertering"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Fargekorrigering"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått på."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått av."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Trykk og hold inne begge volumtastene i tre sekunder for å bruke <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-forespørsel endret til videoanrop"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-forespørsel endret til USSD-forespørsel"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Endret til ny SS-forespørsel"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Varsel om nettfisking"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Arbeidsprofil"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Varslet"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Vis"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maksimer"</string> <string name="close_button_text" msgid="10603510034455258">"Lukk"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g><xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Svar"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Avvis"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Legg på"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Innkommende anrop"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Pågående samtale"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrerer et innkommende anrop"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> er valgt</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> er valgt</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"For å fortsette må <b><xliff:g id="APP">%s</xliff:g></b> ha tilgang til enhetskameraet."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Slå på"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorpersonvern"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 541ace80518c..83c03a4361c0 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi मात्र"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ब्याकअप कलिङ"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अगाडि पठाइएको छैन"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> पछि <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकेन्ड"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"यो यन्त्रमा कुनै पनि फिंगरप्रिन्ट सेन्सर छैन।"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"केही समयका लागि सेन्सर असक्षम पारियो।"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"औंला <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"जारी राख्न आफ्नो फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फिंगरप्रिन्ट आइकन"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"अब उप्रान्त अनुहार पहिचान गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"अनुहार उस्तै भयो, कृपया आफ्नो पोज बदल्नुहोस्।"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"आफ्नो टाउको अलि थोरै घुमाउनुहोस्।"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"आफ्नो टाउको अलि थोरै घुमाउनुहोस्।"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"आफ्नो टाउको अलि थोरै घुमाउनुहोस्।"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"तपाईंको अनुहार लुकाउने सबै कुरा लुकाउनुहोस्।"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"कालो रङको पट्टीलगायत आफ्नो स्क्रिनको माथिल्लो भाग सफा गर्नुहोस्"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"बाधा नपुर्याउँनुहोस् कन्फिगरेसन पढ्न र लेख्नको लागि एपलाई अनुमति दिनुहोस्।"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"हेर्ने अनुमतिको प्रयोग सुरु गर्नुहोस्"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"वाहकलाई कुनै एपसम्बन्धी अनुमतिको प्रयोग सुरु गर्न दिन्छ। साधारण एपहरूलाई कहिल्यै आवश्यक नपर्नु पर्ने हो।"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"नमुना लिने उच्च दरमा सेन्सरसम्बन्धी डेटा प्रयोग गर्ने"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"यो अनुमति दिइएमा एपले २०० हर्जभन्दा बढी दरमा सेन्सरसम्बन्धी डेटाको नमुना लिन सक्छ"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियमहरू मिलाउनुहोस्"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"स्क्रिन लक पासवर्ड र PIN हरूमा अनुमति दिइएको लम्बाइ र वर्णहरूको नियन्त्रण गर्नुहोस्।"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"मनिटरको स्क्रिन अनलक गर्ने प्रयासहरू"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"सर्टकट प्रयोग गर्नुहोस्"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"रङ्ग उल्टाउने सुविधा"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"रङ्ग सच्याउने सुविधा"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन भयो।"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अफ भयो।"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> प्रयोग गर्न दुवै भोल्युम कुञ्जीहरूलाई तीन सेकेन्डसम्म थिचिराख्नुहोस्"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS अनुरोधलाई भिडियो कलमा परिवर्तन गरियो"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS अनुरोधलाई USSD अनुरोधमा परिवर्तन गरियो"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"नयाँ SS अनुरोधमा परिवर्तन गरियो"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"फिसिङसम्बन्धी अलर्ट"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"कार्य प्रोफाइल"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"सतर्कता गरियो"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"विस्तृत गर्नुहोस्"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"ठुलो बनाउनुहोस्"</string> <string name="close_button_text" msgid="10603510034455258">"बन्द गर्नुहोस्"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"कलको जवाफ दिनु…"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"अस्वीकार गर्नुहोस्"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"फोन राख्नुहोस्"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"आगमन कल"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"भइरहेको कल"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"आगमन कल जाँचिँदै छ"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> चयन गरियो</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> चयन गरियो</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"जारी राख्न <b><xliff:g id="APP">%s</xliff:g></b> लाई तपाईंको यन्त्रको क्यामेरा प्रयोग गर्ने अनुमति दिनु पर्ने हुन्छ।"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"अन गर्नुहोस्"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"सेन्सरसम्बन्धी गोपनीयता"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index 29f2b6f14b57..4410e944db39 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -26,6 +26,10 @@ <color name="notification_default_color_dark">#ddffffff</color> + <color name="notification_primary_text_color_current">@color/notification_primary_text_color_dark</color> + <color name="notification_secondary_text_color_current">@color/notification_secondary_text_color_dark</color> + <color name="notification_default_color_current">@color/notification_default_color_dark</color> + <color name="chooser_row_divider">@color/list_divider_color_dark</color> <color name="chooser_gradient_background">@color/loading_gradient_background_color_dark</color> <color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color> diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml index 952cdd08451c..3fd82b874a84 100644 --- a/core/res/res/values-night/values.xml +++ b/core/res/res/values-night/values.xml @@ -28,9 +28,4 @@ </style> <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Dialog" /> - - <style name="TextAppearance.Material.Notification"> - <item name="textColor">?attr/textColorPrimary</item> - <item name="textSize">@dimen/notification_text_size</item> - </style> </resources>
\ No newline at end of file diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 7f23cbbbb6dd..e2be98d723fa 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -515,9 +515,9 @@ <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Hiermee kan de app pakketten ontvangen die via multicastadressen naar alle apparaten in een wifi-netwerk worden verzonden, niet alleen naar je Android TV-apparaat. Het stroomgebruik ligt hierbij hoger dan in de niet-multicastmodus."</string> <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Hiermee kan de app pakketten ontvangen die via multicastadressen naar alle apparaten in een wifi-netwerk worden verzonden, niet alleen naar je telefoon. Het stroomgebruik ligt hierbij hoger dan in de niet-multicastmodus."</string> <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"Bluetooth-instellingen openen"</string> - <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Hiermee kan de app de lokale bluetooth-tablet configureren en externe apparaten zoeken en koppelen."</string> - <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Hiermee kan de app Bluetooth op je Android TV-apparaat configureren en externe apparaten zoeken en koppelen."</string> - <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Hiermee kan de app de lokale bluetooth-telefoon configureren en externe apparaten zoeken en koppelen."</string> + <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Hiermee kan de app de lokale bluetooth-tablet instellen en externe apparaten zoeken en koppelen."</string> + <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Hiermee kan de app Bluetooth op je Android TV-apparaat isntellen en externe apparaten zoeken en koppelen."</string> + <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Hiermee kan de app de lokale bluetooth-telefoon instellen en externe apparaten zoeken en koppelen."</string> <string name="permlab_accessWimaxState" msgid="7029563339012437434">"WiMAX-verbinding maken en verbreken"</string> <string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Hiermee kan de app bepalen of WiMAX is ingeschakeld en informatie bekijken over alle WiMAX-netwerken waarmee verbinding is gemaakt."</string> <string name="permlab_changeWimaxState" msgid="6223305780806267462">"WiMAX-status wijzigen"</string> @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dit apparaat heeft geen vingerafdruksensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor tijdelijk uitgeschakeld."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gebruik je vingerafdruk om door te gaan."</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Vingerafdruk-icoon"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Herkent gezicht niet meer. Probeer het nog eens."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Lijkt te veel op elkaar. Verander je pose."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Draai je hoofd iets minder."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Draai je hoofd iets minder."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Draai je hoofd iets minder."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Zorg dat je gezicht volledig zichtbaar is."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Reinig de bovenkant van je scherm, inclusief de zwarte balk"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Hiermee kan de app configuratie voor Niet storen lezen en schrijven."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"rechtengebruik starten"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Hiermee kan de houder het rechtengebruik voor een app starten. Nooit vereist voor normale apps."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"toegang krijgen tot sensorgegevens met een hoge samplingsnelheid"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Hiermee kan de app sensorgegevens samplen met een snelheid die hoger is dan 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Wachtwoordregels instellen"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"De lengte en het aantal tekens beheren die zijn toegestaan in wachtwoorden en pincodes voor schermvergrendeling."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Pogingen voor schermontgrendeling bijhouden"</string> @@ -1346,7 +1350,7 @@ <string name="select_input_method" msgid="3971267998568587025">"Invoermethode selecteren"</string> <string name="show_ime" msgid="6406112007347443383">"Dit op het scherm weergeven terwijl het fysieke toetsenbord actief is"</string> <string name="hardware" msgid="1800597768237606953">"Virtueel toetsenbord tonen"</string> - <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Fysiek toetsenbord configureren"</string> + <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Fysiek toetsenbord instellen"</string> <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Tik om een taal en indeling te selecteren"</string> <string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> @@ -1369,7 +1373,7 @@ <string name="ext_media_unmountable_notification_message" product="automotive" msgid="2274596120715020680">"Je moet het apparaat misschien opnieuw formatteren. Tik om het uit te werpen."</string> <string name="ext_media_unsupported_notification_title" msgid="4358280700537030333">"<xliff:g id="NAME">%s</xliff:g> niet ondersteund"</string> <string name="ext_media_unsupported_notification_title" product="automotive" msgid="6004193172658722381">"<xliff:g id="NAME">%s</xliff:g> werkt niet"</string> - <string name="ext_media_unsupported_notification_message" msgid="917738524888367560">"Dit apparaat biedt geen ondersteuning voor deze <xliff:g id="NAME">%s</xliff:g>. Tik om te configureren in een ondersteunde indeling."</string> + <string name="ext_media_unsupported_notification_message" msgid="917738524888367560">"Dit apparaat biedt geen ondersteuning voor deze <xliff:g id="NAME">%s</xliff:g>. Tik om in te stellen in een ondersteunde indeling."</string> <string name="ext_media_unsupported_notification_message" product="tv" msgid="7744945987775645685">"Dit apparaat biedt geen ondersteuning voor deze <xliff:g id="NAME">%s</xliff:g>. Selecteer om in te stellen in een ondersteunde indeling."</string> <string name="ext_media_unsupported_notification_message" product="automotive" msgid="3412494732736336330">"Je moet het apparaat misschien opnieuw formatteren"</string> <string name="ext_media_badremoval_notification_title" msgid="4114625551266196872">"<xliff:g id="NAME">%s</xliff:g> is onverwacht verwijderd"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sneltoets gebruiken"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Kleurinversie"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Kleurcorrectie"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is ingeschakeld."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> uitgeschakeld."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Houd beide volumetoetsen drie seconden ingedrukt om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> te gebruiken"</string> @@ -1856,7 +1862,7 @@ <string name="zen_mode_default_weekends_name" msgid="4707200272709377930">"Weekend"</string> <string name="zen_mode_default_events_name" msgid="2280682960128512257">"Afspraken"</string> <string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Slapen"</string> - <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> dempt sommige geluiden"</string> + <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> zet sommige geluiden uit"</string> <string name="system_error_wipe_data" msgid="5910572292172208493">"Er is een intern probleem met je apparaat. Het apparaat kan instabiel zijn totdat u het apparaat terugzet naar de fabrieksinstellingen."</string> <string name="system_error_manufacturer" msgid="703545241070116315">"Er is een intern probleem met je apparaat. Neem contact op met de fabrikant voor meer informatie."</string> <string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD-verzoek gewijzigd in normaal gesprek"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-verzoek gewijzigd in videogesprek"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-verzoek gewijzigd in USSD-verzoek"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Gewijzigd in nieuw SS-verzoek"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishingmelding"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Werkprofiel"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Gemeld"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Uitvouwen"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximaliseren"</string> <string name="close_button_text" msgid="10603510034455258">"Sluiten"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Beantwoorden"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Weigeren"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Ophangen"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Inkomend gesprek"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Actief gesprek"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Een inkomend gesprek screenen"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> geselecteerd</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> geselecteerd</item> @@ -1924,7 +1937,7 @@ <string name="demo_starting_message" msgid="6577581216125805905">"Demo starten…"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"Apparaat resetten…"</string> <string name="suspended_widget_accessibility" msgid="6331451091851326101">"<xliff:g id="LABEL">%1$s</xliff:g> uitgeschakeld"</string> - <string name="conference_call" msgid="5731633152336490471">"Conferencecall"</string> + <string name="conference_call" msgid="5731633152336490471">"Telefonische vergadering"</string> <string name="tooltip_popup_title" msgid="7863719020269945722">"Knopinfo"</string> <string name="app_category_game" msgid="4534216074910244790">"Games"</string> <string name="app_category_audio" msgid="8296029904794676222">"Muziek en audio"</string> @@ -2000,7 +2013,7 @@ <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> wil segmenten van <xliff:g id="APP_2">%2$s</xliff:g> weergeven"</string> <string name="screenshot_edit" msgid="7408934887203689207">"Bewerken"</string> <string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"Trillen bij gesprekken en meldingen"</string> - <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Gesprekken en meldingen zijn gedempt"</string> + <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Telefoon- en meldingsgeluid wordt uitgezet"</string> <string name="notification_channel_system_changes" msgid="2462010596920209678">"Systeemwijzigingen"</string> <string name="notification_channel_do_not_disturb" msgid="7832584281883687653">"Niet storen"</string> <string name="zen_upgrade_notification_visd_title" msgid="2001148984371968620">"Nieuw: \'Niet storen\' verbergt meldingen"</string> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"<b><xliff:g id="APP">%s</xliff:g></b> heeft toegang tot de camera van je apparaat nodig om door te gaan."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aanzetten"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorprivacy"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index f02d1573b7b2..bc761429b4c7 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"କେବଳ ୱାଇ-ଫାଇ"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ବ୍ୟାକଅପ୍ କଲିଂ"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ଫରୱାର୍ଡ କରାଯାଇନାହିଁ"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ସେକେଣ୍ଡ ପରେ"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ଏହି ଡିଭାଇସ୍ରେ ଟିପଚିହ୍ନ ସେନ୍ସର୍ ନାହିଁ।"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ସେନ୍ସରକୁ ଅସ୍ଥାୟୀ ଭାବେ ଅକ୍ଷମ କରାଯାଇଛି।"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ଆଙ୍ଗୁଠି <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ଟିପଚିହ୍ନ ଆଇକନ୍"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"ଆଉ ମୁହଁ ଚିହ୍ନଟ କରିହେଲା ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"ଅତ୍ୟନ୍ତ ସମପରି, ଦୟାକରି ଆପଣଙ୍କର ପୋଜ୍ ବଦଳାନ୍ତୁ।"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"ଆପଣଙ୍କର ମୁଣ୍ଡକୁ ଟିକିଏ ବୁଲାନ୍ତୁ।"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"ଆପଣଙ୍କର ମୁଣ୍ଡକୁ ଟିକିଏ ବୁଲାନ୍ତୁ।"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"ଆପଣଙ୍କର ମୁଣ୍ଡକୁ ଟିକିଏ ବୁଲାନ୍ତୁ।"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"ଆପଣଙ୍କର ମୁହଁ ଲୁଚାଉଥିବା ଜିନିଷକୁ କାଢ଼ି ଦିଅନ୍ତୁ।"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"କଳା ବାର୍ ସମେତ ଆପଣଙ୍କ ସ୍କ୍ରିନ୍ର ଶୀର୍ଷକୁ ସଫା କରନ୍ତୁ"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" କନଫିଗରେଶନ୍ ପଢ଼ିବା ତଥା ଲେଖିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦେଇଥାଏ।"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ଅନୁମତି ବ୍ୟବହାର ଦେଖିବା ଆରମ୍ଭ କରନ୍ତୁ"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ଏକ ଆପ୍ ପାଇଁ ଅନୁମତିର ବ୍ୟବହାର ଆରମ୍ଭ କରିବାକୁ ଧାରକକୁ ଅନୁମତି ଦେଇଥାଏ। ସାଧାରଣ ଆପ୍ଗୁଡ଼ିକ ପାଇଁ ଏହା ଆବଶ୍ୟକ ନୁହେଁ।"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ଏକ ଉଚ୍ଚ ନମୁନାକରଣ ରେଟରେ ସେନ୍ସର୍ ଡାଟାକୁ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz ଠାରୁ ଅଧିକ ଏକ ରେଟରେ ସେନ୍ସର୍ ଡାଟାର ନମୁନା ନେବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"ପାସ୍ୱର୍ଡ ନିୟମାବଳୀ ସେଟ୍ କରନ୍ତୁ"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"ଲକ୍ ସ୍କ୍ରୀନ୍ ପାସ୍ୱର୍ଡ ଓ PINରେ ଅନୁମୋଦିତ ଦୀର୍ଘତା ଓ ବର୍ଣ୍ଣ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ।"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"ସ୍କ୍ରୀନ୍-ଅନଲକ୍ କରିବା ଉଦ୍ୟମ ନୀରିକ୍ଷଣ କରନ୍ତୁ"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ଶର୍ଟକଟ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"ରଙ୍ଗ ବଦଳାଇବାର ସୁବିଧା"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"ରଙ୍ଗ ସଂଶୋଧନ"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଚାଲୁ ହୋଇଛି।"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବନ୍ଦ ହୋଇଛି।"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବ୍ୟବହାର କରିବାକୁ ତିନି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ୍ କୀ ଦବାଇ ଧରି ରଖନ୍ତୁ"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SSଙ୍କ ଅନୁରୋଧକୁ ଭିଡିଓ କଲ୍ରେ ପରିବର୍ତ୍ତନ କରାଗଲା"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS ଅନୁରୋଧ, USSD ଅନୁରୋଧକୁ ପରିବର୍ତ୍ତନ ହେଲା"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"ନୂତନ SS ଅନୁରୋଧରେ ପରିବର୍ତ୍ତନ ହେଲା"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ଫିସିଂ ଆଲର୍ଟ"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"ୱର୍କ ପ୍ରୋଫାଇଲ୍"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"ଆଲର୍ଟ କରାଯାଇଛି"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ବଢ଼ାନ୍ତୁ"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"ବଡ଼ କରନ୍ତୁ"</string> <string name="close_button_text" msgid="10603510034455258">"ବନ୍ଦ କରନ୍ତୁ"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"ଉତ୍ତର ଦିଅନ୍ତୁ"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"ଅଗ୍ରାହ୍ୟ କର"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ସମାପ୍ତ କରନ୍ତୁ"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"ଇନକମିଂ କଲ୍"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"ଚାଲିଥିବା କଲ୍"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"ଏକ ଇନକମିଂ କଲକୁ ସ୍କ୍ରିନ୍ କରୁଛି"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ଚୟନିତ</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ଚୟନିତ</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ଜାରି ରଖିବାକୁ, <b><xliff:g id="APP">%s</xliff:g></b> ଆପଣଙ୍କ ଡିଭାଇସର କ୍ୟାମେରାକୁ ଆକ୍ସେସ୍ ଆବଶ୍ୟକ କରେ।"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ଚାଲୁ କରନ୍ତୁ"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ସେନ୍ସର୍ ଗୋପନୀୟତା"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index a717af6ae89b..c9f57efc0c29 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"ਸਿਰਫ਼ ਵਾਈ-ਫਾਈ"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> ਬੈਕਅੱਪ ਕਾਲਿੰਗ"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ਅੱਗੇ ਨਹੀਂ ਭੇਜਿਆ ਗਿਆ"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> ਸਕਿੰਟਾਂ ਬਾਅਦ"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨਹੀਂ ਹੈ।"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ਸੈਂਸਰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ਉਂਗਲ <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣਾ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤੋ"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਤੀਕ"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"ਹੁਣ ਚਿਹਰਾ ਪਛਾਣਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"ਬਹੁਤ ਮਿਲਦਾ-ਜੁਲਦਾ ਹੈ, ਕਿਰਪਾ ਕਰਕੇ ਆਪਣਾ ਅੰਦਾਜ਼ ਬਦਲੋ।"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"ਆਪਣਾ ਸਿਰ ਥੋੜਾ ਜਿਹਾ ਝੁਕਾਓ।"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"ਆਪਣਾ ਸਿਰ ਥੋੜਾ ਜਿਹਾ ਝੁਕਾਓ।"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"ਆਪਣਾ ਸਿਰ ਥੋੜਾ ਜਿਹਾ ਝੁਕਾਓ।"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"ਤੁਹਾਡਾ ਚਿਹਰਾ ਲੁਕਾਉਣ ਵਾਲੀ ਕੋਈ ਵੀ ਚੀਜ਼ ਹਟਾਓ।"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"ਕਾਲੀ ਪੱਟੀ ਸਮੇਤ, ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੇ ਸਿਖਰ ਨੂੰ ਸਾਫ਼ ਕਰੋ"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ਐਪ ਨੂੰ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਕੌਂਫਿਗਰੇਸ਼ਨ ਨੂੰ ਪੜ੍ਹਨ ਅਤੇ ਲਿਖਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"ਇਜਾਜ਼ਤ ਵਰਤੋਂ ਦੇਖਣਾ ਸ਼ੁਰੂ ਕਰੋ"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ਧਾਰਕ ਨੂੰ ਕਿਸੇ ਹੋਰ ਐਪ ਲਈ ਇਜਾਜ਼ਤ ਵਰਤੋਂ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਦਿੰਦਾ ਹੈ। ਸਧਾਰਨ ਐਪਾਂ ਲਈ ਕਦੇ ਵੀ ਲੋੜੀਂਦਾ ਨਹੀਂ ਹੋਵੇਗਾ।"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ਉੱਚ ਸੈਂਪਲਿੰਗ ਰੇਟ \'ਤੇ ਸੈਂਸਰ ਡਾਟਾ ਤੱਕ ਪਹੁੰਚ ਕਰੋ"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"ਐਪ ਨੂੰ 200 Hz ਤੋਂ ਵੱਧ ਦੀ ਦਰ \'ਤੇ ਸੈਂਸਰ ਡਾਟੇ ਦਾ ਨਮੂਨਾ ਲੈਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"ਪਾਸਵਰਡ ਨਿਯਮ ਸੈੱਟ ਕਰੋ"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"ਸਕ੍ਰੀਨ ਲਾਕ ਪਾਸਵਰਡਾਂ ਅਤੇ ਪਿੰਨ ਵਿੱਚ ਆਗਿਆ ਦਿੱਤੀ ਲੰਮਾਈ ਅਤੇ ਅੱਖਰਾਂ ਤੇ ਨਿਯੰਤਰਣ ਪਾਓ।"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"ਸਕ੍ਰੀਨ ਅਣਲਾਕ ਕਰਨ ਦੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ \'ਤੇ ਨਿਗਰਾਨੀ ਰੱਖੋ"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ਸ਼ਾਰਟਕੱਟ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"ਰੰਗ ਪਲਟਨਾ"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"ਰੰਗ ਸੁਧਾਈ"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਦਬਾ ਕੇ ਰੱਖੀਆਂ ਗਈਆਂ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਦਬਾ ਕੇ ਰੱਖੀਆਂ ਗਈਆਂ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ ਕੁੰਜੀਆਂ ਨੂੰ 3 ਸਕਿੰਟਾਂ ਲਈ ਦਬਾਈ ਰੱਖੋ"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS ਬੇਨਤੀ ਨੂੰ ਵੀਡੀਓ ਕਾਲ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS ਬੇਨਤੀ ਨੂੰ USSD ਬੇਨਤੀ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"ਨਵੀਂ SS ਬੇਨਤੀ ਵਿੱਚ ਬਦਲਿਆ ਗਿਆ"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ਫ਼ਿਸ਼ਿੰਗ ਸੰਬੰਧੀ ਸੁਚੇਤਨਾ"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"ਸੁਚੇਤਨਾਵਾਂ"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ਵਿਸਤਾਰ ਕਰੋ"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"ਵੱਡਾ ਕਰੋ"</string> <string name="close_button_text" msgid="10603510034455258">"ਬੰਦ ਕਰੋ"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"ਜਵਾਬ ਦਿਓ"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"ਅਸਵੀਕਾਰ ਕਰੋ"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"ਸਮਾਪਤ ਕਰੋ"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"ਇਨਕਮਿੰਗ ਕਾਲ"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"ਜਾਰੀ ਕਾਲ"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"ਇਨਕਮਿੰਗ ਕਾਲ ਦੀ ਸਕ੍ਰੀਨਿੰਗ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ਚੁਣਿਆ ਗਿਆ</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ਚੁਣਿਆ ਗਿਆ</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"ਜਾਰੀ ਰੱਖਣ ਲਈ, <b><xliff:g id="APP">%s</xliff:g></b> ਨੂੰ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰਾ ਤੱਕ ਪਹੁੰਚ ਦੀ ਲੋੜ ਹੈ।"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ਚਾਲੂ ਕਰੋ"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ਸੈਂਸਰ ਪਰਦੇਦਾਰੀ"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index f73b38246e5a..6c12366d932a 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -585,6 +585,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"To urządzenie nie jest wyposażone w czytnik linii papilarnych."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Czujnik jest tymczasowo wyłączony."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Odcisk palca <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Użyj odcisku palca, by kontynuować"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona odcisku palca"</string> @@ -611,7 +612,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Nie można już rozpoznać twarzy. Spróbuj ponownie."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Za mała różnica. Zmień pozycję."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Trochę mniej obróć głowę."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Trochę mniej obróć głowę."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Trochę mniej obróć głowę."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Usuń wszystko, co zasłania Ci twarz."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Wyczyść górną krawędź ekranu, w tym czarny pasek"</string> @@ -690,6 +692,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Pozwala aplikacji na odczyt i zmianę konfiguracji trybu Nie przeszkadzać."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"rozpocząć wyświetlanie użycia uprawnień"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umożliwia rozpoczęcie korzystania z uprawnienia dotyczącego danej aplikacji jego posiadaczowi. Zwykłe aplikacje nie powinny potrzebować tego uprawnienia."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"dostęp do danych czujnika z wysoką częstotliwością"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Zezwala aplikacji na pobieranie próbek danych z czujnika z częstotliwością wyższą niż 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Określ reguły hasła"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolowanie długości haseł blokady ekranu i kodów PIN oraz dozwolonych w nich znaków."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorowanie prób odblokowania ekranu"</string> @@ -1708,6 +1712,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Użyj skrótu"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Odwrócenie kolorów"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Korekcja kolorów"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została włączona."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została wyłączona."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Naciśnij i przytrzymaj oba przyciski głośności przez trzy sekundy, by użyć usługi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1929,6 +1935,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Żądanie SS zmienione na rozmowę wideo"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Żądanie SS zmienione na żądanie USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Zmieniono na nowe żądanie SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alert o phishingu"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil służbowy"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alert"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Rozwiń"</string> @@ -1942,6 +1949,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maksymalizuj"</string> <string name="close_button_text" msgid="10603510034455258">"Zamknij"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Odbierz"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Odrzuć"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Rozłącz"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Połączenie przychodzące"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Trwa połączenie"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtruję połączenie przychodzące"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="few">Wybrano <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="many">Wybrano <xliff:g id="COUNT_1">%1$d</xliff:g></item> @@ -2276,4 +2289,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Aby kontynuować, musisz przyznać aplikacji „<xliff:g id="APP">%s</xliff:g>” dostęp do aparatu urządzenia."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Włącz"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Poufność danych z czujników"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 693af83522e7..d5cec263b6b9 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use sua impressão digital para continuar"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícone de impressão digital"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"O rosto não é mais reconhecido. Tente novamente."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Muito parecido, mude de posição."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Incline a cabeça um pouco menos."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Incline a cabeça um pouco menos."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Incline a cabeça um pouco menos."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Remova tudo que esteja ocultando seu rosto."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Limpe a parte superior da tela, inclusive a barra preta"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permitir que o app leia e grave a configuração \"Não perturbe\"."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso da permissão para visualização"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o sistema inicie o uso de permissão para um app. Não deve ser necessário para apps comuns."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acessar os dados do sensor em uma taxa de amostragem elevada"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que o app receba amostras de dados do sensor em uma taxa maior que 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Definir regras para senha"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Controla o tamanho e os caracteres permitidos nos PINs e nas senhas do bloqueio de tela."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorar tentativas de desbloqueio de tela"</string> @@ -864,7 +868,7 @@ <string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pausar"</string> <string name="lockscreen_transport_play_description" msgid="106868788691652733">"Reproduzir"</string> <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"Parar"</string> - <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Retroceder"</string> + <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Voltar"</string> <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Avançar"</string> <string name="emergency_calls_only" msgid="3057351206678279851">"Só chamadas de emergência"</string> <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Rede bloqueada"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Correção de cor"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Solicitação SS alterada para videochamada"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Solicitação SS alterada para solicitação USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Alterada para uma nova solicitação SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerta de phishing"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de trabalho"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alertado"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expandir"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximizar"</string> <string name="close_button_text" msgid="10603510034455258">"Fechar"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Atender"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Recusar"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Desligar"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada recebida"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada em andamento"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando uma chamada recebida"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionado</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Para continuar, o app <b><xliff:g id="APP">%s</xliff:g></b> precisa acessar a câmera do dispositivo."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Ativar"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacidade do sensor"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index e00034acf491..686795065c61 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem sensor de impressões digitais."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporariamente desativado."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Utilize a sua impressão digital para continuar."</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícone de impressão digital"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Impossível reconhecer o rosto. Tente novamente."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Muito parecida, mude de pose."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Rode a cabeça um pouco menos."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Rode a cabeça um pouco menos."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Rode a cabeça um pouco menos."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Remova tudo o que esteja a ocultar o seu rosto."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Limpe a parte superior do ecrã, incluindo a barra preta."</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite à app ler e alterar a configuração de Não incomodar"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar utilização da autorização de visualização"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o titular inicie a utilização de autorizações para uma app. Nunca deverá ser necessário para aplicações normais."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"aceder aos dados de sensores a uma taxa de amostragem elevada"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que a app obtenha uma amostra dos dados de sensores a uma taxa superior a 200 Hz."</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Definir regras de palavra-passe"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Controlar o comprimento e os carateres permitidos nos PINs e nas palavras-passe do bloqueio de ecrã."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorizar tentativas de desbloqueio do ecrã"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizar atalho"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Correção da cor"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas do volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Prima sem soltar as teclas de volume durante três segundos para utilizar o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"O pedido SS foi alterado para uma videochamada."</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"O pedido SS foi alterado para um novo pedido USSD."</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Foi alterado para um novo pedido SS."</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerta de phishing."</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de trabalho"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alertado"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expandir"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximizar"</string> <string name="close_button_text" msgid="10603510034455258">"Fechar"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Atender"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Recusar"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Desligar"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada recebida"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada em curso"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"A filtrar uma chamada recebida…"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selecionado</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Para continuar, a app <b><xliff:g id="APP">%s</xliff:g></b> precisa de acesso à câmara do dispositivo."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Ativar"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacidade dos sensores"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 693af83522e7..d5cec263b6b9 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Use sua impressão digital para continuar"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícone de impressão digital"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"O rosto não é mais reconhecido. Tente novamente."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Muito parecido, mude de posição."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Incline a cabeça um pouco menos."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Incline a cabeça um pouco menos."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Incline a cabeça um pouco menos."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Remova tudo que esteja ocultando seu rosto."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Limpe a parte superior da tela, inclusive a barra preta"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permitir que o app leia e grave a configuração \"Não perturbe\"."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"iniciar uso da permissão para visualização"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite que o sistema inicie o uso de permissão para um app. Não deve ser necessário para apps comuns."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"acessar os dados do sensor em uma taxa de amostragem elevada"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite que o app receba amostras de dados do sensor em uma taxa maior que 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Definir regras para senha"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Controla o tamanho e os caracteres permitidos nos PINs e nas senhas do bloqueio de tela."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitorar tentativas de desbloqueio de tela"</string> @@ -864,7 +868,7 @@ <string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pausar"</string> <string name="lockscreen_transport_play_description" msgid="106868788691652733">"Reproduzir"</string> <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"Parar"</string> - <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Retroceder"</string> + <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Voltar"</string> <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Avançar"</string> <string name="emergency_calls_only" msgid="3057351206678279851">"Só chamadas de emergência"</string> <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Rede bloqueada"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversão de cores"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Correção de cor"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Toque nos dois botões de volume e os mantenha pressionados por três segundo para usar o <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Solicitação SS alterada para videochamada"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Solicitação SS alterada para solicitação USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Alterada para uma nova solicitação SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerta de phishing"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de trabalho"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Alertado"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Expandir"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximizar"</string> <string name="close_button_text" msgid="10603510034455258">"Fechar"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Atender"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Recusar"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Desligar"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Chamada recebida"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Chamada em andamento"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Filtrando uma chamada recebida"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionado</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Para continuar, o app <b><xliff:g id="APP">%s</xliff:g></b> precisa acessar a câmera do dispositivo."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Ativar"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacidade do sensor"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 5fd6f1a0634b..690884a452f9 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -582,6 +582,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dispozitivul nu are senzor de amprentă."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzorul este dezactivat temporar."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Degetul <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Folosiți amprenta pentru a continua"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Pictograma amprentă"</string> @@ -608,7 +609,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Nu se mai poate recunoaște fața. Încercați din nou."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Prea asemănător, schimbați poziția."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Întoarceți capul mai puțin."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Întoarceți capul mai puțin."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Întoarceți capul mai puțin."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Eliminați orice vă ascunde chipul."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Curățați partea de sus a ecranului, inclusiv bara neagră"</string> @@ -687,6 +689,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite aplicației să citească și să scrie configurația Nu deranja."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"porniți folosirea permisiunii de vizualizare"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite proprietarului să pornească folosirea permisiunii pentru o aplicație. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"să acceseze date de la senzori la o rată de eșantionare mare"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Permite aplicației să colecteze date de la senzori la o rată de eșantionare de peste 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Să seteze reguli pentru parolă"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Stabiliți lungimea și tipul de caractere permise pentru parolele și codurile PIN de blocare a ecranului."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Să monitorizeze încercările de deblocare a ecranului"</string> @@ -1686,6 +1690,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizați comanda rapidă"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inversarea culorilor"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Corecția culorii"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S-au apăsat lung tastele de volum. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S-au apăsat lung tastele de volum. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Apăsați ambele butoane de volum timp de trei secunde pentru a folosi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1898,6 +1904,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Solicitarea SS a fost schimbată cu un apel video"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Solicitarea SS a fost schimbată cu o solicitare USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Schimbat cu o solicitare SS nouă"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alertă privind phishingul"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil de serviciu"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Notificat"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Extindeți"</string> @@ -1911,6 +1918,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximizați"</string> <string name="close_button_text" msgid="10603510034455258">"Închideți"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Răspundeți"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Respingeți"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Încheiați"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Apel primit"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Apel în desfășurare"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Se filtrează un apel primit"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> selectate</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selectate</item> @@ -2242,4 +2255,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Pentru a continua, <b><xliff:g id="APP">%s</xliff:g></b> necesită acces la camera dispozitivului."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Activați"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Confidențialitatea privind senzorii"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index d1cd374d2bc7..84752d5bfe5a 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -585,6 +585,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На этом устройстве нет сканера отпечатков пальцев."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сканер отпечатков пальцев временно отключен."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Отпечаток <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Чтобы продолжить, используйте цифровой отпечаток"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок отпечатка пальца"</string> @@ -611,7 +612,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Не удалось распознать лицо. Повторите попытку."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Слишком похожее выражение лица. Измените позу."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Держите голову ровнее."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Держите голову ровнее."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Держите голову ровнее."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Ваше лицо плохо видно."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Протрите верхнюю часть экрана (в том числе черную панель)."</string> @@ -690,6 +692,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Открывает приложению доступ к настройкам режима \"Не беспокоить\" и позволяет изменять их."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"Просмотр данных об используемых разрешениях"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Приложение получит доступ к данным об используемых разрешениях. Это разрешение не требуется обычным приложениям."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"Доступ к данным датчиков при высокой частоте дискретизации"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Приложение сможет считывать данные датчиков на частоте более 200 Гц."</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Настройка правил для паролей"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролировать длину и символы при вводе пароля и PIN-кода."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Отслеживание попыток разблокировать экран"</string> @@ -1708,6 +1712,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Использовать быстрое включение"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Инверсия цветов"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Коррекция цвета"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" включена."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" отключена."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Чтобы использовать сервис \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", нажмите и удерживайте обе клавиши громкости в течение трех секунд."</string> @@ -1929,6 +1935,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-запрос преобразован в видеовызов"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-запрос преобразован в USSD-запрос"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Преобразовано в SS-запрос"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Предупреждение о фишинге"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Рабочий профиль"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Отправлено оповещение"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Развернуть"</string> @@ -1942,6 +1949,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Развернуть"</string> <string name="close_button_text" msgid="10603510034455258">"Закрыть"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Ответить"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Отклонить"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Завершить"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Входящий вызов"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Текущий вызов"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Фильтрация входящего вызова"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one">Выбрано: <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="few">Выбрано: <xliff:g id="COUNT_1">%1$d</xliff:g></item> @@ -2276,4 +2289,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Чтобы продолжить, предоставьте приложению <b><xliff:g id="APP">%s</xliff:g></b> доступ к камере устройства."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Включить"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Конфиденциальность датчиков"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index d102183f25ad..66547decf56e 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"මෙම උපාංගයේ ඇඟිලි සලකුණු සංවේදකයක් නොමැත."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"සංවේදකය තාවකාලිකව අබල කර ඇත."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"ඇඟිලි <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ඉදිරියට යාමට ඔබගේ ඇඟිලි සලකුණ භාවිත කරන්න"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ඇඟිලි සලකුණු නිරූපකය"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"තවදුරටත් මුහුණ හඳුනාගත නොහැක. නැවත උත්සාහ කරන්න."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"ඉතා සමානයි, ඔබේ හැඩ ගැසීම වෙනස් කරන්න."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"ඔබේ හිස ටිකක් අඩුවෙන් කරකවන්න."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"ඔබේ හිස ටිකක් අඩුවෙන් කරකවන්න."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"ඔබේ හිස ටිකක් අඩුවෙන් කරකවන්න."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"ඔබේ මුහුණ සඟවන කිසිවක් ඉවත් කරන්න."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"කලු තීරුව ඇතුළුව, ඔබේ තිරයෙහි මුදුන පිරිසිදු කරන්න"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"බාධා නොකරන්න වින්යාස කිරීම කියවීමට සහ ලිවීමට යෙදුමට ඉඩ දෙයි."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"අවසර භාවිතය බැලීමට ආරම්භ කරන්න"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"තබා සිටින්නාට යෙදුමක් සඳහා අවසර භාවිතය ආරම්භ කිරීමට ඉඩ දෙයි. සාමාන්ය යෙදුම් සඳහා කිසි විටෙක අවශ්ය නොවිය යුතු ය."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"ඉහළ නියැදි කිරීමේ වේගයකින් සංවේදක දත්ත වෙත පිවිසෙන්න"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz ට වඩා වැඩි වේගයකින් සංවේදක දත්ත නියැදි කිරීමට යෙදුමට ඉඩ දෙයි"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"මුරපද නීති සකස් කිරීම"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"තිර අගුලු මුරපද සහ PIN තුළ ඉඩ දෙන දිග සහ අනුලකුණු පාලනය කිරීම."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"තිරය අගුළු ඇරීමේ උත්සාහයන් නිරීක්ෂණය කරන්න"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"කෙටිමඟ භාවිතා කරන්න"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"වර්ණ අපවර්තනය"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"වර්ණ නිවැරදි කිරීම"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාත්මකයි."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්රියාවිරහිතයි."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> භාවිත කිරීමට හඬ පරිමා යතුරු දෙකම තත්පර තුනකට ඔබාගෙන සිටින්න"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS ඉල්ලීම වීඩියෝ ඇමතුමට වෙනස් කරන ලදී"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS ඉල්ලීම USSD ඉල්ලීමට වෙනස් කරන ලදී"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"නව SS ඉල්ලීමට වෙනස් කරන ලදී"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"තතුබෑම් ඇඟවීම"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"කාර්යාල පැතිකඩ"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"අනතුරු අඟවන ලදී"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"දිග හරින්න"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"විහිදන්න"</string> <string name="close_button_text" msgid="10603510034455258">"වසන්න"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"පිළිතුරු දෙ."</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"ප්රතික්ෂේප ක"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"විසන්ධි කරන්න"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"එන ඇමතුම"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"කරගෙන යන ඇමතුම"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"එන ඇමතුමක් පරීක්ෂා කරන්න"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ක් තෝරන ලදි</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ක් තෝරන ලදි</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"දිගටම කර ගෙන යාමට, <b><xliff:g id="APP">%s</xliff:g></b> හට ඔබගේ උපාංගයෙහි කැමරාවට ප්රවේශය අවශ්යයි."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ක්රියාත්මක කරන්න"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"සංවේදක පෞද්ගලිකත්වය"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index ffa4b26589d5..487b8181e510 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -585,6 +585,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zariadenie nemá senzor odtlačkov prstov."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasne vypnutý."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst: <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Pokračujte nasnímaním odtlačku prsta"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona odtlačku prsta"</string> @@ -611,7 +612,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Tvár už nie je možné rozpoznať. Skúste to znova."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Príliš rovnaké, zmeňte postoj."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Otočte hlavu o niečo menej."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Otočte hlavu o niečo menej."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Otočte hlavu o niečo menej."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Odstráňte všetko, čo vám zakrýva tvár."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Vyčistite hornú časť obrazovky vrátane čierneho panela"</string> @@ -690,6 +692,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Umožňuje aplikácii čítať a zapisovať konfiguráciu režimu bez vyrušení."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"spustenie používania povolenia na zobrazenie"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Umožňuje držiteľovi spustiť používanie povolenia aplikáciou. Bežné aplikácie by toto povolenie nemali nikdy potrebovať."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"prístup k dátam senzorom s vysokou vzorkovacou frekvenciou"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Umožňuje aplikácii vzorkovať dáta senzorov s frekvenciou vyššou ako 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Nastaviť pravidlá pre heslo"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Nastavte dĺžku hesiel na odomknutie obrazovky aj kódov PIN a v nich používané znaky."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Sledovanie pokusov o odomknutie obrazovky"</string> @@ -1708,6 +1712,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Použiť skratku"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzia farieb"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Úprava farieb"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vypnutá."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Ak chcete používať službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, pridržte tri sekundy oba klávesy hlasitosti"</string> @@ -1929,6 +1935,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Žiadosť SS bola zmenená na videohovor"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Žiadosť SS bola zmenená na žiadosť USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Zmenené na novú žiadosť SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Upozornenie na phishing"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Pracovný profil"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Upozornené"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Rozbaliť"</string> @@ -1942,6 +1949,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximalizovať"</string> <string name="close_button_text" msgid="10603510034455258">"Zavrieť"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Prijať"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Odmietnuť"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Zložiť"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Prichádzajúci hovor"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Prebiehajúci hovor"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Preveruje sa prichádzajúci hovor"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="few">Vybrané: <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="many">Vybrané: <xliff:g id="COUNT_1">%1$d</xliff:g></item> @@ -2276,4 +2289,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Ak chcete pokračovať, <b><xliff:g id="APP">%s</xliff:g></b> požaduje prístup k fotoaparátu zariadenia."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Zapnúť"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Ochrana súkromia senzorov"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index d09ef047c624..87a66b36b7d7 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -150,8 +150,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Samo Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"Pomožno klicanje prek operaterja <xliff:g id="SPN">%s</xliff:g>"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ni posredovano"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po toliko sekundah: <xliff:g id="TIME_DELAY">{2}</xliff:g>"</string> @@ -586,6 +585,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ta naprava nima tipala prstnih odtisov."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tipalo je začasno onemogočeno."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Uporabite prstni odtis, če želite nadaljevati."</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona prstnih odtisov"</string> @@ -612,7 +612,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Obraza ni več mogoče prepoznati. Poskusite znova."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Preveč podobno, spremenite položaj."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Glejte malce bolj naravnost."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Malce manj nagnite glavo."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Glejte malce bolj naravnost."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Umaknite vse, kar vam morda zakriva obraz."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Očistite vrhnji del zaslona, vključno s črno vrstico"</string> @@ -691,6 +692,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Aplikaciji omogoča branje in pisanje konfiguracije načina »ne moti«."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"začetek uporabe dovoljenja za ogledovanje"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Imetniku omogoča začetek uporabe dovoljenj za aplikacijo. Nikoli ni potrebno za navadne aplikacije."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"dostop do podatkov tipal z večjo hitrostjo vzorčenja"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Aplikaciji dovoljuje, da vzorči podatke tipal s hitrostjo, večjo od 200 Hz."</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Nastavitev pravil za geslo"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Nadzor nad dolžino in znaki, ki so dovoljeni v geslih in kodah PIN za odklepanje zaslona."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Nadzor nad poskusi odklepanja zaslona"</string> @@ -1709,6 +1712,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Uporabi bližnjico"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverzija barv"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Popravljanje barv"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vklopljena."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je izklopljena."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Za uporabo storitve <xliff:g id="SERVICE_NAME">%1$s</xliff:g> pritisnite obe tipki za glasnost in ju pridržite tri sekunde"</string> @@ -1930,6 +1935,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Zahteva SS je spremenjena v videoklic"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Zahteva SS je spremenjena v zahtevo USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Spremenjeno v novo zahtevo SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Opozorilo o lažnem predstavljanju"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Delovni profil"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Opozorilo prikazano"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Razširi"</string> @@ -1943,6 +1949,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maksimiziraj"</string> <string name="close_button_text" msgid="10603510034455258">"Zapri"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Sprejmi"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Zavrni"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Prekini klic"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Dohodni klic"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Aktivni klic"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Preverjanje dohodnega klica"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> izbran</item> <item quantity="two"><xliff:g id="COUNT_1">%1$d</xliff:g> izbrana</item> @@ -2277,4 +2289,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Za nadaljevanje potrebuje aplikacija <b><xliff:g id="APP">%s</xliff:g></b> dostop do fotoaparata v napravi."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Vklopi"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Zasebnost pri uporabi tipal"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 592e54ddceb9..4c3742493fbb 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kjo pajisje nuk ka sensor të gjurmës së gishtit."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensori është çaktivizuar përkohësisht."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Gishti <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Përdor gjurmën e gishtit për të vazhduar"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona e gjurmës së gishtit"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Fytyra nuk mund të njihet më. Provo përsëri."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Tepër e ngjashme, ndrysho pozën"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Ktheje kokën pak më pak."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Ktheje kokën pak më pak."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Ktheje kokën pak më pak."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Hiq gjithçka që fsheh fytyrën tënde."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Pastro kreun e ekranit, duke përfshirë shiritin e zi"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Lejon aplikacionin të lexojë dhe shkruajë konfigurimin e \"Mos shqetëso\"."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"nis përdorimin e lejes për shikimin"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Lejon që mbajtësi të nisë përdorimin e lejeve për një aplikacion. Nuk duhet të nevojitet asnjëherë për aplikacionet normale."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"qasu te të dhënat e sensorit me një shpejtësi më të lartë shembulli"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Lejon aplikacionin të mbledhë shembujt e të dhënave të sensorit me shpejtësi më të lartë se 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Cakto rregullat e fjalëkalimit"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrollo gjatësinë dhe karakteret e lejuara në fjalëkalimet dhe kodet PIN të kyçjes së ekranit."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Monitoro tentativat e shkyçjes së ekranit"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Përdor shkurtoren"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Kthimi i ngjyrës"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Korrigjimi i ngjyrës"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tastet e volumit të mbajtura shtypur. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> i aktivizuar."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tastet e volumit të mbajtura shtypur. U çaktivizua \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Shtyp dhe mbaj shtypur të dy butonat e volumit për tre sekonda për të përdorur <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Kërkesa SS u ndryshua në telefonatë me video"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Kërkesa SS u ndryshua në kërkesë USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"U ndryshua në kërkesë të re SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Sinjalizim për mashtrim"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profili i punës"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Sinjalizuar"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Zgjero"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maksimizo"</string> <string name="close_button_text" msgid="10603510034455258">"Mbyll"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Përgjigju"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Refuzo"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Mbyll"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Telefonatë hyrëse"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Telefonatë në vazhdim"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Po filtron një telefonatë hyrëse"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> të zgjedhura</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> i zgjedhur</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Për të vazhduar, <b><xliff:g id="APP">%s</xliff:g></b> ka nevojë të qaset në kamerën e pajisjes sate."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktivizo"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privatësia e sensorit"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 65037a058067..d061d6f68b9f 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -582,6 +582,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Овај уређај нема сензор за отисак прста."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензор је привремено онемогућен."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Наставите помоћу отиска прста"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона отиска прста"</string> @@ -608,7 +609,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Више не може да се препозна лице. Пробајте поново."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Превише је слично, промените позу."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Мало мање померите главу."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Мало мање померите главу."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Мало мање померите главу."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Уклоните све што вам заклања лице."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Очистите горњи део екрана, укључујући црну траку"</string> @@ -687,6 +689,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Дозвољава апликацији да чита и уписује конфигурацију подешавања Не узнемиравај."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"почетак коришћења дозволе за преглед"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Дозвољава власнику да започне коришћење дозволе за апликацију. Никада не би требало да буде потребна за уобичајене апликације."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"приступ подацима сензора при великој брзини узорковања"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Дозвољава апликацији да узима узорак података сензора при брзини већој од 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Подешавање правила за лозинку"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Контролише дужину и знакове дозвољене у лозинкама и PIN-овима за закључавање екрана."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Надгледајте покушаје откључавања екрана"</string> @@ -1686,6 +1690,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Користи пречицу"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Инверзија боја"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Корекција боја"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је укључена."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је искључена."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Притисните и задржите оба тастера за јачину звука три секунде да бисте користили <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1898,6 +1904,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS захтев је промењен у видео позив"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS захтев је промењен у USSD захтев"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Промењено је у нови SS захтев"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Упозорење о „пецању“"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Пословни профил"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Обавештено"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Прошири"</string> @@ -1911,6 +1918,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Увећај"</string> <string name="close_button_text" msgid="10603510034455258">"Затвори"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Одговори"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Одбиј"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Прекини везу"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Долазни позив"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Позив је у току"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Проверава се долазни позив"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one">Изабрана је <xliff:g id="COUNT_1">%1$d</xliff:g> ставка</item> <item quantity="few">Изабране су <xliff:g id="COUNT_1">%1$d</xliff:g> ставке</item> @@ -2242,4 +2255,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"<b><xliff:g id="APP">%s</xliff:g></b> захтева приступ камери уређаја ради настављања."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Укључи"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Приватност сензора"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index ef7a102fa257..8288d413e9c9 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Enheten har ingen fingeravtryckssensor."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensorn har tillfälligt inaktiverats."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Fortsätt med hjälp av ditt fingeravtryck"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon för fingeravtryck"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Ansiktet kan inte längre kännas igen. Försök igen."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"För likt. Ändra ansiktsposition."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Vrid mindre på huvudet."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Vrid mindre på huvudet."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Vrid mindre på huvudet."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Ta bort allt som täcker ansiktet."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Rengör skärmens överkant, inklusive det svarta fältet"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ger appen läs- och skrivbehörighet till konfigurationen för Stör ej."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"börja visa behörighetsanvändningen"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Gör att innehavaren kan öppna behörighetsanvändning för en app. Ska inte behövas för vanliga appar."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"åtkomst till sensordata med en hög samplingsfrekvens"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Tillåter att appen får åtkomst till sensordata med en högre samplingsfrekvens än 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Ange lösenordsregler"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Styr tillåten längd och tillåtna tecken i lösenord och pinkoder för skärmlåset."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Övervaka försök att låsa upp skärmen"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Använd kortkommandot"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Inverterade färger"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Färgkorrigering"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har aktiverats."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har inaktiverats."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Tryck och håll båda volymknapparna i tre sekunder för att använda <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS-begäran har ändrats till videosamtal"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS-begäran har ändrats till en USSD-begäran"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Har ändrats till ny SS-begäran"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Varning om nätfiske"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Jobbprofil"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Aviserad"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Utöka"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maximera"</string> <string name="close_button_text" msgid="10603510034455258">"Stäng"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Svara"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Avvisa"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Lägg på"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Inkommande samtal"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Pågående samtal"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Ett inkommande samtal förhandsgranskas"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> har valts</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> har valts</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"<b><xliff:g id="APP">%s</xliff:g></b> behöver behörighet till enhetens kamera för att fortsätta."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aktivera"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorintegritet"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index cc82f4ec8fea..6ac7d2a583e2 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kifaa hiki hakina kitambua alama ya kidole."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Kitambuzi kimezimwa kwa muda."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Kidole cha <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Tumia alama ya kidole chako ili uendelee"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Aikoni ya alama ya kidole"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Haiwezi tena kutambua uso. Jaribu tena."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Inafanana sana, tafadhali badilisha mkao wako."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Geuza kichwa chako kidogo."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Geuza kichwa chako kidogo."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Geuza kichwa chako kidogo."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Ondoa kitu chochote kinachoficha uso wako."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Safisha sehemu ya juu ya skrini yako, ikiwa ni pamoja na upau mweusi"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Inaruhusu programu kusoma na kuandika usanidi wa kipengee cha Usinisumbue."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"anzisha kipengele cha kuona matumizi ya ruhusa"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Huruhusu kishikiliaji kuanzisha matumizi ya ruhusa ya programu. Haipaswi kuhitajika kwa ajili ya programu za kawaida."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"fikia data ya vitambuzi kwa kasi ya juu ya sampuli"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Huruhusu programu kujaribu sampuli ya data ya vitambuzi kwa kasi inayozidi Hz 200"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Kuweka kanuni za nenosiri"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Dhibiti urefu na maandishi yanayokubalika katika nenosiri la kufunga skrini na PIN."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Kuhesabu mara ambazo skrini inajaribu kufunguliwa"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tumia Njia ya Mkato"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Ugeuzaji rangi"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Usahihishaji wa rangi"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Vitufe vya sauti vilivyoshikiliwa. Umewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Vitufe vya sauti vimeshikiliwa. Umezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Bonyeza na ushikilie vitufe vyote viwili vya sauti kwa sekunde tatu ili utumie <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Imebadilisha ombi la SS kuwa simu ya video"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Imebadilisha ombi la SS kuwa ombi la USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Imebadilisha kuwa ombi jipya la SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Arifa ya wizi wa data binafsi"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Wasifu wa kazini"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Imearifu"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Panua"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Panua"</string> <string name="close_button_text" msgid="10603510034455258">"Funga"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Jibu"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Kataa"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Kata simu"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Simu uliyopigiwa"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Simu inayoendelea"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Inachuja simu unayopigiwa"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> vimechaguliwa</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> kimechaguliwa</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Ili uendelee, <b><xliff:g id="APP">%s</xliff:g></b> inahitaji ruhusa ya kufikia kamera ya kifaa chako."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Washa"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Faragha ya Kitambuzi"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index ca90171d50ce..1ac17340e941 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"சென்சார் தற்காலிகமாக முடக்கப்பட்டுள்ளது."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"கைரேகை <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"தொடர்வதற்கு கைரேகையைப் பயன்படுத்துங்கள்"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"கைரேகை ஐகான்"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"முகத்தைக் கண்டறிய இயலவில்லை. மீண்டும் முயலவும்."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"மீண்டும் அதே போஸ் தருகிறீர்கள், வேறு முயலுங்கள்."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"தலையை லேசாகத் திருப்பவும்."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"தலையை லேசாகத் திருப்பவும்."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"உங்கள் தலையைச் சற்றுத் திருப்பவும்."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"உங்கள் முகத்தை மறைக்கும் அனைத்தையும் நீக்குக."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"திரையையும் அதிலுள்ள கருப்புப் பட்டியையும் சுத்தம் செய்யவும்"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"தொந்தரவு செய்ய வேண்டாம் உள்ளமைவைப் படிக்கவும் எழுதவும், ஆப்ஸை அனுமதிக்கிறது."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"அனுமதி உபயோகத்தை அணுகுதல்"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"ஆப்ஸிற்கான அனுமதி உபயோகத்தை ஹோல்டருக்கு வழங்கும். இயல்பான ஆப்ஸிற்கு இது எப்போதுமே தேவைப்படாது."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"அதிகளவிலான சாம்பிளிங் ரேட்டில் சென்சார் தரவை அணுகுதல்"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 ஹெர்ட்ஸ்க்கும் அதிகமான வீதத்தில் சென்சார் தரவை மாதிரியாக்க ஆப்ஸை அனுமதிக்கும்"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"கடவுச்சொல் விதிகளை அமைக்கவும்"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"திரைப் பூட்டின் கடவுச்சொற்கள் மற்றும் பின்களில் அனுமதிக்கப்படும் நீளத்தையும் எழுத்துக்குறிகளையும் கட்டுப்படுத்தும்."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"திரையைத் திறப்பதற்கான முயற்சிகளைக் கண்காணி"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ஷார்ட்கட்டைப் பயன்படுத்து"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"கலர் இன்வெர்ஷன்"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"வண்ணத் திருத்தம்"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆன் செய்யப்பட்டது."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆஃப் செய்யப்பட்டது."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ஐப் பயன்படுத்த 3 விநாடிகளுக்கு இரண்டு ஒலியளவு பட்டன்களையும் அழுத்திப் பிடிக்கவும்"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS கோரிக்கை, வீடியோ அழைப்பிற்கு மாற்றப்பட்டது"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS கோரிக்கை, USSD கோரிக்கைக்கு மாற்றப்பட்டது"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"புதிய SS கோரிக்கைக்கு மாற்றப்பட்டது"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ஃபிஷிங் எச்சரிக்கை"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"பணிக் கணக்கு"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"விழிப்பூட்டல் ஐகான்"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"விரிவாக்கும்"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"பெரிதாக்கு"</string> <string name="close_button_text" msgid="10603510034455258">"மூடு"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"பதிலளி"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"நிராகரி"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"துண்டி"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"உள்வரும் அழைப்பு"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"செயலில் இருக்கும் அழைப்பு"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"உள்வரும் அழைப்பை மதிப்பாய்வு செய்கிறது"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> தேர்ந்தெடுக்கப்பட்டன</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> தேர்ந்தெடுக்கப்பட்டது</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"தொடர, உங்கள் சாதனத்தின் கேமராவை அணுகுவதற்கு <b><xliff:g id="APP">%s</xliff:g></b> ஆப்ஸுக்கு அனுமதி வேண்டும்."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ஆன் செய்"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"சென்சார் தனியுரிமை"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index b0726e811d47..9f822e31260c 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi మాత్రమే"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> బ్యాకప్ కాలింగ్"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ఫార్వార్డ్ చేయబడలేదు"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> సెకన్ల తర్వాత <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ఈ పరికరంలో వేలిముద్ర సెన్సార్ ఎంపిక లేదు."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"సెన్సార్ తాత్కాలికంగా డిజేబుల్ చేయబడింది."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"వేలు <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"కొనసాగించడానికి మీ వేలిముద్రను ఉపయోగించండి"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"వేలిముద్ర చిహ్నం"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"ఇక ముఖం గుర్తించలేదు. మళ్లీ ప్రయత్నించండి."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"ఒకే మాదిరిగా ఉంది, దయచేసి భంగిమను మార్చండి."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"మీ తలను ఇంకాస్త తక్కువ తిప్పండి."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"మీ తలను ఇంకాస్త తక్కువ తిప్పండి."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"మీ తలను ఎడమ/కుడి వైపుగా ఇంకాస్త తిప్పండి."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"మీ ముఖానికి అడ్డుగా ఉన్నవాటిని తీసివేస్తుంది."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"నల్లని పట్టీతో సహా మీ స్క్రీన్ పైభాగం అంతటినీ శుభ్రంగా తుడవండి"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు వ్రాయడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"వీక్షణ అనుమతి వినియోగాన్ని ప్రారంభించండి"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"యాప్నకు అనుమతి వినియోగాన్ని ప్రారంభించడానికి హోల్డర్ను అనుమతిస్తుంది. సాధారణ యాప్లకు ఎప్పటికీ ఇటువంటి అనుమతి అవసరం ఉండదు."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"అధిక శాంపిల్ రేటు వద్ద సెన్సార్ డేటాను యాక్సెస్ చేయండి"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"200 Hz కంటే ఎక్కువ రేట్ వద్ద శాంపిల్ సెన్సార్ డేటాకు యాప్ను అనుమతిస్తుంది"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"పాస్వర్డ్ నియమాలను సెట్ చేయండి"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"స్క్రీన్ లాక్ పాస్వర్డ్లు మరియు PINల్లో అనుమతించబడిన పొడవు మరియు అక్షరాలను నియంత్రిస్తుంది."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"స్క్రీన్ అన్లాక్ ప్రయత్నాలను పర్యవేక్షించండి"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"సత్వరమార్గాన్ని ఉపయోగించు"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"కలర్ మార్పిడి"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"కలర్ సరిచేయడం"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆన్ చేయబడింది"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>ని ఉపయోగించడానికి వాల్యూమ్ కీలు రెండింటినీ 3 సెకన్లు నొక్కి ఉంచండి"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS అభ్యర్థన వీడియో కాల్కి మార్చబడింది"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS అభ్యర్థన USSD అభ్యర్థనకు మార్చబడింది"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"కొత్త SS అభ్యర్థనకు మార్చబడింది"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ఫిషింగ్ అలర్ట్"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"కార్యాలయ ప్రొఫైల్"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"హెచ్చరించబడింది"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"విస్తరింపజేయి"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"గరిష్టీకరించు"</string> <string name="close_button_text" msgid="10603510034455258">"మూసివేయి"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"సమాధానం ఇవ్వు"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"తిరస్కరించండి"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"కాల్ ముగించు"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"ఇన్కమింగ్ కాల్"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"కాల్ కొనసాగుతోంది"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"ఇన్కమింగ్ కాల్ను స్క్రీన్ చేయండి"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ఎంచుకోబడ్డాయి</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ఎంచుకోబడింది</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"కొనసాగించడానికి, <b><xliff:g id="APP">%s</xliff:g></b&gtకు మీ పరికరం యొక్క కెమెరా యాక్సెస్ అవసరం."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"ఆన్ చేయి"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"సెన్సార్ గోప్యత"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 5079f232091b..21865cde4a70 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ปิดใช้เซ็นเซอร์ชั่วคราวแล้ว"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"นิ้ว <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"ใช้ลายนิ้วมือของคุณเพื่อดำเนินการต่อ"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ไอคอนลายนิ้วมือ"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"จำใบหน้าไม่ได้แล้ว ลองอีกครั้ง"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"ใกล้เคียงเกินไป โปรดเปลี่ยนท่าโพส"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"จัดตำแหน่งศีรษะให้ตรง"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"จัดตำแหน่งศีรษะให้ตรง"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"จัดตำแหน่งศีรษะให้ตรง"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"เอาสิ่งที่ปิดบังใบหน้าออก"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"ทำความสะอาดด้านบนของหน้าจอ รวมถึงแถบสีดำ"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"อนุญาตให้แอปอ่านและเขียนการกำหนดค่าโหมดห้ามรบกวน"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"เริ่มการใช้สิทธิ์การดู"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"อนุญาตให้เจ้าของเริ่มการใช้สิทธิ์ของแอป ไม่จำเป็นสำหรับแอปทั่วไป"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"เข้าถึงข้อมูลเซ็นเซอร์ที่อัตราการสุ่มตัวอย่างสูง"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"อนุญาตให้แอปสุ่มตัวอย่างข้อมูลเซ็นเซอร์ที่อัตราสูงกว่า 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"ตั้งค่ากฎรหัสผ่าน"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"ควบคุมความยาวและอักขระที่สามารถใช้ในรหัสผ่านของการล็อกหน้าจอและ PIN"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"ตรวจสอบความพยายามในการปลดล็อกหน้าจอ"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ใช้ทางลัด"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"การกลับสี"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"การแก้สี"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"กดปุ่มปรับระดับเสียงค้างไว้แล้ว เปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"กดปุ่มปรับระดับเสียงค้างไว้แล้ว ปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"กดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 3 วินาทีเพื่อใช้ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"คำขอ SS เปลี่ยนเป็นวิดีโอคอลแล้ว"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"คำขอ SS เปลี่ยนเป็นคำขอ USSD แล้ว"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"เปลี่ยนเป็นคำขอ SS ใหม่แล้ว"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"การแจ้งเตือนฟิชชิง"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"โปรไฟล์งาน"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"แจ้งเตือนแล้ว"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"ขยาย"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"ขยายใหญ่สุด"</string> <string name="close_button_text" msgid="10603510034455258">"ปิด"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"รับสาย"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"ปฏิเสธ"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"วางสาย"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"สายเรียกเข้า"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"สายที่สนทนาอยู่"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"กำลังสกรีนสายเรียกเข้า"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other">เลือกไว้ <xliff:g id="COUNT_1">%1$d</xliff:g> รายการ</item> <item quantity="one">เลือกไว้ <xliff:g id="COUNT_0">%1$d</xliff:g> รายการ</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"<b><xliff:g id="APP">%s</xliff:g></b> ต้องได้รับสิทธิ์เข้าถึงกล้องของอุปกรณ์เพื่อดำเนินการต่อ"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"เปิด"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"ความเป็นส่วนตัวสำหรับเซ็นเซอร์"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 81bfe5258284..37d06f83284f 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Walang sensor ng fingerprint ang device na ito."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Pansamantalang na-disable ang sensor."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Daliri <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Gamitin ang iyong fingerprint para magpatuloy"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icon ng fingerprint"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Hindi na makilala ang mukha. Subukang muli."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Masyadong magkatulad, pakibago ang pose mo."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Huwag masyadong lumingon."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Huwag masyadong tumingala o yumuko."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Huwag masyadong lumingon."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Alisin ang anumang humaharang sa iyong mukha."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Linisin ang itaas ng iyong screen, kasama ang itim na bar"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Nagbibigay-daan sa app na basahin at isulat ang configuration ng Huwag Istorbohin."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"simulan ang paggamit sa pahintulot sa pagtingin"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Binibigyang-daan ang may hawak na simulan ang paggamit ng pahintulot para sa isang app. Hindi dapat kailanganin kailanman para sa mga normal na app."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"mag-access ng data ng sensor sa mataas na rate ng pag-sample"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Pinapahintulutan ang app na mag-sample ng data ng sensor sa rate na higit sa 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Magtakda ng mga panuntunan sa password"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Kontrolin ang haba at ang mga character na pinapayagan sa mga password at PIN sa screen lock."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Subaybayan ang mga pagsubok sa pag-unlock ng screen"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Gamitin ang Shortcut"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Pag-invert ng Kulay"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Pagwawasto ng Kulay"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pinindot nang matagal ang volume keys. Na-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pinindot nang matagal ang volume keys. Na-off ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Pindutin nang matagal ang parehong volume key sa loob ng tatlong segundo para magamit ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Ginawang video call ang SS na kahilingan"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Ginawang USSD na kahilingan ang SS na kahilingan"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Ginawang bagong SS na kahilingan"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Alerto sa phishing"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profile sa trabaho"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Naalertuhan"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Palawakin"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"I-maximize"</string> <string name="close_button_text" msgid="10603510034455258">"Isara"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Sagutin"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Tanggihan"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Ibaba"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Papasok na tawag"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Kasalukuyang tawag"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Nagsi-screen ng papasok na tawag"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ang napili</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ang napili</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Para magpatuloy, kailangan ng <b><xliff:g id="APP">%s</xliff:g></b> ng access sa camera ng iyong device."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"I-on"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Privacy ng Sensor"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 7095bb8c5875..2132aace6350 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda parmak izi sensörü yok."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensör geçici olarak devre dışı bırakıldı."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. parmak"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Devam etmek için parmak izinizi kullanın"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Parmak izi simgesi"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Yüz artık tanınamıyor. Tekrar deneyin."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Duruşunuz çok benzer, lütfen pozunuzu değiştirin."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Başınızı biraz daha az çevirin."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Başınızı biraz daha az çevirin."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Başınızı biraz daha az çevirin."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Yüzünüzün görünmesini engelleyen şeyleri kaldırın."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Siyah çubuk da dahil olmak üzere ekranınızın üst kısmını temizleyin"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Uygulamaya, Rahatsız Etmeyin yapılandırmasını okuma ve yazma izni verir."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"izin kullanımı görüntülemeye başlama"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"İzin sahibinin bir uygulama için izin kullanımı başlatmasına olanak tanır. Normal uygulamalar için hiçbir zaman kullanılmamalıdır."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"sensör verilerine daha yüksek örnekleme hızında eriş"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Uygulamanın, sensör verilerini 200 Hz\'den daha yüksek bir hızda örneklemesine olanak tanır"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Şifre kuralları ayarla"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Ekran kilidini açma şifrelerinde ve PIN\'lerde izin verilen uzunluğu ve karakterleri denetleyin."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Ekran kilidini açma denemelerini izle"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Kısayolu Kullan"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Rengi Ters Çevirme"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Renk Düzeltme"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> açıldı."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kapatıldı."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini kullanmak için her iki ses tuşunu basılı tutun"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS isteği görüntülü görüşme olarak değişti"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS isteği USSD isteği olarak değişti"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Yeni SS isteği olarak değişti"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Kimlik avı uyarısı"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"İş profili"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Sesli uyarıldı"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Genişlet"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Ekranı Kapla"</string> <string name="close_button_text" msgid="10603510034455258">"Kapat"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Yanıtla"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Reddet"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Kapat"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Gelen çağrı"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Devam eden çağrı"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Gelen arama süzülüyor"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> öğe seçildi</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> öğe seçildi</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Devam etmek için <b><xliff:g id="APP">%s</xliff:g></b> uygulamasının cihazınızın kamerasına erişmesi gerekiyor."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Aç"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensör Gizliliği"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 9864b4b4e4a4..3b9db401be3a 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -585,6 +585,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На цьому пристрої немає сканера відбитків пальців."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик тимчасово вимкнено."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Відбиток пальця <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Щоб продовжити, скористайтеся своїм відбитком пальця"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок відбитка пальця"</string> @@ -611,7 +612,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Розпізнати обличчя вже не вдається. Повторіть спробу."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Надто схоже на попередню спробу, змініть позу."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Трохи перемістіть обличчя."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Трохи перемістіть обличчя."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Трохи поверніть обличчя."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Приберіть об’єкти, які затуляють ваше обличчя."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Очистьте верхню частину екрана, зокрема чорну панель"</string> @@ -690,6 +692,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Додаток зможе переглядати та змінювати конфігурацію режиму \"Не турбувати\"."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"перегляньте дані про використання дозволів"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Власник зможе використовувати дозволи для цього додатка. Цей дозвіл не потрібен для звичайних додатків."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"доступ до даних датчиків із високою частотою дикретизації"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Додаток зможе дискретизувати дані даних датчиків із частотою понад 200 Гц"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Устан. правила пароля"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Укажіть максимальну довжину та кількість символів для паролів розблокування екрана та PIN-кодів."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Відстежувати спроби розблокування екрана"</string> @@ -1708,6 +1712,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Використовувати ярлик"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Інверсія кольорів"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Корекція кольорів"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> увімкнено."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> вимкнено."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Щоб скористатися службою <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, утримуйте обидві клавіші гучності впродовж трьох секунд"</string> @@ -1929,6 +1935,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Запит SS змінено на відеовиклик"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Запит SS змінено на запит USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Змінено на новий запит SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Попередження про фішинг"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Робочий профіль"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Зі звуком"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Розгорнути"</string> @@ -1942,6 +1949,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Розгорнути"</string> <string name="close_button_text" msgid="10603510034455258">"Закрити"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Відповісти"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Відхилити"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Завершити"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Вхідний виклик"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Активний виклик"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Фільтрування вхідного виклику"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one">Вибрано <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="few">Вибрано <xliff:g id="COUNT_1">%1$d</xliff:g></item> @@ -2276,4 +2289,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Щоб продовжити, надайте додатку <b><xliff:g id="APP">%s</xliff:g></b> доступ до камери пристрою."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Увімкнути"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Конфіденційність датчиків"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 64fb2fc414e2..c10f3e97bca2 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -148,8 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"صرف Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <!-- no translation found for crossSimFormat_spn_cross_sim_calling (5620807020002879057) --> - <skip /> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"<xliff:g id="SPN">%s</xliff:g> بیک اپ کالنگ"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : فارورڈ نہیں کی گئی"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد از <xliff:g id="TIME_DELAY">{2}</xliff:g> سیکنڈ"</string> @@ -580,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"اس آلہ میں فنگر پرنٹ سینسر نہیں ہے۔"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"سینسر عارضی طور غیر فعال ہے۔"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"انگلی <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"جاری رکھنے کیلئے اپنا فنگر پرنٹ استعمال کریں"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"فنگر پرنٹ آئیکن"</string> @@ -606,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"اب چہرے کی شناخت نہیں کر سکتے۔ پھر آزمائيں۔"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"کافی ملتا جلتا ہے، براہ کرم اپنا پوز بدلیں۔"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"اپنا سر تھوڑا کم کریں۔"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"اپنا سر تھوڑا کم کریں۔"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"اپنا سر تھوڑا کم کریں۔"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"آپ کے چہرہ کو چھپانے والی ہر چیز کو ہٹائیں۔"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"سیاہ بار سمیت، اپنی اسکرین کے اوپری حصے کو صاف کریں"</string> @@ -685,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"ایپ کو ڈسٹرب نہ کریں کنفیگریشن لکھنے اور پڑھنے کے قابل کرتا ہے۔"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"اجازت کی استعمال کا ملاحظہ شروع کریں"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"حامل کو ایپ کی اجازت کے استعمال کو شروع کرنے کی اجازت دیتا ہے۔ عام ایپس کے لیے کبھی بھی درکار نہیں ہونا چاہیے۔"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"نمونہ کاری کی اعلی شرح پر سینسر کے ڈیٹا تک رسائی حاصل کریں"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"ایپ کو Hz200 سے زیادہ شرح پر سینسر ڈیٹا کا نمونہ لینے کی اجازت دیتی ہے"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"پاس ورڈ کے اصول سیٹ کریں"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"اسکرین لاک پاس ورڈز اور PINs میں اجازت یافتہ لمبائی اور حروف کو کنٹرول کریں۔"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"اسکرین غیر مقفل کرنے کی کوششیں مانیٹر کریں"</string> @@ -1665,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"شارٹ کٹ استعمال کریں"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"رنگوں کی تقلیب"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"رنگ کی تصحیح"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آن ہے۔"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آف ہے۔"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> کا استعمال کرنے کے لیے 3 سیکنڈ تک والیوم کی دونوں کلیدوں کو چھوئیں اور دبائے رکھیں"</string> @@ -1868,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS درخواست کو ویڈیو کال میں تبدیل کر دیا گیا"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS درخواست کو USSD درخواست میں تبدیل کر دیا گیا"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"نئی SS درخواست میں تبدیل کر دیا گیا"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"فریب دہی کا الرٹ"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"دفتری پروفائل"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"الرٹ کیا گیا"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"پھیلائیں"</string> @@ -1881,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"بڑا کریں"</string> <string name="close_button_text" msgid="10603510034455258">"بند کریں"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"جواب"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"مسترد کریں"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"منقطع کر دیں"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"اِن کمنگ کال"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"جاری کال"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"اِن کمنگ کال کی اسکریننگ"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> منتخب کردہ</item> <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> منتخب کردہ</item> @@ -2209,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"جاری رکھنے کیلئے <b><xliff:g id="APP">%s</xliff:g></b> کو آپ کے آلے کے کیمرے تک رسائی درکار ہے۔"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"آن کریں"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"سینسر کی رازداری"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 6612cc9a875a..f77de7daad1d 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu qurilmada barmoq izi skaneri mavjud emas."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor vaqtincha faol emas."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Barmoq izi <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Davom etish uchun barmoq izingizdan foydalaning"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Barmoq izi belgisi"</string> @@ -605,7 +606,7 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Yuz tanilmadi. Qaytadan urining."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Yuz ifodasi oldingiday. Holatingizni oʻzgartiring."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Boshingizni asta buring."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Boshingizni asta buring."</string> + <string name="face_acquired_tilt_too_extreme" msgid="8618210742620248049">"Boshingizni asta qiyalang."</string> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Boshingizni asta buring."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Yuzingizni berkitayotgan narsalarni olib tashlang."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Ekranning yuqori qismini, shuningdek, qora panelni ham tozalang"</string> @@ -684,6 +685,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"“Bezovta qilinmasin” rejimi sozlamalarini ko‘rish va o‘zgartirish."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"foydalaniladigan ruxsatlar axborotini ochish"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ilova foydalanadigan ruxsatlar axborotini ishga tushirishga ruxsat beradi. Oddiy ilovalar uchun talab qilinmaydi."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"yuqori diskretlash chastotali sensor axborotiga ruxsat"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Ilova sensor axborotini 200 Hz dan yuqori tezlikda hisoblashi mumkin"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Parol qoidalarini o‘rnatish"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Ekran qulfi paroli va PIN kodlari uchun qo‘yiladigan talablarni (belgilar soni va uzunligi) nazorat qiladi."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Ekranni qulfdan chiqarishga urinishlarni nazorat qilish"</string> @@ -1664,6 +1667,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Tezkor ishga tushirishdan foydalanish"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Ranglarni akslantirish"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Rangni tuzatish"</string> + <string name="reduce_bright_colors_feature_name" msgid="8978255324027479398">"Yorqinlikni pasaytirish"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> yoqildi."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> faolsizlantirildi."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"<xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmatidan foydalanish uchun ikkala ovoz balandligi tugmalarini uzoq bosib turing"</string> @@ -1867,6 +1871,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS talabi video chaqiruvga almashtirildi"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS talabi USSD talabiga almashtirildi"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Yangi SS talabiga almashtirildi"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Fishing signali"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Ish profili"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Ogohlantirildi"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Yoyish"</string> @@ -1880,6 +1885,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Maksimallashtirish"</string> <string name="close_button_text" msgid="10603510034455258">"Yopish"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Javob berish"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Rad etish"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Tugatish"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Kiruvchi chaqiruv"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Joriy chaqiruv"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Kiruvchi chaqiruvni filtrlash"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other">Belgilandi: <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="one">Belgilandi: <xliff:g id="COUNT_0">%1$d</xliff:g></item> @@ -2208,4 +2219,6 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Davom etish uchun <b><xliff:g id="APP">%s</xliff:g></b> qurilmangiz kamerasiga kirishi kerak."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Yoqish"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorlar maxfiyligi"</string> + <string name="splash_screen_view_icon_description" msgid="180638751260598187">"Ilova belgisi"</string> + <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Ilova brendining rasmi"</string> </resources> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 8f8e0bce3409..baf0b86dd1be 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Thiết bị này không có cảm biến vân tay."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Đã tạm thời tắt cảm biến."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Ngón tay <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Sử dụng dấu vân tay của bạn để tiếp tục"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Biểu tượng vân tay"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Không nhận ra khuôn mặt. Hãy thử lại."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Khuôn mặt quá giống nhau, vui lòng đổi tư thế."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Hãy bớt di chuyển đầu."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Hãy bớt di chuyển đầu."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Hãy bớt di chuyển đầu."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Hãy loại bỏ mọi thứ che khuất khuôn mặt bạn."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Vệ sinh phần đầu màn hình, bao gồm cả thanh màu đen"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Cho phép ứng dụng đọc và ghi cấu hình Không làm phiền."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"cấp quyền xem"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Cho phép chủ sở hữu cấp quyền cho một ứng dụng. Các ứng dụng thông thường sẽ không bao giờ cần quyền này."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"truy cập vào dữ liệu cảm biến ở tốc độ lấy mẫu cao"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Cho phép ứng dụng lấy mẫu dữ liệu cảm biến ở tốc độ lớn hơn 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Đặt quy tắc mật khẩu"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Kiểm soát độ dài và ký tự được phép trong mật khẩu khóa màn hình và mã PIN."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Giám sát những lần thử mở khóa màn hình"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sử dụng phím tắt"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Đảo màu"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Chỉnh màu"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã bật."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã tắt."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Nhấn và giữ đồng thời cả hai phím âm lượng trong 3 giây để sử dụng <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Yêu cầu SS đã thay đổi thành cuộc gọi video"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Yêu cầu SS đã thay đổi thành yêu cầu USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Đã thay đổi thành yêu cầu SS mới"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Cảnh báo về hành vi lừa đảo"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Hồ sơ công việc"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Đã phát âm báo"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Mở rộng"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Tối đa hóa"</string> <string name="close_button_text" msgid="10603510034455258">"Đóng"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Trả lời"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Từ chối"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Kết thúc"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Cuộc gọi đến"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Cuộc gọi đang thực hiện"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Đang sàng lọc cuộc gọi đến"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other">Đã chọn <xliff:g id="COUNT_1">%1$d</xliff:g></item> <item quantity="one">Đã chọn <xliff:g id="COUNT_0">%1$d</xliff:g></item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Để tiếp tục, <b><xliff:g id="APP">%s</xliff:g></b> cần quyền truy cập vào máy ảnh trên thiết bị của bạn."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Bật"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Quyền riêng tư khi sử dụng cảm biến"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index af5f846d368b..9345fc82b1d3 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此设备没有指纹传感器。"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"传感器已暂时停用。"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"使用指纹完成验证才能继续"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指纹图标"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"已无法识别人脸,请重试。"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"与先前的姿势太相近,请换一个姿势。"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"请将您的头稍微上下倾斜。"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"请将您的头稍微上下倾斜。"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"请将您的头稍微左右旋转。"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"请移除所有遮挡您面部的物体。"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"请将屏幕顶部(包括黑色条栏)清理干净"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允许此应用读取和写入“勿扰”模式配置。"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"授权使用“查看权限”"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允许该应用开始查看应用的权限使用情况(普通应用绝不需要此权限)。"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"以高采样率访问传感器数据"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"允许应用以高于 200 Hz 的频率对传感器数据进行采样"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"设置密码规则"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"控制锁屏密码和 PIN 码所允许的长度和字符。"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"监控屏幕解锁尝试次数"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用快捷方式"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"颜色反转"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已开启。"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已关闭。"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"同时按住两个音量键 3 秒钟即可使用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS 请求已更改为视频通话"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS 请求已更改为 USSD 请求"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"已更改为新的 SS 请求"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"网上诱骗警报"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"工作资料"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"已提醒"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"展开"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"最大化"</string> <string name="close_button_text" msgid="10603510034455258">"关闭"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>:<xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"接听"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"拒接"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"挂断"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"来电"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"正在通话"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"正在过滤来电"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other">已选择 <xliff:g id="COUNT_1">%1$d</xliff:g> 项</item> <item quantity="one">已选择 <xliff:g id="COUNT_0">%1$d</xliff:g> 项</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"如要继续操作,请向<b><xliff:g id="APP">%s</xliff:g></b>授予设备的相机使用权。"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"开启"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"传感器隐私权"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 65586f7f1b09..1187f003124d 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -148,7 +148,7 @@ <string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"只限 Wi-Fi"</string> <!-- no translation found for crossSimFormat_spn (9125246077491634262) --> <skip /> - <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"「<xliff:g id="SPN">%s</xliff:g>」備用通話網路"</string> + <string name="crossSimFormat_spn_cross_sim_calling" msgid="5620807020002879057">"「<xliff:g id="SPN">%s</xliff:g>」備用通話"</string> <string name="cfTemplateNotForwarded" msgid="862202427794270501">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:尚未轉接"</string> <string name="cfTemplateForwarded" msgid="9132506315842157860">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> <string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> 於 <xliff:g id="TIME_DELAY">{2}</xliff:g> 秒後轉接"</string> @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此裝置沒有指紋感應器。"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"請使用您的指紋繼續"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指紋圖示"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"無法再識別臉孔。請再試一次。"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"臉孔位置太相近,請改變您的姿勢。"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"減少頭部左右轉動幅度。"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"減少頭部上下轉動幅度。"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"減少頭部左右轉動幅度。"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"移除遮住您臉孔的任何東西。"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"請清理螢幕頂部,包括黑色列"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允許應用程式讀取和寫入「請勿騷擾」設定。"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"開始查看權限使用情況"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允許應用程式開始查看應用程式的權限使用情況 (一般應用程式並不需要)。"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"以高取樣率存取感應器資料"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"允許應用程式以大於 200 Hz 的頻率對感應器資料進行取樣"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"設定密碼規則"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"控制螢幕鎖定密碼和 PIN 所允許的長度和字元。"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"監控螢幕解鎖嘗試次數"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用快速鍵"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"色彩反轉"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量鍵。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 已開啟。"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量鍵。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 已關閉。"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"㩒住兩個音量鍵 3 秒就可以用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS 要求已變更為視像通話"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS 要求已變更為 USSD 要求"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"已變更為新的 SS 要求"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"仿冒詐騙警示"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"工作設定檔"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"已提醒"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"展開"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"最大化"</string> <string name="close_button_text" msgid="10603510034455258">"關閉"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>:<xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"接聽"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"拒接"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"掛斷"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"來電"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"通話中"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"正在過濾來電"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other">已選取 <xliff:g id="COUNT_1">%1$d</xliff:g> 個項目</item> <item quantity="one">已選取 <xliff:g id="COUNT_0">%1$d</xliff:g> 個項目</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"如要繼續,<b><xliff:g id="APP">%s</xliff:g></b> 需要裝置的相機存取權。"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"開啟"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"感應器私隱"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 6facceb39df7..949257234918 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"這個裝置沒有指紋感應器。"</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"使用指紋完成驗證才能繼續操作"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指紋圖示"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"已無法辨識臉孔,請再試一次。"</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"與先前的姿勢太相似,請換一個姿勢。"</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"請將你的頭部稍微向左或向右轉動。"</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"請將你的頭部稍微向上或向下傾斜。"</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"請將你的頭部稍微向左或向右旋轉。"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"請移除任何會遮住臉孔的物體。"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"請清理螢幕頂端,包括黑色橫列"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"允許應用程式讀取及寫入「零打擾」設定。"</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"啟動檢視權限用途"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"允許應用程式開始使用其他應用程式 (一般應用程式並不需要)。"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"以高取樣率存取感應器資料"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"允許應用程式以高於 200 Hz 的頻率對感應器資料進行取樣"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"設定密碼規則"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"管理螢幕鎖定密碼和 PIN 碼支援的字元和長度上限。"</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"監控螢幕解鎖嘗試次數"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"使用捷徑"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"色彩反轉"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"色彩校正"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量鍵。「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」已開啟。"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量鍵。「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」已關閉。"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"同時按住調低及調高音量鍵三秒即可使用「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS 要求已變更為視訊通話"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS 要求已變更為 USSD 要求"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"已變更為新的 SS 要求"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"網路詐騙警示"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"工作資料夾"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"已提醒"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"展開"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"最大化"</string> <string name="close_button_text" msgid="10603510034455258">"關閉"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>:<xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"接聽"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"拒接"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"掛斷"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"來電"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"通話中"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"正在過濾來電"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="other">已選取 <xliff:g id="COUNT_1">%1$d</xliff:g> 個項目</item> <item quantity="one">已選取 <xliff:g id="COUNT_0">%1$d</xliff:g> 個項目</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"如要繼續操作,請將裝置的相機存取權授予「<xliff:g id="APP">%s</xliff:g>」<b></b>。"</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"開啟"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"感應器隱私權"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 3b22e9435462..e5a026bd6fa5 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -579,6 +579,7 @@ <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Le divayisi ayinayo inzwa yezigxivizo zeminwe."</string> <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Inzwa ikhutshazwe okwesikhashana."</string> <string name="fingerprint_name_template" msgid="8941662088160289778">"Umunwe ongu-<xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Sebenzisa izigxivizo zakho zeminwe ukuze uqhubeke"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Isithonjana sezigxivizo zeminwe"</string> @@ -605,7 +606,8 @@ <string name="face_acquired_too_different" msgid="4699657338753282542">"Ayisakwazi ukubona ubuso. Zama futhi."</string> <string name="face_acquired_too_similar" msgid="7684650785108399370">"Kufana kakhulu, sicela ushintshe ukuma kwakho."</string> <string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Jikisa ikhanda lakho kancane."</string> - <string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Jikisa ikhanda lakho kancane."</string> + <!-- no translation found for face_acquired_tilt_too_extreme (8618210742620248049) --> + <skip /> <string name="face_acquired_roll_too_extreme" msgid="1442830503572636825">"Jikisa ikhanda lakho kancane."</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"Susa noma yini efihle ubuso bakho."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Hlanza okuphezulu kwesikrini sakho, kufaka phakathi ibha emnyama"</string> @@ -684,6 +686,8 @@ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Ivumela izinhlelo zokusebenza ukufunda nokubhala ukulungiswa kokuthi Ungaphazamisi."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"qala ukusetshenziswa kokubuka imvume"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Ivumela umphathi ukuthi aqale ukusetshenziswa kwemvume kohlelo lokusebenza. Akumele idingelwe izinhlelo zokusebenza ezijwayelekile."</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"finyelela idatha yenzwa ngenani eliphezulu lokwenza isampuli"</string> + <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Ivumela uhlelo lokusebenza lusampule idatha yenzwa ngenani elikhulu kuno-200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Misa imithetho yephasiwedi"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Lawula ubude nezinhlamvu ezivunyelwe kumaphasiwedi wokukhiya isikrini nama-PIN."</string> <string name="policylab_watchLogin" msgid="7599669460083719504">"Qapha imizamo yokuvula isikrini sakho"</string> @@ -1664,6 +1668,8 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Sebenzisa isinqamuleli"</string> <string name="color_inversion_feature_name" msgid="326050048927789012">"Ukuguqulwa kombala"</string> <string name="color_correction_feature_name" msgid="3655077237805422597">"Ukulungiswa kombala"</string> + <!-- no translation found for reduce_bright_colors_feature_name (8978255324027479398) --> + <skip /> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivuliwe."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivaliwe."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Cindezela uphinde ubambe bobabili okhiye bevolumu ngamasekhondi amathathu ukuze usebenzise i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> @@ -1867,6 +1873,7 @@ <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"Isicelo se-SS sishintshele kukholi yevidiyo"</string> <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"Isicelo se-SS sishintshele kusicelo se-USSD"</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"Ishintshele kusicelo esisha se-SS"</string> + <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Isexwayiso sobugebengu bokweba imininingwane ebucayi"</string> <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Iphrofayela yomsebenzi"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"Kuxwayisiwe"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Nweba"</string> @@ -1880,6 +1887,12 @@ <string name="maximize_button_text" msgid="4258922519914732645">"Khulisa"</string> <string name="close_button_text" msgid="10603510034455258">"Vala"</string> <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string> + <string name="call_notification_answer_action" msgid="5999246836247132937">"Phendula"</string> + <string name="call_notification_decline_action" msgid="3700345945214000726">"Yenqaba"</string> + <string name="call_notification_hang_up_action" msgid="9130720590159188131">"Vala Ucingo"</string> + <string name="call_notification_incoming_text" msgid="6143109825406638201">"Ikholi engenayo"</string> + <string name="call_notification_ongoing_text" msgid="3880832933933020875">"Ikholi eqhubekayo"</string> + <string name="call_notification_screening_text" msgid="8396931408268940208">"Ukuveza ikholi engenayo"</string> <plurals name="selected_count" formatted="false" msgid="3946212171128200491"> <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> okukhethiwe</item> <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> okukhethiwe</item> @@ -2208,4 +2221,8 @@ <string name="sensor_privacy_start_use_camera_notification_content" msgid="4738005643315863736">"Ukuze uqhubeke, <b>i-<xliff:g id="APP">%s</xliff:g></b> idinga ukufinyelela ikhamera yakho."</string> <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7921147002346108119">"Vula"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Ubumfihlo Benzwa"</string> + <!-- no translation found for splash_screen_view_icon_description (180638751260598187) --> + <skip /> + <!-- no translation found for splash_screen_view_branding_description (7911129347402728216) --> + <skip /> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 69bb20cf9c1e..100983bcef0d 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4117,6 +4117,94 @@ user's time zone. Please refer to {@link java.util.TimeZone} for more information about time zone ids. --> <attr name="timeZone" format="string"/> + <!-- Tint to apply to the dial graphic. --> + <attr name="dialTint" format="color" /> + <!-- Blending mode used to apply the dial graphic tint. --> + <attr name="dialTintMode"> + <!-- The tint is drawn on top of the drawable. + [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] --> + <enum name="src_over" value="3" /> + <!-- The tint is masked by the alpha channel of the drawable. The drawable’s + color channels are thrown out. [Sa * Da, Sc * Da] --> + <enum name="src_in" value="5" /> + <!-- The tint is drawn above the drawable, but with the drawable’s alpha + channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] --> + <enum name="src_atop" value="9" /> + <!-- Multiplies the color and alpha channels of the drawable with those of + the tint. [Sa * Da, Sc * Dc] --> + <enum name="multiply" value="14" /> + <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] --> + <enum name="screen" value="15" /> + <!-- Combines the tint and drawable color and alpha channels, clamping the + result to valid color values. Saturate(S + D) --> + <enum name="add" value="16" /> + </attr> + <!-- Tint to apply to the hour hand graphic. --> + <attr name="hand_hourTint" format="color" /> + <!-- Blending mode used to apply the hour hand graphic tint. --> + <attr name="hand_hourTintMode"> + <!-- The tint is drawn on top of the drawable. + [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] --> + <enum name="src_over" value="3" /> + <!-- The tint is masked by the alpha channel of the drawable. The drawable’s + color channels are thrown out. [Sa * Da, Sc * Da] --> + <enum name="src_in" value="5" /> + <!-- The tint is drawn above the drawable, but with the drawable’s alpha + channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] --> + <enum name="src_atop" value="9" /> + <!-- Multiplies the color and alpha channels of the drawable with those of + the tint. [Sa * Da, Sc * Dc] --> + <enum name="multiply" value="14" /> + <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] --> + <enum name="screen" value="15" /> + <!-- Combines the tint and drawable color and alpha channels, clamping the + result to valid color values. Saturate(S + D) --> + <enum name="add" value="16" /> + </attr> + <!-- Tint to apply to the minute hand graphic. --> + <attr name="hand_minuteTint" format="color" /> + <!-- Blending mode used to apply the minute hand graphic tint. --> + <attr name="hand_minuteTintMode"> + <!-- The tint is drawn on top of the drawable. + [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] --> + <enum name="src_over" value="3" /> + <!-- The tint is masked by the alpha channel of the drawable. The drawable’s + color channels are thrown out. [Sa * Da, Sc * Da] --> + <enum name="src_in" value="5" /> + <!-- The tint is drawn above the drawable, but with the drawable’s alpha + channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] --> + <enum name="src_atop" value="9" /> + <!-- Multiplies the color and alpha channels of the drawable with those of + the tint. [Sa * Da, Sc * Dc] --> + <enum name="multiply" value="14" /> + <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] --> + <enum name="screen" value="15" /> + <!-- Combines the tint and drawable color and alpha channels, clamping the + result to valid color values. Saturate(S + D) --> + <enum name="add" value="16" /> + </attr> + <!-- Tint to apply to the second hand graphic. --> + <attr name="hand_secondTint" format="color" /> + <!-- Blending mode used to apply the second hand graphic tint. --> + <attr name="hand_secondTintMode"> + <!-- The tint is drawn on top of the drawable. + [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] --> + <enum name="src_over" value="3" /> + <!-- The tint is masked by the alpha channel of the drawable. The drawable’s + color channels are thrown out. [Sa * Da, Sc * Da] --> + <enum name="src_in" value="5" /> + <!-- The tint is drawn above the drawable, but with the drawable’s alpha + channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] --> + <enum name="src_atop" value="9" /> + <!-- Multiplies the color and alpha channels of the drawable with those of + the tint. [Sa * Da, Sc * Dc] --> + <enum name="multiply" value="14" /> + <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] --> + <enum name="screen" value="15" /> + <!-- Combines the tint and drawable color and alpha channels, clamping the + result to valid color values. Saturate(S + D) --> + <enum name="add" value="16" /> + </attr> </declare-styleable> <declare-styleable name="Button"> </declare-styleable> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 0ae6a76e2a60..45e11ba9820e 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1893,6 +1893,11 @@ <!-- User data will remain unchanged during rollback. --> <enum name="retain" value="2" /> </attr> + + <!-- Applications can set this attribute to an xml resource within their app where they + specified the rules determining which files and directories can be copied from the device + as part of backup or transfer operations. --> + <attr name="dataExtractionRules" format="reference"/> </declare-styleable> <!-- An attribution is a logical part of an app and is identified by a tag. diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 59c260cecfbf..d79c01faaa36 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -143,6 +143,10 @@ <color name="notification_default_color_dark">@color/primary_text_default_material_light</color> <color name="notification_default_color_light">#a3202124</color> + <color name="notification_primary_text_color_current">@color/notification_primary_text_color_light</color> + <color name="notification_secondary_text_color_current">@color/notification_secondary_text_color_light</color> + <color name="notification_default_color_current">@color/notification_default_color_light</color> + <color name="notification_default_color">#757575</color> <!-- Gray 600 --> <color name="notification_action_button_text_color">@color/notification_default_color</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index faa2157be302..12cb3980f785 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1173,6 +1173,9 @@ <!-- Default value for LED off time when the battery is low on charge in miliseconds --> <integer name="config_notificationsBatteryLedOff">2875</integer> + <!-- If true, only colorized CallStyle notifications will apply custom colors --> + <bool name="config_callNotificationActionColorsRequireColorized">true</bool> + <!-- Number of notifications to keep in the notification service historical archive --> <integer name="config_notificationServiceArchiveSize">100</integer> @@ -1950,6 +1953,8 @@ <string name="config_systemShell" translatable="false">com.android.shell</string> <!-- The name of the package that will hold the system contacts role. --> <string name="config_systemContacts" translatable="false">com.android.contacts</string> + <!-- The name of the package that will hold the speech recognizer role by default. --> + <string name="config_systemSpeechRecognizer" translatable="false"></string> <!-- The name of the package that will be allowed to change its components' label/icon. --> <string name="config_overrideComponentUiPackage" translatable="false"></string> @@ -3205,6 +3210,10 @@ panning to scaling the magnification viewport. --> <item name="config_screen_magnification_scaling_threshold" format="float" type="dimen">0.3</item> + <!-- Whether to support magnification area. If not enabled, it would hide the entry in + magnification settings and adjust the default magnification capability. --> + <bool name="config_magnification_area">true</bool> + <!-- If true, the display will be shifted around in ambient mode. --> <bool name="config_enableBurnInProtection">false</bool> @@ -4588,11 +4597,12 @@ <item>com.android.systemui</item> </string-array> - <!-- Package name of custom media key dispatcher class used by MediaSessionService. --> + <!-- Component name of custom media key dispatcher class used by MediaSessionService. --> <string name="config_customMediaKeyDispatcher"></string> - <!-- Package name of custom session policy provider class used by MediaSessionService. --> - <string name="config_customSessionPolicyProvider"></string> + <!-- Component name of custom media session policy provider class used by + MediaSessionService. --> + <string name="config_customMediaSessionPolicyProvider"></string> <!-- The max scale for the wallpaper when it's zoomed in --> <item name="config_wallpaperMaxScale" format="float" type="dimen">1.10</item> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 706641985e20..c2b6b99dcc1c 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -30,7 +30,13 @@ will be displayed in the app launcher and elsewhere. --> <dimen name="app_icon_size">48dip</dimen> - <dimen name="toast_y_offset">24dp</dimen> + <!-- Offset from the bottom of the device a toast shows --> + <dimen name="toast_y_offset">48dp</dimen> + <!-- Max width of a toast --> + <dimen name="toast_width">300dp</dimen> + <!-- Text size of the message within a toast --> + <dimen name="toast_text_size">14sp</dimen> + <!-- Height of the status bar --> <dimen name="status_bar_height">@dimen/status_bar_height_portrait</dimen> <!-- Height of the status bar in portrait. The height should be @@ -265,11 +271,8 @@ <!-- The top margin before the notification progress bar. --> <dimen name="notification_progress_margin_top">8dp</dimen> - <!-- height of the notification header when the notification is alone (minimized / groups) --> - <dimen name="notification_header_solo_height">48dp</dimen> - - <!-- height of the notification header when in a "big" layout --> - <dimen name="notification_header_big_height">56dp</dimen> + <!-- height of the notification header --> + <dimen name="notification_header_height">56dp</dimen> <!-- The height of the background for a notification header on a group --> <dimen name="notification_header_background_height">49.5dp</dimen> @@ -316,26 +319,22 @@ <!-- The top padding for the notification expand button. --> <dimen name="notification_expand_button_padding_top">1dp</dimen> - <!-- minimum vertical margin for the headerless notification content, when cap = 60dp --> - <dimen name="notification_headerless_margin_minimum">8dp</dimen> - - <!-- extra vertical margin for the headerless notification content, when cap = 60dp --> - <dimen name="notification_headerless_margin_extra">10dp</dimen> + <!-- Vertical margin for the headerless notification content, when content has 1 line --> + <!-- 16 * 2 (margins) + 24 (1 line) = 56 (notification) --> + <dimen name="notification_headerless_margin_oneline">16dp</dimen> - <!-- minimum vertical margin for the headerless notification content, when cap = 48dp --> - <dimen name="notification_headerless_margin_constrained_minimum">14dp</dimen> - - <!-- extra vertical margin for the headerless notification content, when cap = 48dp --> - <dimen name="notification_headerless_margin_constrained_extra">4dp</dimen> + <!-- Vertical margin for the headerless notification content, when content has 2 lines --> + <!-- 20 * 2 (margins) + 24 * 2 (2 lines) = 88 (notification) --> + <dimen name="notification_headerless_margin_twoline">20dp</dimen> <!-- The height of each of the 1 or 2 lines in the headerless notification template --> - <dimen name="notification_headerless_line_height">20sp</dimen> + <dimen name="notification_headerless_line_height">24dp</dimen> <!-- vertical margin for the headerless notification content --> <dimen name="notification_headerless_min_height">56dp</dimen> <!-- Height of a small notification in the status bar --> - <dimen name="notification_min_height">76dp</dimen> + <dimen name="notification_min_height">88dp</dimen> <!-- The width of the big icons in notifications. --> <dimen name="notification_large_icon_width">64dp</dimen> @@ -359,7 +358,7 @@ <dimen name="media_notification_expanded_image_margin_bottom">20dp</dimen> <!-- The absolute height for the header in a media notification. --> - <dimen name="media_notification_header_height">@dimen/notification_header_big_height</dimen> + <dimen name="media_notification_header_height">@dimen/notification_header_height</dimen> <!-- The margin of the content to an image--> <dimen name="notification_content_image_margin_end">8dp</dimen> @@ -496,14 +495,20 @@ <!-- The padding on top of inbox style elements --> <dimen name="notification_inbox_item_top_padding">5dp</dimen> + <!-- Size of the verification icon for call notifications --> + <dimen name="notification_verification_icon_size">@dimen/notification_badge_size</dimen> + <!-- Size of the feedback indicator for notifications --> <dimen name="notification_feedback_size">20dp</dimen> + <!-- Size of the phishing alert for notifications --> + <dimen name="notification_phishing_alert_size">@dimen/notification_badge_size</dimen> + <!-- Size of the profile badge for notifications --> <dimen name="notification_badge_size">12dp</dimen> <!-- Size of the alerted icon for notifications --> - <dimen name="notification_alerted_size">12dp</dimen> + <dimen name="notification_alerted_size">@dimen/notification_badge_size</dimen> <!-- Keyguard dimensions --> <!-- TEMP --> @@ -729,10 +734,11 @@ <!-- The maximum width of a image in a media notification. The images will be reduced to that width in case they are bigger.--> <dimen name="notification_media_image_max_width">280dp</dimen> <!-- The size of the right icon --> - <dimen name="notification_right_icon_size">52dp</dimen> + <dimen name="notification_right_icon_size">48dp</dimen> <!-- The top and bottom margin of the right icon in the normal notification states --> - <dimen name="notification_right_icon_headerless_margin">12dp</dimen> + <dimen name="notification_right_icon_headerless_margin">20dp</dimen> <!-- The top margin of the right icon in the "big" notification states --> + <!-- TODO(b/181048615): Move the large icon below the expander in big states --> <dimen name="notification_right_icon_big_margin_top">16dp</dimen> <!-- The size of the left icon --> <dimen name="notification_left_icon_size">@dimen/notification_icon_circle_size</dimen> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index ab4e0f3b608e..0f0ac56f1e51 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -145,6 +145,7 @@ <item type="id" name="remote_input_tag" /> <item type="id" name="pending_intent_tag" /> + <item type="id" name="remote_checked_change_listener_tag" /> <item type="id" name="cross_task_transition" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 22dce9b9935c..2004d0a8d15c 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3076,6 +3076,15 @@ <public name="maxResizeHeight" /> <public name="targetCellWidth" /> <public name="targetCellHeight" /> + <public name="dialTint"/> + <public name="dialTintMode"/> + <public name="hand_hourTint"/> + <public name="hand_hourTintMode"/> + <public name="hand_minuteTint"/> + <public name="hand_minuteTintMode"/> + <public name="hand_secondTint"/> + <public name="hand_secondTintMode"/> + <public name="dataExtractionRules"/> </public-group> <public-group type="drawable" first-id="0x010800b5"> @@ -3153,6 +3162,12 @@ <public name="config_systemShell" /> <!-- @hide @SystemApi --> <public name="config_systemContacts" /> + <!-- @hide @SystemApi --> + <public name="config_customMediaKeyDispatcher" /> + <!-- @hide @SystemApi --> + <public name="config_customMediaSessionPolicyProvider" /> + <!-- @hide @SystemApi --> + <public name="config_systemSpeechRecognizer" /> </public-group> <public-group type="id" first-id="0x01020055"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 9ebe23af2c59..71ba44b0ded1 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1644,7 +1644,7 @@ <!-- Message shown during acqusition when the user's face is turned too far left or right [CHAR LIMIT=50] --> <string name="face_acquired_pan_too_extreme">Turn your head a little less.</string> <!-- Message shown during acqusition when the user's face is tilted too high or too low [CHAR LIMIT=50] --> - <string name="face_acquired_tilt_too_extreme">Turn your head a little less.</string> + <string name="face_acquired_tilt_too_extreme">Tilt your head a little less.</string> <!-- Message shown during acquisiton when the user's face is tilted too far left or right [CHAR LIMIT=50] --> <string name="face_acquired_roll_too_extreme">Turn your head a little less.</string> <!-- Message shown during acquisition when the user's face is obscured [CHAR LIMIT=50] --> @@ -4538,7 +4538,7 @@ <string name="color_correction_feature_name">Color Correction</string> <!-- Title of Reduce Brightness feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] --> - <string name="reduce_bright_colors_feature_name">Reduce Brightness</string> + <string name="reduce_bright_colors_feature_name">Reduce brightness</string> <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] --> <string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string> @@ -5002,12 +5002,18 @@ <string name="stk_cc_ss_to_ussd">SS request changed to USSD request</string> <string name="stk_cc_ss_to_ss">Changed to new SS request</string> + <!-- Content description of the phishing alert icon in the notification. [CHAR_LIMIT=NONE] --> + <string name="notification_phishing_alert_content_description">Phishing alert</string> + <!-- Content description of the work profile icon in the notification. --> <string name="notification_work_profile_content_description">Work profile</string> <!-- Content description of the alerting icon in the notification. [CHAR_LIMIT=NONE] --> <string name="notification_alerted_content_description">Alerted</string> + <!-- Default content description of the verification icon in the call notification. [CHAR_LIMIT=NONE] --> + <string name="notification_verified_content_description">Verified</string> + <!-- Content description of the expand button icon in the notification when collaped.--> <string name="expand_button_content_description_collapsed">Expand</string> @@ -5846,4 +5852,8 @@ ul.</string> <!--- Label for notification channel for all sensor privacy related notifications. [CHAR LIMIT=NONE] --> <string name="sensor_privacy_notification_channel_label">Sensor Privacy</string> + <!-- Content description for the icon on the splash screen. [CHAR LIMIT=50] --> + <string name="splash_screen_view_icon_description">Application icon</string> + <!-- Content description for the branding image on the splash screen. [CHAR LIMIT=50] --> + <string name="splash_screen_view_branding_description">Application branding image</string> </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 24afe07b57cf..c7ded0cfa3a2 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -964,8 +964,9 @@ please see styles_device_defaults.xml. </style> <style name="TextAppearance.Toast"> - <item name="fontFamily">sans-serif</item> + <item name="fontFamily">@*android:string/config_headlineFontFamily</item> <item name="textSize">14sp</item> + <item name="textColor">?android:attr/textColorPrimary</item> </style> <style name="TextAppearance.Tooltip"> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 158289a39d5c..3c4a5d4aab73 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -466,18 +466,20 @@ please see styles_device_defaults.xml. </style> <style name="TextAppearance.Material.Notification"> - <item name="textColor">@color/notification_secondary_text_color_light</item> + <item name="textColor">@color/notification_secondary_text_color_current</item> <item name="textSize">@dimen/notification_text_size</item> </style> <style name="TextAppearance.Material.Notification.Reply" /> <style name="TextAppearance.Material.Notification.Title"> + <item name="textColor">@color/notification_primary_text_color_current</item> <item name="fontFamily">sans-serif-medium</item> <item name="textSize">@dimen/notification_title_text_size</item> </style> <style name="TextAppearance.Material.Notification.BigTitle"> + <item name="textColor">@color/notification_primary_text_color_current</item> <item name="fontFamily">sans-serif-medium</item> <item name="textSize">@dimen/notification_big_title_text_size</item> </style> @@ -492,9 +494,8 @@ please see styles_device_defaults.xml. <style name="TextAppearance.Material.Notification.Time" parent="TextAppearance.Material.Notification.Info" /> - <style name="TextAppearance.Material.Notification.Emphasis"> - <item name="textColor">#66000000</item> - </style> + <!-- unused; keep identical to parent --> + <style name="TextAppearance.Material.Notification.Emphasis"/> <style name="TextAppearance.Material.Notification.Conversation.AppName" parent="TextAppearance.Material.Notification.Title"> <item name="android:textSize">16sp</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f66d33b02b69..dbb584dfe293 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -233,6 +233,7 @@ <java-symbol type="id" name="pin_confirm_text" /> <java-symbol type="id" name="pin_error_message" /> <java-symbol type="id" name="timePickerLayout" /> + <java-symbol type="id" name="phishing_alert" /> <java-symbol type="id" name="profile_badge" /> <java-symbol type="id" name="alerted_icon" /> <java-symbol type="id" name="transitionPosition" /> @@ -2285,6 +2286,7 @@ <java-symbol type="string" name="config_wifi_tether_enable" /> <java-symbol type="bool" name="config_intrusiveNotificationLed" /> <java-symbol type="bool" name="config_notificationBadging" /> + <java-symbol type="bool" name="config_callNotificationActionColorsRequireColorized" /> <java-symbol type="dimen" name="preference_fragment_padding_bottom" /> <java-symbol type="dimen" name="preference_fragment_padding_side" /> <java-symbol type="drawable" name="expander_ic_maximized" /> @@ -2814,6 +2816,7 @@ <java-symbol type="id" name="smart_reply_container" /> <java-symbol type="id" name="remote_input_tag" /> <java-symbol type="id" name="pending_intent_tag" /> + <java-symbol type="id" name="remote_checked_change_listener_tag" /> <java-symbol type="id" name="notification_action_index_tag" /> <java-symbol type="attr" name="seekBarDialogPreferenceStyle" /> @@ -2892,8 +2895,6 @@ <java-symbol type="id" name="alternate_expand_target" /> <java-symbol type="id" name="notification_header" /> <java-symbol type="id" name="notification_top_line" /> - <java-symbol type="id" name="notification_headerless_margin_extra_top" /> - <java-symbol type="id" name="notification_headerless_margin_extra_bottom" /> <java-symbol type="id" name="time_divider" /> <java-symbol type="id" name="header_text_divider" /> <java-symbol type="id" name="header_text_secondary_divider" /> @@ -2915,8 +2916,8 @@ <java-symbol type="dimen" name="notification_header_icon_size" /> <java-symbol type="dimen" name="notification_header_app_name_margin_start" /> <java-symbol type="dimen" name="notification_header_separating_margin" /> - <java-symbol type="dimen" name="notification_headerless_margin_constrained_minimum" /> - <java-symbol type="dimen" name="notification_headerless_margin_constrained_extra" /> + <java-symbol type="dimen" name="notification_headerless_margin_oneline" /> + <java-symbol type="dimen" name="notification_headerless_margin_twoline" /> <java-symbol type="string" name="default_notification_channel_label" /> <java-symbol type="string" name="importance_from_user" /> <java-symbol type="string" name="importance_from_person" /> @@ -3109,8 +3110,10 @@ <java-symbol type="dimen" name="call_notification_collapsible_indent"/> <java-symbol type="drawable" name="ic_call_answer" /> <java-symbol type="drawable" name="ic_call_decline" /> + <java-symbol type="id" name="verification_divider" /> <java-symbol type="id" name="verification_icon" /> <java-symbol type="id" name="verification_text" /> + <java-symbol type="string" name="notification_verified_content_description" /> <!-- Notification handler / dashboard package --> <java-symbol type="string" name="config_notificationHandlerPackage" /> @@ -4113,8 +4116,6 @@ <java-symbol type="string" name="notification_history_title_placeholder" /> - <java-symbol type="string" name="config_customMediaKeyDispatcher" /> - <java-symbol type="string" name="config_customSessionPolicyProvider" /> <!-- The max scale for the wallpaper when it's zoomed in --> <java-symbol type="dimen" name="config_wallpaperMaxScale"/> @@ -4174,6 +4175,8 @@ <java-symbol type="string" name="turn_on_magnification_settings_action" /> <java-symbol type="string" name="dismiss_action" /> + <java-symbol type="bool" name="config_magnification_area" /> + <java-symbol type="bool" name="config_trackerAppNeedsPermissions"/> <!-- Package with global data query permissions for AppSearch --> @@ -4201,4 +4204,6 @@ <java-symbol type="bool" name="config_telephony5gNonStandalone" /> <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" /> + + <java-symbol type="bool" name="config_enableOneHandedKeyguard" /> </resources> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index e7e049da18c9..16d720b891e2 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -238,6 +238,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -271,6 +273,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -306,6 +310,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -340,6 +346,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -417,6 +425,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -449,6 +459,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -482,6 +494,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -531,6 +545,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -565,6 +581,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -597,6 +615,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -631,6 +651,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -664,6 +686,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -697,6 +721,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -730,6 +756,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -763,6 +791,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -800,6 +830,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -834,6 +866,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -865,6 +899,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -1069,6 +1105,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1101,6 +1139,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1134,6 +1174,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1169,6 +1211,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1203,6 +1247,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1281,6 +1327,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1316,6 +1364,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1352,6 +1402,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1426,6 +1478,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1463,6 +1517,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1498,6 +1554,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1532,6 +1590,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1565,6 +1625,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1598,6 +1660,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1629,6 +1693,8 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1750,6 +1816,8 @@ easier. <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item> @@ -1782,6 +1850,8 @@ easier. <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1824,6 +1894,8 @@ easier. <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> @@ -1859,6 +1931,8 @@ easier. <item name="colorSecondary">@color/secondary_device_default_settings</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> + <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="textColorPrimary">@color/text_color_primary_device_default_light</item> <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item> <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item> diff --git a/core/sysprop/Android.bp b/core/sysprop/Android.bp index 237ede2006ea..f89099e5630d 100644 --- a/core/sysprop/Android.bp +++ b/core/sysprop/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + sysprop_library { name: "com.android.sysprop.localization", srcs: ["LocalizationProperties.sysprop"], diff --git a/core/tests/BroadcastRadioTests/Android.bp b/core/tests/BroadcastRadioTests/Android.bp index b8efd198aebe..113f45dfef55 100644 --- a/core/tests/BroadcastRadioTests/Android.bp +++ b/core/tests/BroadcastRadioTests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "BroadcastRadioTests", privileged: true, diff --git a/core/tests/ConnectivityManagerTest/Android.bp b/core/tests/ConnectivityManagerTest/Android.bp index a33d219f53d2..beaf17615f1d 100644 --- a/core/tests/ConnectivityManagerTest/Android.bp +++ b/core/tests/ConnectivityManagerTest/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ConnectivityManagerTest", libs: [ diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp index e1787762e067..2789e9f316d1 100644 --- a/core/tests/GameManagerTests/Android.bp +++ b/core/tests/GameManagerTests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "FrameworksCoreGameManagerTests", // Include all test java files diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp index 055249d47cb3..c112cbb47b55 100644 --- a/core/tests/PackageInstallerSessions/Android.bp +++ b/core/tests/PackageInstallerSessions/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "FrameworksCorePackageInstallerSessionsTests", diff --git a/core/tests/PlatformCompatFramework/Android.bp b/core/tests/PlatformCompatFramework/Android.bp index 33802650fa12..95e23ad396af 100644 --- a/core/tests/PlatformCompatFramework/Android.bp +++ b/core/tests/PlatformCompatFramework/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "PlatformCompatFrameworkTests", // Include all test java files. diff --git a/core/tests/bandwidthtests/Android.bp b/core/tests/bandwidthtests/Android.bp index 5d881b8be68c..f1ecd45073eb 100644 --- a/core/tests/bandwidthtests/Android.bp +++ b/core/tests/bandwidthtests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "BandwidthTests", // Include all test java files. diff --git a/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp b/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp index ade97b81e775..1c0ea839ec02 100644 --- a/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp +++ b/core/tests/batterystatstests/BatteryStatsLoadTests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "BatteryStatsLoadTests", srcs: ["src/**/*.java"], diff --git a/core/tests/batterystatstests/BatteryStatsViewer/Android.bp b/core/tests/batterystatstests/BatteryStatsViewer/Android.bp index 1e0498be5800..c2e7d81cb283 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/Android.bp +++ b/core/tests/batterystatstests/BatteryStatsViewer/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "BatteryStatsViewer", srcs: ["src/**/*.java"], diff --git a/core/tests/benchmarks/Android.bp b/core/tests/benchmarks/Android.bp index 8dd7928e6fc2..4cd546753dbf 100644 --- a/core/tests/benchmarks/Android.bp +++ b/core/tests/benchmarks/Android.bp @@ -16,6 +16,15 @@ // build framework base core benchmarks // ============================================================ +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: "frameworks-base-core-benchmarks", installable: true, diff --git a/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java b/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java new file mode 100644 index 000000000000..1cf430205627 --- /dev/null +++ b/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java @@ -0,0 +1,133 @@ +/* + * 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; + +import android.annotation.SuppressLint; +import android.graphics.Point; +import android.graphics.Rect; +import android.util.MergedConfiguration; +import android.view.InsetsSource; +import android.view.InsetsState; + +import com.google.caliper.AfterExperiment; +import com.google.caliper.BeforeExperiment; + +/** + * Benchmark of read/write large Parcelable class. This also shows the performance of different + * implementations for nested Parcelable class: + * <ul> + * <li>Well-written read/writeFromParcel (direct access)</li> + * <li>read/writeTypedObject (object creation + addition int to indicate nullity)</li> + * <li>read/writeParcelable (object creation + addition type String)</li> + * </ul> + */ +public class ParcelableBenchmark { + private Parcel mParcel; + + @BeforeExperiment + protected void setUp() { + mParcel = Parcel.obtain(); + } + + @AfterExperiment + protected void tearDown() { + mParcel.recycle(); + mParcel = null; + } + + public void timeReadWriteMergedConfiguration(int reps) { + final MergedConfiguration mergedConfiguration = new MergedConfiguration(); + for (int i = 0; i < reps; i++) { + mergedConfiguration.writeToParcel(mParcel, 0); + mParcel.setDataPosition(0); + mergedConfiguration.readFromParcel(mParcel); + } + } + + public void timeReadWriteInsetsState(int reps) { + final InsetsState insetsState = new InsetsState(); + for (int i = 0; i < InsetsState.SIZE; i++) { + insetsState.addSource(new InsetsSource(i)); + } + for (int i = 0; i < reps; i++) { + insetsState.writeToParcel(mParcel, 0); + mParcel.setDataPosition(0); + insetsState.readFromParcel(mParcel); + } + } + + public void timeReadWritePointArray(int reps) { + final PointArray pointArray = new PointArray(); + for (int i = 0; i < reps; i++) { + pointArray.writeToParcel(mParcel, 0); + mParcel.setDataPosition(0); + pointArray.readFromParcel(mParcel); + } + } + + public void timeReadWritePointArrayFast(int reps) { + final PointArrayFast pointArray = new PointArrayFast(); + for (int i = 0; i < reps; i++) { + pointArray.writeToParcel(mParcel, 0); + mParcel.setDataPosition(0); + pointArray.readFromParcel(mParcel); + } + } + + @SuppressLint("ParcelCreator") + private static class PointArray implements Parcelable { + Rect mBounds = new Rect(); + Point[] mPoints = new Point[10]; + { + for (int i = 0; i < mPoints.length; i++) { + mPoints[i] = new Point(); + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mBounds, flags); + dest.writeParcelableArray(mPoints, flags); + } + + void readFromParcel(Parcel in) { + mBounds = in.readParcelable(Rect.class.getClassLoader()); + mPoints = in.readParcelableArray(Point.class.getClassLoader(), Point.class); + } + + @Override + public int describeContents() { + return 0; + } + } + + @SuppressLint("ParcelCreator") + private static class PointArrayFast extends PointArray { + + @Override + public void writeToParcel(Parcel dest, int flags) { + mBounds.writeToParcel(dest, flags); + dest.writeTypedArray(mPoints, flags); + } + + @Override + void readFromParcel(Parcel in) { + mBounds.readFromParcel(in); + in.readTypedArray(mPoints, Point.CREATOR); + } + } +} diff --git a/core/tests/bluetoothtests/Android.bp b/core/tests/bluetoothtests/Android.bp index 4b6f9db33afc..a2e4dff8f826 100644 --- a/core/tests/bluetoothtests/Android.bp +++ b/core/tests/bluetoothtests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "BluetoothTests", // Include all test java files. diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp index e42b4b4ae2bf..f87797a90c90 100644 --- a/core/tests/bugreports/Android.bp +++ b/core/tests/bugreports/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "BugreportManagerTestCases", srcs: ["src/**/*.java"], diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index e2b975ff78ac..fbabf4ae1200 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "FrameworksCoreTests", diff --git a/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp b/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp index 25e4fc366124..b47c4702eafe 100644 --- a/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp +++ b/core/tests/coretests/BinderDeathRecipientHelperApp/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "BinderDeathRecipientHelperApp1", diff --git a/core/tests/coretests/BinderProxyCountingTestApp/Android.bp b/core/tests/coretests/BinderProxyCountingTestApp/Android.bp index 6279a4873c67..b1df8dbbee19 100644 --- a/core/tests/coretests/BinderProxyCountingTestApp/Android.bp +++ b/core/tests/coretests/BinderProxyCountingTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "BinderProxyCountingTestApp", diff --git a/core/tests/coretests/BinderProxyCountingTestService/Android.bp b/core/tests/coretests/BinderProxyCountingTestService/Android.bp index 22718cb86d66..52c23a15e4e3 100644 --- a/core/tests/coretests/BinderProxyCountingTestService/Android.bp +++ b/core/tests/coretests/BinderProxyCountingTestService/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "BinderProxyCountingTestService", diff --git a/core/tests/coretests/BstatsTestApp/Android.bp b/core/tests/coretests/BstatsTestApp/Android.bp index a89d72830686..c82da9e7b449 100644 --- a/core/tests/coretests/BstatsTestApp/Android.bp +++ b/core/tests/coretests/BstatsTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "BstatsTestApp", diff --git a/core/tests/coretests/DisabledTestApp/Android.bp b/core/tests/coretests/DisabledTestApp/Android.bp index 419816e42eff..3ab321827e53 100644 --- a/core/tests/coretests/DisabledTestApp/Android.bp +++ b/core/tests/coretests/DisabledTestApp/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "DisabledTestApp", diff --git a/core/tests/coretests/EnabledTestApp/Android.bp b/core/tests/coretests/EnabledTestApp/Android.bp index bc4f4bd2e4d7..750b578a0253 100644 --- a/core/tests/coretests/EnabledTestApp/Android.bp +++ b/core/tests/coretests/EnabledTestApp/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "EnabledTestApp", diff --git a/core/tests/coretests/aidl/Android.bp b/core/tests/coretests/aidl/Android.bp index 6e442db78500..2d848cca4618 100644 --- a/core/tests/coretests/aidl/Android.bp +++ b/core/tests/coretests/aidl/Android.bp @@ -12,6 +12,15 @@ // 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_test { name: "coretests-aidl", sdk_version: "current", diff --git a/core/tests/coretests/apks/Android.bp b/core/tests/coretests/apks/Android.bp index 20c87b2d2ce9..eda875acfdb1 100644 --- a/core/tests/coretests/apks/Android.bp +++ b/core/tests/coretests/apks/Android.bp @@ -1,3 +1,12 @@ +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_defaults { name: "FrameworksCoreTests_apks_defaults", sdk_version: "current", diff --git a/core/tests/coretests/apks/install/Android.bp b/core/tests/coretests/apks/install/Android.bp index e783fe2ec2dc..652b49130433 100644 --- a/core/tests/coretests/apks/install/Android.bp +++ b/core/tests/coretests/apks/install/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/install_bad_dex/Android.bp b/core/tests/coretests/apks/install_bad_dex/Android.bp index d156793cf22a..7b96c9b47553 100644 --- a/core/tests/coretests/apks/install_bad_dex/Android.bp +++ b/core/tests/coretests/apks/install_bad_dex/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install_bad_dex_", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/install_complete_package_info/Android.bp b/core/tests/coretests/apks/install_complete_package_info/Android.bp index 123558bda076..3fee0c6e7f7c 100644 --- a/core/tests/coretests/apks/install_complete_package_info/Android.bp +++ b/core/tests/coretests/apks/install_complete_package_info/Android.bp @@ -1,7 +1,15 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install_complete_package_info", defaults: ["FrameworksCoreTests_apks_defaults"], srcs: ["**/*.java"], } - diff --git a/core/tests/coretests/apks/install_decl_perm/Android.bp b/core/tests/coretests/apks/install_decl_perm/Android.bp index 868e8b51bd49..bf1f0de9672a 100644 --- a/core/tests/coretests/apks/install_decl_perm/Android.bp +++ b/core/tests/coretests/apks/install_decl_perm/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install_decl_perm", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/install_jni_lib/Android.bp b/core/tests/coretests/apks/install_jni_lib/Android.bp index df19fa09adcf..d315e7b70cb9 100644 --- a/core/tests/coretests/apks/install_jni_lib/Android.bp +++ b/core/tests/coretests/apks/install_jni_lib/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + cc_test_library { name: "libframeworks_coretests_jni", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp index 602b704a0a5d..20cc96ebc5c0 100644 --- a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp +++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install_jni_lib_open_from_apk", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/install_loc_auto/Android.bp b/core/tests/coretests/apks/install_loc_auto/Android.bp index 6393915ba283..37daf7608a43 100644 --- a/core/tests/coretests/apks/install_loc_auto/Android.bp +++ b/core/tests/coretests/apks/install_loc_auto/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install_loc_auto", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/install_loc_internal/Android.bp b/core/tests/coretests/apks/install_loc_internal/Android.bp index 770aaa511b9d..3e233132b58d 100644 --- a/core/tests/coretests/apks/install_loc_internal/Android.bp +++ b/core/tests/coretests/apks/install_loc_internal/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install_loc_internal", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/install_loc_sdcard/Android.bp b/core/tests/coretests/apks/install_loc_sdcard/Android.bp index 177940102e83..708e655e07db 100644 --- a/core/tests/coretests/apks/install_loc_sdcard/Android.bp +++ b/core/tests/coretests/apks/install_loc_sdcard/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install_loc_sdcard", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/install_loc_unspecified/Android.bp b/core/tests/coretests/apks/install_loc_unspecified/Android.bp index 21c0f82b44ff..76869e9b9ed5 100644 --- a/core/tests/coretests/apks/install_loc_unspecified/Android.bp +++ b/core/tests/coretests/apks/install_loc_unspecified/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install_loc_unspecified", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/install_use_perm_good/Android.bp b/core/tests/coretests/apks/install_use_perm_good/Android.bp index bb41ebb2fdca..89700ddb94be 100644 --- a/core/tests/coretests/apks/install_use_perm_good/Android.bp +++ b/core/tests/coretests/apks/install_use_perm_good/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install_use_perm_good", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/install_uses_feature/Android.bp b/core/tests/coretests/apks/install_uses_feature/Android.bp index 0ec747b0f151..913a96a1cd27 100644 --- a/core/tests/coretests/apks/install_uses_feature/Android.bp +++ b/core/tests/coretests/apks/install_uses_feature/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install_uses_feature", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/install_verifier_bad/Android.bp b/core/tests/coretests/apks/install_verifier_bad/Android.bp index 1265739a3107..ed13d74789bf 100644 --- a/core/tests/coretests/apks/install_verifier_bad/Android.bp +++ b/core/tests/coretests/apks/install_verifier_bad/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install_verifier_bad", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/install_verifier_good/Android.bp b/core/tests/coretests/apks/install_verifier_good/Android.bp index 4911ffbd2020..fe9a24f6bd7b 100644 --- a/core/tests/coretests/apks/install_verifier_good/Android.bp +++ b/core/tests/coretests/apks/install_verifier_good/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_install_verifier_good", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/keyset/Android.bp b/core/tests/coretests/apks/keyset/Android.bp index e252b081341a..93c3b1f60327 100644 --- a/core/tests/coretests/apks/keyset/Android.bp +++ b/core/tests/coretests/apks/keyset/Android.bp @@ -1,4 +1,13 @@ //apks signed by keyset_A +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_keyset_sa_unone", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/locales/Android.bp b/core/tests/coretests/apks/locales/Android.bp index 4a730ef53404..4fe6c7fd09b2 100644 --- a/core/tests/coretests/apks/locales/Android.bp +++ b/core/tests/coretests/apks/locales/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_locales", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/overlay_config/Android.bp b/core/tests/coretests/apks/overlay_config/Android.bp index 957355726fe8..9c971fd2f1bc 100644 --- a/core/tests/coretests/apks/overlay_config/Android.bp +++ b/core/tests/coretests/apks/overlay_config/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_overlay_config", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/version/Android.bp b/core/tests/coretests/apks/version/Android.bp index 371ccfc301cb..8b750f7b5ee4 100644 --- a/core/tests/coretests/apks/version/Android.bp +++ b/core/tests/coretests/apks/version/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_version_1", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/apks/version_nosys/Android.bp b/core/tests/coretests/apks/version_nosys/Android.bp index 575667822393..de40f49849b7 100644 --- a/core/tests/coretests/apks/version_nosys/Android.bp +++ b/core/tests/coretests/apks/version_nosys/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksCoreTests_version_1_nosys", defaults: ["FrameworksCoreTests_apks_defaults"], diff --git a/core/tests/coretests/certs/Android.bp b/core/tests/coretests/certs/Android.bp index bd5c8293f6f6..8411183c3335 100644 --- a/core/tests/coretests/certs/Android.bp +++ b/core/tests/coretests/certs/Android.bp @@ -1,3 +1,14 @@ +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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], +} + android_app_certificate { name: "FrameworksCoreTests_keyset_A_cert", certificate: "keyset_A", diff --git a/core/tests/coretests/jni/Android.bp b/core/tests/coretests/jni/Android.bp index bb090e2a3408..edac8ef25fe7 100644 --- a/core/tests/coretests/jni/Android.bp +++ b/core/tests/coretests/jni/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + cc_test_library { name: "libpowermanagertest_jni", srcs: [ diff --git a/core/tests/coretests/src/android/app/backup/BackupAgentTest.java b/core/tests/coretests/src/android/app/backup/BackupAgentTest.java index ea903f2b61eb..37cf4700c1d0 100644 --- a/core/tests/coretests/src/android/app/backup/BackupAgentTest.java +++ b/core/tests/coretests/src/android/app/backup/BackupAgentTest.java @@ -57,14 +57,6 @@ public class BackupAgentTest { } @Test - public void testGetIncludeExcludeRules_isMigration_returnsEmptyRules() throws Exception { - mBackupAgent = getAgentForOperationType(OperationType.MIGRATION); - - IncludeExcludeRules rules = mBackupAgent.getIncludeExcludeRules(mBackupScheme); - assertThat(rules).isEqualTo(IncludeExcludeRules.emptyRules()); - } - - @Test public void testGetIncludeExcludeRules_isNotMigration_returnsRules() throws Exception { PathWithRequiredFlags path = new PathWithRequiredFlags("path", /* requiredFlags */ 0); Map<String, Set<PathWithRequiredFlags>> includePaths = Collections.singletonMap("test", diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java index 1573c19b89a3..7cb680499d98 100644 --- a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java +++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java @@ -37,6 +37,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Parcel; +import android.os.UserHandle; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -124,13 +125,13 @@ public class PeopleSpaceTileTest { } @Test - public void testUid() { + public void testUserHandle() { PeopleSpaceTile tile = new PeopleSpaceTile .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) - .setUid(42) + .setUserHandle(new UserHandle(0)) .build(); - assertThat(tile.getUid()).isEqualTo(42); + assertThat(tile.getUserHandle()).isEqualTo(new UserHandle(0)); } @Test @@ -227,7 +228,7 @@ public class PeopleSpaceTileTest { .setUserName("name") .setUserIcon(mIcon) .setContactUri(Uri.parse("contact")) - .setUid(42) + .setUserHandle(new UserHandle(1)) .setPackageName("package.name") .setLastInteractionTimestamp(7L) .setIsImportantConversation(true) @@ -246,7 +247,7 @@ public class PeopleSpaceTileTest { assertThat(readTile.getUserName()).isEqualTo(tile.getUserName()); assertThat(readTile.getUserIcon().toString()).isEqualTo(tile.getUserIcon().toString()); assertThat(readTile.getContactUri()).isEqualTo(tile.getContactUri()); - assertThat(readTile.getUid()).isEqualTo(tile.getUid()); + assertThat(readTile.getUserHandle()).isEqualTo(tile.getUserHandle()); assertThat(readTile.getPackageName()).isEqualTo(tile.getPackageName()); assertThat(readTile.getLastInteractionTimestamp()).isEqualTo( tile.getLastInteractionTimestamp()); diff --git a/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java b/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java index 266ff3dd09f6..1c6b3cc47a45 100644 --- a/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java +++ b/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java @@ -33,17 +33,20 @@ public class ExternalTimeSuggestionTest { @Test public void testEquals() { - ExternalTimeSuggestion one = new ExternalTimeSuggestion(ARBITRARY_TIME); + ExternalTimeSuggestion one = new ExternalTimeSuggestion( + ARBITRARY_TIME.getReferenceTimeMillis(), + ARBITRARY_TIME.getValue()); assertEquals(one, one); - ExternalTimeSuggestion two = new ExternalTimeSuggestion(ARBITRARY_TIME); + ExternalTimeSuggestion two = new ExternalTimeSuggestion( + ARBITRARY_TIME.getReferenceTimeMillis(), + ARBITRARY_TIME.getValue()); assertEquals(one, two); assertEquals(two, one); - TimestampedValue<Long> differentTime = new TimestampedValue<>( + ExternalTimeSuggestion three = new ExternalTimeSuggestion( ARBITRARY_TIME.getReferenceTimeMillis() + 1, ARBITRARY_TIME.getValue()); - ExternalTimeSuggestion three = new ExternalTimeSuggestion(differentTime); assertNotEquals(one, three); assertNotEquals(three, one); @@ -55,7 +58,9 @@ public class ExternalTimeSuggestionTest { @Test public void testParcelable() { - ExternalTimeSuggestion suggestion = new ExternalTimeSuggestion(ARBITRARY_TIME); + ExternalTimeSuggestion suggestion = new ExternalTimeSuggestion( + ARBITRARY_TIME.getReferenceTimeMillis(), + ARBITRARY_TIME.getValue()); assertRoundTripParcelable(suggestion); // DebugInfo should also be stored (but is not checked by equals()) diff --git a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java index da92e69b6378..ffe93bc99cf5 100644 --- a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java +++ b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java @@ -47,15 +47,16 @@ public class AppSearchShortcutInfoTest { final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW); final ShortcutInfo shortcut = new AppSearchShortcutInfo.Builder(id) .setActivity(activity) - .setText(id) + .setLongLabel(id) .setIconResName(shortcutIconResName) .setIntent(shortcutIntent) .setPerson(person) .setCategories(categorySet) .setFlags(ShortcutInfo.FLAG_LONG_LIVED) .build() - .toShortcutInfo(); + .toShortcutInfo(0); + assertThat(shortcut.getUserId()).isEqualTo(0); assertThat(shortcut.getId()).isEqualTo(id); assertThat(shortcut.getShortLabel()).isEqualTo(id); assertThat(shortcut.getIconResName()).isEqualTo(shortcutIconResName); diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java new file mode 100644 index 000000000000..412b36713fa2 --- /dev/null +++ b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java @@ -0,0 +1,227 @@ +/* + * 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.hardware.input; + +import static android.hardware.lights.LightsRequest.Builder; + +import static com.google.common.truth.Truth.assertThat; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNotNull; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.lights.Light; +import android.hardware.lights.LightState; +import android.hardware.lights.LightsManager; +import android.os.IBinder; +import android.platform.test.annotations.Presubmit; +import android.util.ArrayMap; +import android.view.InputDevice; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Tests for {@link InputDeviceLightsManager}. + * + * Build/Install/Run: + * atest FrameworksCoreTests:InputDeviceLightsManagerTest + */ +@Presubmit +@RunWith(MockitoJUnitRunner.class) +public class InputDeviceLightsManagerTest { + private static final String TAG = "InputDeviceLightsManagerTest"; + + private static final int DEVICE_ID = 1000; + private static final int PLAYER_ID = 3; + + @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + + private InputManager mInputManager; + + @Mock private IInputManager mIInputManagerMock; + + @Before + public void setUp() throws Exception { + when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID}); + + when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn( + createInputDevice(DEVICE_ID)); + + mInputManager = InputManager.resetInstance(mIInputManagerMock); + + ArrayMap<Integer, LightState> lightStatesById = new ArrayMap<>(); + doAnswer(invocation -> { + final int[] lightIds = (int[]) invocation.getArguments()[1]; + final LightState[] lightStates = + (LightState[]) invocation.getArguments()[2]; + for (int i = 0; i < lightIds.length; i++) { + lightStatesById.put(lightIds[i], lightStates[i]); + } + return null; + }).when(mIInputManagerMock).setLightStates(eq(DEVICE_ID), + any(int[].class), any(LightState[].class), any(IBinder.class)); + + doAnswer(invocation -> { + int lightId = (int) invocation.getArguments()[1]; + if (lightStatesById.containsKey(lightId)) { + return lightStatesById.get(lightId); + } + return new LightState(0); + }).when(mIInputManagerMock).getLightState(eq(DEVICE_ID), anyInt()); + } + + @After + public void tearDown() { + InputManager.clearInstance(); + } + + private InputDevice createInputDevice(int id) { + return new InputDevice(id, 0 /* generation */, 0 /* controllerNumber */, "name", + 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */, + 0 /* sources */, 0 /* keyboardType */, null /* keyCharacterMap */, + false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */, + false /* hasSensor */, false /* hasBattery */); + } + + private void mockLights(Light[] lights) throws Exception { + // Mock the Lights returned form InputManagerService + when(mIInputManagerMock.getLights(eq(DEVICE_ID))).thenReturn( + new ArrayList(Arrays.asList(lights))); + } + + @Test + public void testGetInputDeviceLights() throws Exception { + InputDevice device = mInputManager.getInputDevice(DEVICE_ID); + assertNotNull(device); + + Light[] mockedLights = { + new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_SINGLE), + new Light(2 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB), + new Light(3 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_PLAYER_ID) + }; + mockLights(mockedLights); + + LightsManager lightsManager = device.getLightsManager(); + List<Light> lights = lightsManager.getLights(); + verify(mIInputManagerMock).getLights(eq(DEVICE_ID)); + assertEquals(lights, Arrays.asList(mockedLights)); + } + + @Test + public void testControlMultipleLights() throws Exception { + InputDevice device = mInputManager.getInputDevice(DEVICE_ID); + assertNotNull(device); + + Light[] mockedLights = { + new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB), + new Light(2 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB), + new Light(3 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB), + new Light(4 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB) + }; + mockLights(mockedLights); + + LightsManager lightsManager = device.getLightsManager(); + List<Light> lightList = lightsManager.getLights(); + LightState[] states = new LightState[]{new LightState(0xf1), new LightState(0xf2), + new LightState(0xf3)}; + // Open a session to request turn 3/4 lights on: + LightsManager.LightsSession session = lightsManager.openSession(); + session.requestLights(new Builder() + .addLight(lightsManager.getLights().get(0), states[0]) + .addLight(lightsManager.getLights().get(1), states[1]) + .addLight(lightsManager.getLights().get(2), states[2]) + .build()); + IBinder token = session.getToken(); + + verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID), + any(String.class), eq(token)); + verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}), + eq(states), eq(token)); + + // Then all 3 should turn on. + assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getColor()) + .isEqualTo(0xf1); + assertThat(lightsManager.getLightState(lightsManager.getLights().get(1)).getColor()) + .isEqualTo(0xf2); + assertThat(lightsManager.getLightState(lightsManager.getLights().get(2)).getColor()) + .isEqualTo(0xf3); + + // And the 4th should remain off. + assertThat(lightsManager.getLightState(lightsManager.getLights().get(3)).getColor()) + .isEqualTo(0x00); + + // close session + session.close(); + verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token)); + } + + @Test + public void testControlPlayerIdLight() throws Exception { + InputDevice device = mInputManager.getInputDevice(DEVICE_ID); + assertNotNull(device); + + Light[] mockedLights = { + new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_PLAYER_ID), + new Light(2 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_SINGLE), + new Light(3 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_RGB), + }; + mockLights(mockedLights); + + LightsManager lightsManager = device.getLightsManager(); + List<Light> lightList = lightsManager.getLights(); + LightState[] states = new LightState[]{new LightState(0xf1, PLAYER_ID)}; + // Open a session to request set Player ID light: + LightsManager.LightsSession session = lightsManager.openSession(); + session.requestLights(new Builder() + .addLight(lightsManager.getLights().get(0), states[0]) + .build()); + IBinder token = session.getToken(); + + verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID), + any(String.class), eq(token)); + verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1}), + eq(states), eq(token)); + + // Verify the light state + assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getColor()) + .isEqualTo(0xf1); + assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getPlayerId()) + .isEqualTo(PLAYER_ID); + + // close session + session.close(); + verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token)); + } +} diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java index 1d56e179317f..d555cd90d907 100644 --- a/core/tests/coretests/src/android/os/VibrationEffectTest.java +++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java @@ -220,6 +220,10 @@ public class VibrationEffectTest { restored = scaledDown.scale(1.25f); // Does not restore to the exact original value because scale up is a bit offset. assertEquals(101, restored.getAmplitude(), AMPLITUDE_SCALE_TOLERANCE); + + // Does not go below min amplitude while scaling down. + VibrationEffect.OneShot minAmplitude = new VibrationEffect.OneShot(TEST_TIMING, 1); + assertEquals(1, minAmplitude.scale(0.5f).getAmplitude()); } @Test @@ -245,6 +249,15 @@ public class VibrationEffectTest { } @Test + public void testResolveOneshotFailsWhenAmplitudeNonPositive() { + try { + TEST_ONE_SHOT.resolve(0); + fail("Amplitude is set to zero, should throw IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + } + + @Test public void testScaleWaveform() { VibrationEffect.Waveform initial = (VibrationEffect.Waveform) TEST_WAVEFORM; @@ -255,6 +268,10 @@ public class VibrationEffectTest { assertEquals(216, scaled.getAmplitudes()[0], AMPLITUDE_SCALE_TOLERANCE); assertEquals(0, scaled.getAmplitudes()[1]); assertEquals(-1, scaled.getAmplitudes()[2]); + + VibrationEffect.Waveform minAmplitude = new VibrationEffect.Waveform( + new long[]{100}, new int[] {1}, -1); + assertArrayEquals(new int[]{1}, minAmplitude.scale(0.5f).getAmplitudes()); } @Test diff --git a/core/tests/coretests/src/android/util/RotationUtilsTest.java b/core/tests/coretests/src/android/util/RotationUtilsTest.java new file mode 100644 index 000000000000..5dbe03e8b42b --- /dev/null +++ b/core/tests/coretests/src/android/util/RotationUtilsTest.java @@ -0,0 +1,61 @@ +/* + * 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.util; + +import static android.util.RotationUtils.rotateBounds; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import static org.junit.Assert.assertEquals; + +import android.graphics.Rect; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link RotationUtils}. + * + * Build/Install/Run: + * atest FrameworksCoreTests:RotationUtilsTest + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class RotationUtilsTest { + + @Test + public void testRotateBounds() { + Rect testParent = new Rect(0, 0, 1000, 600); + Rect testInner = new Rect(40, 20, 120, 80); + + Rect testResult = new Rect(testInner); + rotateBounds(testResult, testParent, ROTATION_90); + assertEquals(new Rect(20, 880, 80, 960), testResult); + + testResult.set(testInner); + rotateBounds(testResult, testParent, ROTATION_180); + assertEquals(new Rect(880, 520, 960, 580), testResult); + + testResult.set(testInner); + rotateBounds(testResult, testParent, ROTATION_270); + assertEquals(new Rect(520, 40, 580, 120), testResult); + } +} diff --git a/core/tests/coretests/src/android/view/BlurAggregatorTest.java b/core/tests/coretests/src/android/view/BlurAggregatorTest.java new file mode 100644 index 000000000000..b01f2755efdd --- /dev/null +++ b/core/tests/coretests/src/android/view/BlurAggregatorTest.java @@ -0,0 +1,318 @@ +/* + * 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.view; + +import static androidx.test.InstrumentationRegistry.getInstrumentation; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.assertNull; +import static junit.framework.TestCase.assertTrue; + +import android.content.Context; +import android.platform.test.annotations.Presubmit; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.internal.graphics.drawable.BackgroundBlurDrawable; +import com.android.internal.graphics.drawable.BackgroundBlurDrawable.Aggregator; +import com.android.internal.graphics.drawable.BackgroundBlurDrawable.BlurRegion; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@RunWith(AndroidJUnit4.class) +public class BlurAggregatorTest { + private static final int TEST_BLUR_RADIUS = 30; + private static final int TEST_FRAME_NUMBER = 1; + + private Context mContext; + + private Aggregator mAggregator; + private BackgroundBlurDrawable mDrawable; + + private ViewRootImpl mViewRoot; + + @Before + public void setUp() { + mContext = getInstrumentation().getTargetContext(); + getInstrumentation().runOnMainSync(() -> { + mViewRoot = new ViewRootImpl(mContext, mContext.getDisplayNoVerify()); + }); + mAggregator = new Aggregator(mViewRoot); + mDrawable = createTestBackgroundBlurDrawable(); + } + + private BackgroundBlurDrawable createTestBackgroundBlurDrawable() { + final BackgroundBlurDrawable drawable = mAggregator.createBackgroundBlurDrawable(mContext); + drawable.setBlurRadius(TEST_BLUR_RADIUS); + final boolean hasUpdates = mAggregator.hasUpdates(); + final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + mAggregator.getBlurRegionsToDispatchToSf(TEST_FRAME_NUMBER, blurRegions, hasUpdates); + return drawable; + } + + @Test + public void testBlurRadiusUpdatePropagatesToRenderThreadIfNeeded() { + mDrawable.setBlurRadius(TEST_BLUR_RADIUS); + assertFalse(mAggregator.hasUpdates()); + + mDrawable.setBlurRadius(0); + assertTrue(mAggregator.hasUpdates()); + BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(0, blurRegions.length); + assertFalse(mAggregator.hasUpdates()); + + mDrawable.setBlurRadius(TEST_BLUR_RADIUS); + assertTrue(mAggregator.hasUpdates()); + blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(1, blurRegions.length); + assertEquals(TEST_BLUR_RADIUS, blurRegions[0].blurRadius); + assertFalse(mAggregator.hasUpdates()); + + } + + @Test + public void testAlphaUpdatePropagatesToRenderThreadIfNeeded() { + mDrawable.setAlpha(20); + assertTrue(mAggregator.hasUpdates()); + BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(1, blurRegions.length); + assertEquals(20 / 255f, blurRegions[0].alpha); + assertFalse(mAggregator.hasUpdates()); + + mDrawable.setAlpha(20); + assertFalse(mAggregator.hasUpdates()); + + mDrawable.setAlpha(0); + assertTrue(mAggregator.hasUpdates()); + blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(0, blurRegions.length); + assertFalse(mAggregator.hasUpdates()); + } + + @Test + public void testCornerRadiusUpdatePropagatesToRenderThreadIfNeeded() { + mDrawable.setCornerRadius(1f, 2f, 3f, 4f); + assertTrue(mAggregator.hasUpdates()); + final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(1, blurRegions.length); + assertEquals(1f, blurRegions[0].cornerRadiusTL); + assertEquals(2f, blurRegions[0].cornerRadiusTR); + assertEquals(3f, blurRegions[0].cornerRadiusBL); + assertEquals(4f, blurRegions[0].cornerRadiusBR); + assertFalse(mAggregator.hasUpdates()); + } + + @Test + public void testVisibleUpdatePropagatesToRenderThreadIfNeeded() { + mDrawable.setVisible(false, /* restart= */false); + assertTrue(mAggregator.hasUpdates()); + BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(0, blurRegions.length); + assertFalse(mAggregator.hasUpdates()); + + mDrawable.setVisible(true, /* restart= */ false); + assertTrue(mAggregator.hasUpdates()); + blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(1, blurRegions.length); + assertEquals(TEST_BLUR_RADIUS, blurRegions[0].blurRadius); + assertFalse(mAggregator.hasUpdates()); + } + + @Test + public void testBlurRegionCopyForRtIsSameIfNoUiUpdates() { + mDrawable.setBlurRadius(30); + BlurRegion[] blurRegions1 = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(1, blurRegions1.length); + assertEquals(30, blurRegions1[0].blurRadius); + + BlurRegion[] blurRegions2 = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(blurRegions1, blurRegions2); + } + + @Test + public void testPositionUpdateAppearsInBlurRegion() { + BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(1, blurRegions.length); + + mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4); + mAggregator.getBlurRegionsToDispatchToSf(TEST_FRAME_NUMBER, blurRegions, + mAggregator.hasUpdates()); + assertEquals(1, blurRegions[0].rect.left); + assertEquals(2, blurRegions[0].rect.top); + assertEquals(3, blurRegions[0].rect.right); + assertEquals(4, blurRegions[0].rect.bottom); + } + + @Test + public void testNoBlurRegionsDispatchedWhenNoUpdates() { + final boolean hasUpdates = mAggregator.hasUpdates(); + assertFalse(hasUpdates); + final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(1, blurRegions.length); + + float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf( + TEST_FRAME_NUMBER, blurRegions, hasUpdates); + assertNull(blurRegionsForSf); + } + + @Test + public void testBlurRegionDispatchedIfOnlyDrawableUpdated() { + mDrawable.setBlurRadius(50); + final boolean hasUpdates = mAggregator.hasUpdates(); + assertTrue(hasUpdates); + final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(1, blurRegions.length); + + float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf( + TEST_FRAME_NUMBER, blurRegions, hasUpdates); + assertNotNull(blurRegionsForSf); + assertEquals(1, blurRegionsForSf.length); + assertEquals(50f, blurRegionsForSf[0][0]); + } + + @Test + public void testBlurRegionDispatchedIfOnlyPositionUpdated() { + final boolean hasUpdates = mAggregator.hasUpdates(); + assertFalse(hasUpdates); + final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(1, blurRegions.length); + + mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4); + float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf( + TEST_FRAME_NUMBER, blurRegions, hasUpdates); + assertNotNull(blurRegionsForSf); + assertEquals(1, blurRegionsForSf.length); + assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSf[0][0]); + assertEquals(1f, blurRegionsForSf[0][2]); + assertEquals(2f, blurRegionsForSf[0][3]); + assertEquals(3f, blurRegionsForSf[0][4]); + assertEquals(4f, blurRegionsForSf[0][5]); + } + + @Test + public void testPositionUpdateIsAppliedInNextFrameIfMissed() { + final boolean hasUpdates = mAggregator.hasUpdates(); + assertFalse(hasUpdates); + final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(1, blurRegions.length); + + mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4); + float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf( + TEST_FRAME_NUMBER + 1, blurRegions, hasUpdates); + assertNotNull(blurRegionsForSf); + assertEquals(1, blurRegionsForSf.length); + assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSf[0][0]); + assertEquals(1f, blurRegionsForSf[0][2]); + assertEquals(2f, blurRegionsForSf[0][3]); + assertEquals(3f, blurRegionsForSf[0][4]); + assertEquals(4f, blurRegionsForSf[0][5]); + } + + @Test + public void testMultipleDrawablesDispatchedToSfIfOneIsUpdated() { + final BackgroundBlurDrawable drawable2 = createTestBackgroundBlurDrawable(); + drawable2.setBlurRadius(50); + final boolean hasUpdates = mAggregator.hasUpdates(); + assertTrue(hasUpdates); + final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(2, blurRegions.length); + + // Check that an update in one of the drawables triggers a dispatch of all blur regions + float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf( + TEST_FRAME_NUMBER, blurRegions, hasUpdates); + assertNotNull(blurRegionsForSf); + assertEquals(2, blurRegionsForSf.length); + + // Check that the Aggregator deleted all position updates for frame TEST_FRAME_NUMBER + blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf( + TEST_FRAME_NUMBER, blurRegions, /* hasUiUpdates= */ false); + assertNull(blurRegionsForSf); + + // Check that a position update triggers a dispatch of all blur regions + drawable2.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4); + blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf( + TEST_FRAME_NUMBER + 1, blurRegions, hasUpdates); + assertNotNull(blurRegionsForSf); + assertEquals(2, blurRegionsForSf.length); + } + + @Test + public void testUiThreadUpdatesDoNotChangeStateOnRenderThread() { + // Updates for frame N + mDrawable.setBlurRadius(50); + mDrawable.setCornerRadius(1, 2, 3, 4); + mDrawable.setAlpha(20); + + final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + assertEquals(1, blurRegions.length); + assertEquals(50, blurRegions[0].blurRadius); + assertEquals(20 / 255f, blurRegions[0].alpha); + assertEquals(1f, blurRegions[0].cornerRadiusTL); + assertEquals(2f, blurRegions[0].cornerRadiusTR); + assertEquals(3f, blurRegions[0].cornerRadiusBL); + assertEquals(4f, blurRegions[0].cornerRadiusBR); + + // Updates for frame N+1 + mDrawable.setBlurRadius(60); + mDrawable.setCornerRadius(10, 20, 30, 40); + mDrawable.setAlpha(40); + + // Assert state for frame N is untouched + assertEquals(50, blurRegions[0].blurRadius); + assertEquals(20 / 255f, blurRegions[0].alpha); + assertEquals(1f, blurRegions[0].cornerRadiusTL); + assertEquals(2f, blurRegions[0].cornerRadiusTR); + assertEquals(3f, blurRegions[0].cornerRadiusBL); + assertEquals(4f, blurRegions[0].cornerRadiusBR); + } + + @Test + public void testPositionUpdatesForFutureFramesAreNotAppliedForCurrentFrame() { + final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT(); + + mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4); + mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER + 1, 5, 6, 7, 8); + + final float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf( + TEST_FRAME_NUMBER, blurRegions, /* hasUiUpdates= */ false); + assertNotNull(blurRegionsForSf); + assertEquals(1, blurRegionsForSf.length); + // Assert state for first frame is not affected by update for second frame + assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSf[0][0]); + assertEquals(1f, blurRegionsForSf[0][2]); + assertEquals(2f, blurRegionsForSf[0][3]); + assertEquals(3f, blurRegionsForSf[0][4]); + assertEquals(4f, blurRegionsForSf[0][5]); + + final float[][] blurRegionsForSfForNextFrame = mAggregator.getBlurRegionsToDispatchToSf( + TEST_FRAME_NUMBER + 1, blurRegions, /* hasUiUpdates= */ false); + assertNotNull(blurRegionsForSfForNextFrame); + assertEquals(1, blurRegionsForSfForNextFrame.length); + // Assert second frame updates are applied normally + assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSfForNextFrame[0][0]); + assertEquals(5f, blurRegionsForSfForNextFrame[0][2]); + assertEquals(6f, blurRegionsForSfForNextFrame[0][3]); + assertEquals(7f, blurRegionsForSfForNextFrame[0][4]); + assertEquals(8f, blurRegionsForSfForNextFrame[0][5]); + } + +} diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java index c61f33e15b18..2106b4bc5be9 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java @@ -169,6 +169,13 @@ public class InsetsSourceTest { } @Test + public void testCalculateInsetsForIme_noIntersection_horizontal() { + mImeSource.setFrame(new Rect(0, 0, 100, 500)); + Insets insets = mImeSource.calculateInsets(new Rect(100, 0, 500, 500), false); + assertEquals(Insets.NONE, insets); + } + + @Test public void testCalculateInsets_zeroWidthIntersection_horizontal_start() { mSource.setFrame(new Rect(0, 0, 100, 500)); Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 0), false); diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java index 786ae89ac2ae..b3450de80092 100644 --- a/core/tests/coretests/src/android/view/MotionEventTest.java +++ b/core/tests/coretests/src/android/view/MotionEventTest.java @@ -169,4 +169,24 @@ public class MotionEventTest { assertEquals(0x3 << 30, ID_SOURCE_MASK & event.getId()); } } + + @Test + public void testEventRotation() { + final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, + ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */); + MotionEvent rot90 = MotionEvent.obtain(event); + rot90.transform(MotionEvent.createRotateMatrix(/* 90 deg */1, 1000, 600)); + assertEquals(50, (int) rot90.getX()); + assertEquals(570, (int) rot90.getY()); + + MotionEvent rot180 = MotionEvent.obtain(event); + rot180.transform(MotionEvent.createRotateMatrix(/* 180 deg */2, 1000, 600)); + assertEquals(970, (int) rot180.getX()); + assertEquals(550, (int) rot180.getY()); + + MotionEvent rot270 = MotionEvent.obtain(event); + rot270.transform(MotionEvent.createRotateMatrix(/* 270 deg */3, 1000, 600)); + assertEquals(950, (int) rot270.getX()); + assertEquals(30, (int) rot270.getY()); + } } diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS index 5031ff913e6d..80165f065995 100644 --- a/core/tests/coretests/src/android/view/OWNERS +++ b/core/tests/coretests/src/android/view/OWNERS @@ -1,7 +1,7 @@ # Input -per-file *MotionEventTest.* = michaelwr@google.com, svv@google.com -per-file *KeyEventTest.* = michaelwr@google.com, svv@google.com -per-file VelocityTest.java = michaelwr@google.com, svv@google.com +per-file *MotionEventTest.* = file:/services/core/java/com/android/server/input/OWNERS +per-file *KeyEventTest.* = file:/services/core/java/com/android/server/input/OWNERS +per-file VelocityTest.java = file:/services/core/java/com/android/server/input/OWNERS # WindowManager per-file *Display* = file:/services/core/java/com/android/server/wm/OWNERS diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java index b9cf1e4a234c..516fb76eeaf7 100644 --- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java +++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java @@ -16,16 +16,12 @@ package android.view; -import static androidx.test.InstrumentationRegistry.getInstrumentation; import static androidx.test.InstrumentationRegistry.getTargetContext; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +31,7 @@ import static org.mockito.Mockito.when; import android.graphics.Point; import android.graphics.Rect; import android.os.Handler; +import android.os.ICancellationSignal; import androidx.test.runner.AndroidJUnit4; @@ -42,9 +39,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; /** * Tests of {@link ScrollCaptureConnection}. @@ -56,261 +51,138 @@ public class ScrollCaptureConnectionTest { private final Point mPositionInWindow = new Point(1, 2); private final Rect mLocalVisibleRect = new Rect(2, 3, 4, 5); private final Rect mScrollBounds = new Rect(3, 4, 5, 6); + private final TestScrollCaptureCallback mCallback = new TestScrollCaptureCallback(); + + private ScrollCaptureTarget mTarget; + private ScrollCaptureConnection mConnection; private Handler mHandler; - private ScrollCaptureTarget mTarget1; @Mock private Surface mSurface; @Mock - private IScrollCaptureCallbacks mConnectionCallbacks; - @Mock - private View mMockView1; + private IScrollCaptureCallbacks mRemote; @Mock - private ScrollCaptureCallback mCallback1; + private View mView; @Before public void setUp() { MockitoAnnotations.initMocks(this); mHandler = new Handler(getTargetContext().getMainLooper()); + when(mSurface.isValid()).thenReturn(true); + when(mView.getScrollCaptureHint()).thenReturn(View.SCROLL_CAPTURE_HINT_INCLUDE); - when(mMockView1.getHandler()).thenReturn(mHandler); - when(mMockView1.getScrollCaptureHint()).thenReturn(View.SCROLL_CAPTURE_HINT_INCLUDE); - - mTarget1 = new ScrollCaptureTarget( - mMockView1, mLocalVisibleRect, mPositionInWindow, mCallback1); - mTarget1.setScrollBounds(mScrollBounds); - } - - /** Test the DelayedAction timeout helper class works as expected. */ - @Test - public void testDelayedAction() { - Runnable action = Mockito.mock(Runnable.class); - ScrollCaptureConnection.DelayedAction delayed = - new ScrollCaptureConnection.DelayedAction(mHandler, 100, action); - try { - Thread.sleep(200); - } catch (InterruptedException ex) { - /* ignore */ - } - getInstrumentation().waitForIdleSync(); - assertFalse(delayed.cancel()); - assertFalse(delayed.timeoutNow()); - verify(action, times(1)).run(); - } - - /** Test the DelayedAction cancel() */ - @Test - public void testDelayedAction_cancel() { - Runnable action = Mockito.mock(Runnable.class); - ScrollCaptureConnection.DelayedAction delayed = - new ScrollCaptureConnection.DelayedAction(mHandler, 100, action); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - /* ignore */ - } - assertTrue(delayed.cancel()); - assertFalse(delayed.timeoutNow()); - try { - Thread.sleep(200); - } catch (InterruptedException ex) { - /* ignore */ - } - getInstrumentation().waitForIdleSync(); - verify(action, never()).run(); - } - - /** Test the DelayedAction timeoutNow() - for testing only */ - @Test - public void testDelayedAction_timeoutNow() { - Runnable action = Mockito.mock(Runnable.class); - ScrollCaptureConnection.DelayedAction delayed = - new ScrollCaptureConnection.DelayedAction(mHandler, 100, action); - try { - Thread.sleep(50); - } catch (InterruptedException ex) { - /* ignore */ - } - assertTrue(delayed.timeoutNow()); - assertFalse(delayed.cancel()); - getInstrumentation().waitForIdleSync(); - verify(action, times(1)).run(); + mTarget = new ScrollCaptureTarget(mView, mLocalVisibleRect, mPositionInWindow, mCallback); + mTarget.setScrollBounds(mScrollBounds); + mConnection = new ScrollCaptureConnection(Runnable::run, mTarget, mRemote); } /** Test creating a client with valid info */ @Test public void testConstruction() { - new ScrollCaptureConnection(mTarget1, mConnectionCallbacks); + ScrollCaptureTarget target = new ScrollCaptureTarget( + mView, mLocalVisibleRect, mPositionInWindow, mCallback); + target.setScrollBounds(new Rect(1, 2, 3, 4)); + new ScrollCaptureConnection(Runnable::run, target, mRemote); } /** Test creating a client fails if arguments are not valid. */ @Test public void testConstruction_requiresScrollBounds() { try { - mTarget1.setScrollBounds(null); - new ScrollCaptureConnection(mTarget1, mConnectionCallbacks); + mTarget.setScrollBounds(null); + new ScrollCaptureConnection(Runnable::run, mTarget, mRemote); fail("An exception was expected."); } catch (RuntimeException ex) { // Ignore, expected. } } - @SuppressWarnings("SameParameterValue") - private static Answer<Void> runRunnable(int arg) { - return invocation -> { - Runnable r = invocation.getArgument(arg); - r.run(); - return null; - }; - } - - @SuppressWarnings("SameParameterValue") - private static Answer<Void> reportBufferSent(int sessionArg, long frameNum, Rect capturedArea) { - return invocation -> { - ScrollCaptureSession session = invocation.getArgument(sessionArg); - session.notifyBufferSent(frameNum, capturedArea); - return null; - }; - } - /** @see ScrollCaptureConnection#startCapture(Surface) */ @Test public void testStartCapture() throws Exception { - final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, - mConnectionCallbacks); - - // Have the session start accepted immediately - doAnswer(runRunnable(1)).when(mCallback1) - .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class)); - connection.startCapture(mSurface); - getInstrumentation().waitForIdleSync(); - - verify(mCallback1, times(1)) - .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class)); - verify(mConnectionCallbacks, times(1)).onCaptureStarted(); - verifyNoMoreInteractions(mConnectionCallbacks); + mConnection.startCapture(mSurface); + + mCallback.completeStartRequest(); + assertTrue(mConnection.isStarted()); + + verify(mRemote, times(1)).onCaptureStarted(); + verifyNoMoreInteractions(mRemote); } @Test - public void testStartCaptureTimeout() throws Exception { - final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, - mConnectionCallbacks); - connection.startCapture(mSurface); + public void testStartCapture_cancellation() throws Exception { + ICancellationSignal signal = mConnection.startCapture(mSurface); + signal.cancel(); - // Force timeout to fire - connection.getTimeoutAction().timeoutNow(); + mCallback.completeStartRequest(); + assertFalse(mConnection.isStarted()); - getInstrumentation().waitForIdleSync(); - verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class)); - } - - private void startCapture(ScrollCaptureConnection connection) throws Exception { - doAnswer(runRunnable(1)).when(mCallback1) - .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class)); - connection.startCapture(mSurface); - getInstrumentation().waitForIdleSync(); - reset(mCallback1, mConnectionCallbacks); + verifyNoMoreInteractions(mRemote); } /** @see ScrollCaptureConnection#requestImage(Rect) */ @Test public void testRequestImage() throws Exception { - final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, - mConnectionCallbacks); - startCapture(connection); - - // Stub the callback to complete the request immediately - doAnswer(reportBufferSent(/* sessionArg */ 0, /* frameNum */ 1L, new Rect(1, 2, 3, 4))) - .when(mCallback1) - .onScrollCaptureImageRequest(any(ScrollCaptureSession.class), any(Rect.class)); - - // Make the inbound binder call - connection.requestImage(new Rect(1, 2, 3, 4)); + mConnection.startCapture(mSurface); + mCallback.completeStartRequest(); + reset(mRemote); - // Wait for handler thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mCallback1, times(1)).onScrollCaptureImageRequest( - any(ScrollCaptureSession.class), eq(new Rect(1, 2, 3, 4))); + mConnection.requestImage(new Rect(1, 2, 3, 4)); + mCallback.completeImageRequest(new Rect(1, 2, 3, 4)); - // Wait for binder thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mConnectionCallbacks, times(1)) - .onCaptureBufferSent(eq(1L), eq(new Rect(1, 2, 3, 4))); - - verifyNoMoreInteractions(mCallback1, mConnectionCallbacks); + verify(mRemote, times(1)) + .onImageRequestCompleted(eq(0), eq(new Rect(1, 2, 3, 4))); + verifyNoMoreInteractions(mRemote); } @Test - public void testRequestImageTimeout() throws Exception { - final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, - mConnectionCallbacks); - startCapture(connection); - - // Make the inbound binder call - connection.requestImage(new Rect(1, 2, 3, 4)); - - // Wait for handler thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mCallback1, times(1)).onScrollCaptureImageRequest( - any(ScrollCaptureSession.class), eq(new Rect(1, 2, 3, 4))); - - // Force timeout to fire - connection.getTimeoutAction().timeoutNow(); - getInstrumentation().waitForIdleSync(); - - // (callback not stubbed, does nothing) - // Timeout triggers request to end capture - verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class)); - verifyNoMoreInteractions(mCallback1, mConnectionCallbacks); + public void testRequestImage_cancellation() throws Exception { + mConnection.startCapture(mSurface); + mCallback.completeStartRequest(); + reset(mRemote); + + ICancellationSignal signal = mConnection.requestImage(new Rect(1, 2, 3, 4)); + signal.cancel(); + mCallback.completeImageRequest(new Rect(1, 2, 3, 4)); + + verifyNoMoreInteractions(mRemote); } /** @see ScrollCaptureConnection#endCapture() */ @Test public void testEndCapture() throws Exception { - final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, - mConnectionCallbacks); - startCapture(connection); - - // Stub the callback to complete the request immediately - doAnswer(runRunnable(0)) - .when(mCallback1) - .onScrollCaptureEnd(any(Runnable.class)); - - // Make the inbound binder call - connection.endCapture(); + mConnection.startCapture(mSurface); + mCallback.completeStartRequest(); + reset(mRemote); - // Wait for handler thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class)); + mConnection.endCapture(); + mCallback.completeEndRequest(); - // Wait for binder thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mConnectionCallbacks, times(1)).onConnectionClosed(); - - verifyNoMoreInteractions(mCallback1, mConnectionCallbacks); + // And the reply is sent + verify(mRemote, times(1)).onCaptureEnded(); + verifyNoMoreInteractions(mRemote); } + /** @see ScrollCaptureConnection#endCapture() */ @Test - public void testEndCaptureTimeout() throws Exception { - final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, - mConnectionCallbacks); - startCapture(connection); + public void testEndCapture_cancellation() throws Exception { + mConnection.startCapture(mSurface); + mCallback.completeStartRequest(); + reset(mRemote); - // Make the inbound binder call - connection.endCapture(); + ICancellationSignal signal = mConnection.endCapture(); + signal.cancel(); + mCallback.completeEndRequest(); - // Wait for handler thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class)); - - // Force timeout to fire - connection.getTimeoutAction().timeoutNow(); - - // Wait for binder thread dispatch - getInstrumentation().waitForIdleSync(); - verify(mConnectionCallbacks, times(1)).onConnectionClosed(); + verifyNoMoreInteractions(mRemote); + } - verifyNoMoreInteractions(mCallback1, mConnectionCallbacks); + @Test + public void testClose() throws Exception { + mConnection.close(); + assertFalse(mConnection.isConnected()); + verifyNoMoreInteractions(mRemote); } + } diff --git a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java new file mode 100644 index 000000000000..cc229e11dcf2 --- /dev/null +++ b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java @@ -0,0 +1,415 @@ +/* + * 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.view; + +import static androidx.test.InstrumentationRegistry.getTargetContext; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.CancellationSignal; +import android.os.SystemClock; + +import androidx.annotation.NonNull; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * Tests of {@link ScrollCaptureTargetSelector}. + */ +@RunWith(AndroidJUnit4.class) +public class ScrollCaptureSearchResultsTest { + + + private static final Rect EMPTY_RECT = new Rect(); + private static final String TAG = "Test"; + + private final Executor mDirectExec = Runnable::run; + private Executor mBgExec; + + @Before + public void setUp() { + mBgExec = Executors.newSingleThreadExecutor(); + } + + @Test + public void testNoTargets() { + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + assertTrue(results.isComplete()); + + assertNull("Expected null due to empty queue", results.getTopResult()); + } + + @Test + public void testNoValidTargets() { + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + + FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mDirectExec); + callback1.setScrollBounds(EMPTY_RECT); + ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50), + new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + + // Supplies scrollBounds = empty rect + FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mDirectExec); + callback2.setScrollBounds(EMPTY_RECT); + ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50), + new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE); + + results.addTarget(target1); + results.addTarget(target2); + + assertTrue(results.isComplete()); + assertNull("Expected null due to no valid targets", results.getTopResult()); + } + + @Test + public void testSingleTarget() { + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + FakeScrollCaptureCallback callback = new FakeScrollCaptureCallback(mDirectExec); + ScrollCaptureTarget target = createTarget(callback, + new Rect(20, 30, 40, 50), new Point(10, 10), + View.SCROLL_CAPTURE_HINT_AUTO); + callback.setScrollBounds(new Rect(2, 2, 18, 18)); + + results.addTarget(target); + assertTrue(results.isComplete()); + + ScrollCaptureTarget result = results.getTopResult(); + assertSame("Excepted the same target as a result", target, result); + assertEquals("result has wrong scroll bounds", + new Rect(2, 2, 18, 18), result.getScrollBounds()); + } + + @Test + public void testSingleTarget_backgroundThread() throws InterruptedException { + FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mBgExec); + ScrollCaptureTarget target1 = createTarget(callback1, + new Rect(20, 30, 40, 50), new Point(10, 10), + View.SCROLL_CAPTURE_HINT_AUTO); + callback1.setDelay(100); + callback1.setScrollBounds(new Rect(2, 2, 18, 18)); + + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + results.addTarget(target1); + + CountDownLatch latch = new CountDownLatch(1); + results.setOnCompleteListener(latch::countDown); + if (!latch.await(200, TimeUnit.MILLISECONDS)) { + fail("onComplete listener was expected"); + } + + ScrollCaptureTarget result = results.getTopResult(); + assertSame("Excepted the single target1 as a result", target1, result); + assertEquals("Result has wrong scroll bounds", + new Rect(2, 2, 18, 18), result.getScrollBounds()); + } + + @Test + public void testRanking() { + + // 1 - Empty + FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mDirectExec); + callback1.setScrollBounds(EMPTY_RECT); + ViewGroup targetView1 = new FakeView(getTargetContext(), 0, 0, 60, 60, 1); + ScrollCaptureTarget target1 = createTargetWithView(targetView1, callback1, + new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + + // 2 - 10x10 + HINT_INCLUDE + FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mDirectExec); + callback2.setScrollBounds(new Rect(0, 0, 10, 10)); + ViewGroup targetView2 = new FakeView(getTargetContext(), 0, 0, 60, 60, 2); + ScrollCaptureTarget target2 = createTargetWithView(targetView2, callback2, + new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_INCLUDE); + + // 3 - 20x20 + AUTO + FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(mDirectExec); + callback3.setScrollBounds(new Rect(0, 0, 20, 20)); + ViewGroup targetView3 = new FakeView(getTargetContext(), 0, 0, 60, 60, 3); + ScrollCaptureTarget target3 = createTargetWithView(targetView3, callback3, + new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + + // 4 - 30x30 + AUTO + FakeScrollCaptureCallback callback4 = new FakeScrollCaptureCallback(mDirectExec); + callback4.setScrollBounds(new Rect(0, 0, 10, 10)); + ViewGroup targetView4 = new FakeView(getTargetContext(), 0, 0, 60, 60, 4); + ScrollCaptureTarget target4 = createTargetWithView(targetView4, callback4, + new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + + // 5 - 10x10 + child of #4 + FakeScrollCaptureCallback callback5 = new FakeScrollCaptureCallback(mDirectExec); + callback5.setScrollBounds(new Rect(0, 0, 10, 10)); + ViewGroup targetView5 = new FakeView(getTargetContext(), 0, 0, 60, 60, 5); + ScrollCaptureTarget target5 = createTargetWithView(targetView5, callback5, + new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + targetView4.addView(targetView5); + + // 6 - 20x20 + child of #4 + FakeScrollCaptureCallback callback6 = new FakeScrollCaptureCallback(mDirectExec); + callback6.setScrollBounds(new Rect(0, 0, 20, 20)); + ViewGroup targetView6 = new FakeView(getTargetContext(), 0, 0, 60, 60, 6); + ScrollCaptureTarget target6 = createTargetWithView(targetView6, callback6, + new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + targetView4.addView(targetView6); + + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + results.addTarget(target1); + results.addTarget(target2); + results.addTarget(target3); + results.addTarget(target4); + results.addTarget(target5); + results.addTarget(target6); + assertTrue(results.isComplete()); + + // Verify "top" result + assertEquals(target2, results.getTopResult()); + + // Verify priority ("best" first) + assertThat(results.getTargets()) + .containsExactly( + target2, + target6, + target5, + target4, + target3, + target1); + } + + /** + * If a timeout expires, late results are ignored. + */ + @Test + public void testTimeout() { + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + + // callback 1, 10x10, hint=AUTO, responds after 100ms from bg thread + FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mBgExec); + callback1.setScrollBounds(new Rect(5, 5, 15, 15)); + callback1.setDelay(100); + ScrollCaptureTarget target1 = createTarget( + callback1, new Rect(20, 30, 40, 50), new Point(10, 10), + View.SCROLL_CAPTURE_HINT_AUTO); + results.addTarget(target1); + + // callback 2, 20x20, hint=AUTO, responds after 5s from bg thread + FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mBgExec); + callback2.setScrollBounds(new Rect(0, 0, 20, 20)); + callback2.setDelay(1000); + ScrollCaptureTarget target2 = createTarget( + callback2, new Rect(20, 30, 40, 50), new Point(10, 10), + View.SCROLL_CAPTURE_HINT_AUTO); + results.addTarget(target2); + + // callback 3, 20x20, hint=INCLUDE, responds after 10s from bg thread + FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(mBgExec); + callback3.setScrollBounds(new Rect(0, 0, 20, 20)); + callback3.setDelay(1500); + ScrollCaptureTarget target3 = createTarget( + callback3, new Rect(20, 30, 40, 50), new Point(10, 10), + View.SCROLL_CAPTURE_HINT_INCLUDE); + results.addTarget(target3); + + // callback 1 will be received + // callback 2 & 3 will be ignored due to timeout + SystemClock.sleep(500); + results.finish(); + + ScrollCaptureTarget result = results.getTopResult(); + assertSame("Expected target1 as the result, due to timeouts of others", target1, result); + assertEquals("callback1 should have been called", + 1, callback1.getOnScrollCaptureSearchCount()); + assertEquals("callback2 should have been called", + 1, callback2.getOnScrollCaptureSearchCount()); + assertEquals("callback3 should have been called", + 1, callback3.getOnScrollCaptureSearchCount()); + + assertEquals("result has wrong scroll bounds", + new Rect(5, 5, 15, 15), result.getScrollBounds()); + assertNull("target2 should not have been updated", + target2.getScrollBounds()); + assertNull("target3 should not have been updated", + target3.getScrollBounds()); + } + + @Test + public void testWithCallbackMultipleReplies() { + // Calls response methods 3 times each + ScrollCaptureCallback callback1 = new CallbackStub() { + @Override + public void onScrollCaptureSearch(@NonNull CancellationSignal signal, + @NonNull Consumer<Rect> onReady) { + onReady.accept(new Rect(1, 2, 3, 4)); + onReady.accept(new Rect(9, 10, 11, 12)); + } + }; + + ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50), + new Point(10, 10), View.SCROLL_CAPTURE_HINT_AUTO); + + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + results.addTarget(target1); + assertTrue(results.isComplete()); + + ScrollCaptureTarget result = results.getTopResult(); + assertSame("Expected target1", target1, result); + assertEquals("result has wrong scroll bounds", + new Rect(1, 2, 3, 4), result.getScrollBounds()); + } + + private void setupTargetView(View view, Rect localVisibleRect, int scrollCaptureHint) { + view.setScrollCaptureHint(scrollCaptureHint); + view.onVisibilityAggregated(true); + // Treat any offset as padding, outset localVisibleRect on all sides and use this as + // child bounds + Rect bounds = new Rect(localVisibleRect); + bounds.inset(-bounds.left, -bounds.top, bounds.left, bounds.top); + view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom); + view.onVisibilityAggregated(true); + } + + private ScrollCaptureTarget createTarget(ScrollCaptureCallback callback, Rect localVisibleRect, + Point positionInWindow, int scrollCaptureHint) { + View mockView = new View(getTargetContext()); + return createTargetWithView(mockView, callback, localVisibleRect, positionInWindow, + scrollCaptureHint); + } + + private ScrollCaptureTarget createTargetWithView(View view, ScrollCaptureCallback callback, + Rect localVisibleRect, Point positionInWindow, int scrollCaptureHint) { + setupTargetView(view, localVisibleRect, scrollCaptureHint); + return new ScrollCaptureTarget(view, localVisibleRect, positionInWindow, callback); + } + + + static class FakeView extends ViewGroup implements ViewParent { + FakeView(Context context, int l, int t, int r, int b, int id) { + super(context); + layout(l, t, r, b); + setId(id); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + } + } + + static class FakeScrollCaptureCallback implements ScrollCaptureCallback { + private final Executor mExecutor; + private Rect mScrollBounds; + private long mDelayMillis; + private int mOnScrollCaptureSearchCount; + FakeScrollCaptureCallback(Executor executor) { + mExecutor = executor; + } + public int getOnScrollCaptureSearchCount() { + return mOnScrollCaptureSearchCount; + } + + @Override + public void onScrollCaptureSearch(CancellationSignal signal, Consumer<Rect> onReady) { + mOnScrollCaptureSearchCount++; + run(() -> { + Rect b = getScrollBounds(); + onReady.accept(b); + }); + } + + @Override + public void onScrollCaptureStart(ScrollCaptureSession session, CancellationSignal signal, + Runnable onReady) { + run(onReady); + } + + @Override + public void onScrollCaptureImageRequest(ScrollCaptureSession session, + CancellationSignal signal, Rect captureArea, Consumer<Rect> onReady) { + run(() -> onReady.accept(captureArea)); + } + + @Override + public void onScrollCaptureEnd(Runnable onReady) { + run(onReady); + } + + public void setScrollBounds(@Nullable Rect scrollBounds) { + mScrollBounds = scrollBounds; + } + + public void setDelay(long delayMillis) { + mDelayMillis = delayMillis; + } + + protected Rect getScrollBounds() { + return mScrollBounds; + } + + protected void run(Runnable r) { + mExecutor.execute(() -> { + delay(); + r.run(); + }); + } + + protected void delay() { + if (mDelayMillis > 0) { + try { + Thread.sleep(mDelayMillis); + } catch (InterruptedException e) { + // Ignore + } + } + } + } + static class CallbackStub implements ScrollCaptureCallback { + @Override + public void onScrollCaptureSearch(@NonNull CancellationSignal signal, + @NonNull Consumer<Rect> onReady) { + } + + @Override + public void onScrollCaptureStart(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Runnable onReady) { + } + + @Override + public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Rect captureArea, + Consumer<Rect> onReady) { + } + + @Override + public void onScrollCaptureEnd(@NonNull Runnable onReady) { + } + } +} diff --git a/core/tests/coretests/src/android/view/ScrollCaptureTargetResolverTest.java b/core/tests/coretests/src/android/view/ScrollCaptureTargetResolverTest.java deleted file mode 100644 index 8b21b8ecee89..000000000000 --- a/core/tests/coretests/src/android/view/ScrollCaptureTargetResolverTest.java +++ /dev/null @@ -1,498 +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.view; - -import static androidx.test.InstrumentationRegistry.getTargetContext; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; - -import android.annotation.Nullable; -import android.content.Context; -import android.graphics.Point; -import android.graphics.Rect; -import android.os.Handler; - -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.LinkedList; -import java.util.function.Consumer; - -/** - * Tests of {@link ScrollCaptureTargetResolver}. - */ -@RunWith(AndroidJUnit4.class) -public class ScrollCaptureTargetResolverTest { - - private static final long TEST_TIMEOUT_MS = 2000; - private static final long RESOLVER_TIMEOUT_MS = 1000; - - private Handler mHandler; - private TargetConsumer mTargetConsumer; - - @Before - public void setUp() { - mTargetConsumer = new TargetConsumer(); - mHandler = new Handler(getTargetContext().getMainLooper()); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testEmptyQueue() throws InterruptedException { - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(new LinkedList<>()); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - // Test only - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertNull("Expected null due to empty queue", result); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testNoValidTargets() throws InterruptedException { - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - - // Supplies scrollBounds = null - FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(); - callback1.setScrollBounds(null); - ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50), - new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); - - // Supplies scrollBounds = empty rect - FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(); - callback2.setScrollBounds(new Rect()); - ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50), - new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE); - - targetQueue.add(target1); - targetQueue.add(target2); - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - // Test only - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertNull("Expected null due to no valid targets", result); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testSingleTarget() throws InterruptedException { - FakeScrollCaptureCallback callback = new FakeScrollCaptureCallback(); - ScrollCaptureTarget target = createTarget(callback, - new Rect(20, 30, 40, 50), new Point(10, 10), - View.SCROLL_CAPTURE_HINT_AUTO); - callback.setScrollBounds(new Rect(2, 2, 18, 18)); - - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - targetQueue.add(target); - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - // Test only - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertSame("Excepted the same target as a result", target, result); - assertEquals("result has wrong scroll bounds", - new Rect(2, 2, 18, 18), result.getScrollBounds()); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testSingleTarget_backgroundThread() throws InterruptedException { - BackgroundTestCallback callback1 = new BackgroundTestCallback(); - ScrollCaptureTarget target1 = createTarget(callback1, - new Rect(20, 30, 40, 50), new Point(10, 10), - View.SCROLL_CAPTURE_HINT_AUTO); - callback1.setDelay(100); - callback1.setScrollBounds(new Rect(2, 2, 18, 18)); - - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - targetQueue.add(target1); - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - // Test only - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertSame("Excepted the single target1 as a result", target1, result); - assertEquals("Result has wrong scroll bounds", - new Rect(2, 2, 18, 18), result.getScrollBounds()); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testPreferNonEmptyBounds() throws InterruptedException { - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - - FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(); - callback1.setScrollBounds(new Rect()); - ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50), - new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); - - FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(); - callback2.setScrollBounds(new Rect(0, 0, 20, 20)); - ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50), - new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE); - - FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(); - callback3.setScrollBounds(null); - ScrollCaptureTarget target3 = createTarget(callback3, new Rect(20, 30, 40, 50), - new Point(0, 40), View.SCROLL_CAPTURE_HINT_AUTO); - - targetQueue.add(target1); - targetQueue.add(target2); // scrollBounds not null or empty() - targetQueue.add(target3); - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertEquals("Expected " + target2 + " as a result", target2, result); - assertEquals("result has wrong scroll bounds", - new Rect(0, 0, 20, 20), result.getScrollBounds()); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testPreferHintInclude() throws InterruptedException { - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - - FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(); - callback1.setScrollBounds(new Rect(0, 0, 20, 20)); - ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50), - new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); - - FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(); - callback2.setScrollBounds(new Rect(1, 1, 19, 19)); - ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50), - new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE); - - FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(); - callback3.setScrollBounds(new Rect(2, 2, 18, 18)); - ScrollCaptureTarget target3 = createTarget(callback3, new Rect(20, 30, 40, 50), - new Point(0, 40), View.SCROLL_CAPTURE_HINT_AUTO); - - targetQueue.add(target1); - targetQueue.add(target2); // * INCLUDE > AUTO - targetQueue.add(target3); - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertEquals("input = " + targetQueue + " Expected " + target2 - + " as the result, due to hint=INCLUDE", target2, result); - assertEquals("result has wrong scroll bounds", - new Rect(1, 1, 19, 19), result.getScrollBounds()); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testDescendantPreferred() throws InterruptedException { - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - - ViewGroup targetView1 = new FakeRootView(getTargetContext(), 0, 0, 60, 60); // 60x60 - ViewGroup targetView2 = new FakeRootView(getTargetContext(), 20, 30, 40, 50); // 20x20 - ViewGroup targetView3 = new FakeRootView(getTargetContext(), 5, 5, 15, 15); // 10x10 - - targetView1.addView(targetView2); - targetView2.addView(targetView3); - - // Create first target with an unrelated parent - FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(); - callback1.setScrollBounds(new Rect(0, 0, 60, 60)); - ScrollCaptureTarget target1 = createTargetWithView(targetView1, callback1, - new Rect(0, 0, 60, 60), - new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); - - // Create second target associated with a view within parent2 - FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(); - callback2.setScrollBounds(new Rect(0, 0, 20, 20)); - ScrollCaptureTarget target2 = createTargetWithView(targetView2, callback2, - new Rect(0, 0, 20, 20), - new Point(20, 30), View.SCROLL_CAPTURE_HINT_AUTO); - - // Create third target associated with a view within parent3 - FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(); - callback3.setScrollBounds(new Rect(0, 0, 15, 15)); - ScrollCaptureTarget target3 = createTargetWithView(targetView3, callback3, - new Rect(0, 0, 15, 15), - new Point(25, 35), View.SCROLL_CAPTURE_HINT_AUTO); - - targetQueue.add(target1); // auto, 60x60 - targetQueue.add(target2); // auto, 20x20 - targetQueue.add(target3); // auto, 15x15 <- innermost scrollable - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - // Test only - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertSame("Expected target3 as the result, due to relation", target3, result); - assertEquals("result has wrong scroll bounds", - new Rect(0, 0, 15, 15), result.getScrollBounds()); - } - - /** - * If a timeout expires, late results are ignored. - */ - @Test(timeout = TEST_TIMEOUT_MS) - public void testTimeout() throws InterruptedException { - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - - // callback 1, 10x10, hint=AUTO, responds immediately from bg thread - BackgroundTestCallback callback1 = new BackgroundTestCallback(); - callback1.setScrollBounds(new Rect(5, 5, 15, 15)); - ScrollCaptureTarget target1 = createTarget( - callback1, new Rect(20, 30, 40, 50), new Point(10, 10), - View.SCROLL_CAPTURE_HINT_AUTO); - targetQueue.add(target1); - - // callback 2, 20x20, hint=AUTO, responds after 5s from bg thread - BackgroundTestCallback callback2 = new BackgroundTestCallback(); - callback2.setScrollBounds(new Rect(0, 0, 20, 20)); - callback2.setDelay(5000); - ScrollCaptureTarget target2 = createTarget( - callback2, new Rect(20, 30, 40, 50), new Point(10, 10), - View.SCROLL_CAPTURE_HINT_AUTO); - targetQueue.add(target2); - - // callback 3, 20x20, hint=INCLUDE, responds after 10s from bg thread - BackgroundTestCallback callback3 = new BackgroundTestCallback(); - callback3.setScrollBounds(new Rect(0, 0, 20, 20)); - callback3.setDelay(10000); - ScrollCaptureTarget target3 = createTarget( - callback3, new Rect(20, 30, 40, 50), new Point(10, 10), - View.SCROLL_CAPTURE_HINT_INCLUDE); - targetQueue.add(target3); - - // callback 1 will be received - // callback 2 & 3 will be ignored due to timeout - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertSame("Expected target1 as the result, due to timeouts of others", target1, result); - assertEquals("result has wrong scroll bounds", - new Rect(5, 5, 15, 15), result.getScrollBounds()); - assertEquals("callback1 should have been called", - 1, callback1.getOnScrollCaptureSearchCount()); - assertEquals("callback2 should have been called", - 1, callback2.getOnScrollCaptureSearchCount()); - assertEquals("callback3 should have been called", - 1, callback3.getOnScrollCaptureSearchCount()); - } - - @Test(timeout = TEST_TIMEOUT_MS) - public void testWithCallbackMultipleReplies() throws InterruptedException { - // Calls response methods 3 times each - RepeatingCaptureCallback callback1 = new RepeatingCaptureCallback(3); - callback1.setScrollBounds(new Rect(2, 2, 18, 18)); - ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50), - new Point(10, 10), View.SCROLL_CAPTURE_HINT_AUTO); - - FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(); - callback2.setScrollBounds(new Rect(0, 0, 20, 20)); - ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50), - new Point(10, 10), View.SCROLL_CAPTURE_HINT_AUTO); - - LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>(); - targetQueue.add(target1); - targetQueue.add(target2); - - ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue); - resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer); - - resolver.waitForResult(); - - ScrollCaptureTarget result = mTargetConsumer.getLastValue(); - assertSame("Expected target2 as the result, due to hint=INCLUDE", target2, result); - assertEquals("result has wrong scroll bounds", - new Rect(0, 0, 20, 20), result.getScrollBounds()); - assertEquals("callback1 should have been called once", - 1, callback1.getOnScrollCaptureSearchCount()); - assertEquals("callback2 should have been called once", - 1, callback2.getOnScrollCaptureSearchCount()); - } - - private static class TargetConsumer implements Consumer<ScrollCaptureTarget> { - volatile ScrollCaptureTarget mResult; - int mAcceptCount; - - ScrollCaptureTarget getLastValue() { - return mResult; - } - - int acceptCount() { - return mAcceptCount; - } - - @Override - public void accept(@Nullable ScrollCaptureTarget t) { - mAcceptCount++; - mResult = t; - } - } - - private void setupTargetView(View view, Rect localVisibleRect, int scrollCaptureHint) { - view.setScrollCaptureHint(scrollCaptureHint); - view.onVisibilityAggregated(true); - // Treat any offset as padding, outset localVisibleRect on all sides and use this as - // child bounds - Rect bounds = new Rect(localVisibleRect); - bounds.inset(-bounds.left, -bounds.top, bounds.left, bounds.top); - view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom); - view.onVisibilityAggregated(true); - } - - private ScrollCaptureTarget createTarget(ScrollCaptureCallback callback, Rect localVisibleRect, - Point positionInWindow, int scrollCaptureHint) { - View mockView = new View(getTargetContext()); - return createTargetWithView(mockView, callback, localVisibleRect, positionInWindow, - scrollCaptureHint); - } - - private ScrollCaptureTarget createTargetWithView(View view, ScrollCaptureCallback callback, - Rect localVisibleRect, Point positionInWindow, int scrollCaptureHint) { - setupTargetView(view, localVisibleRect, scrollCaptureHint); - return new ScrollCaptureTarget(view, localVisibleRect, positionInWindow, callback); - } - - - static class FakeRootView extends ViewGroup implements ViewParent { - FakeRootView(Context context, int l, int t, int r, int b) { - super(context); - layout(l, t, r, b); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - } - } - - static class FakeScrollCaptureCallback implements ScrollCaptureCallback { - private Rect mScrollBounds; - private long mDelayMillis; - private int mOnScrollCaptureSearchCount; - - public int getOnScrollCaptureSearchCount() { - return mOnScrollCaptureSearchCount; - } - - @Override - public void onScrollCaptureSearch(Consumer<Rect> onReady) { - mOnScrollCaptureSearchCount++; - run(() -> { - Rect b = getScrollBounds(); - onReady.accept(b); - }); - } - - @Override - public void onScrollCaptureStart(ScrollCaptureSession session, Runnable onReady) { - run(onReady); - } - - @Override - public void onScrollCaptureImageRequest(ScrollCaptureSession session, Rect captureArea) { - run(() -> session.notifyBufferSent(0, captureArea)); - } - - @Override - public void onScrollCaptureEnd(Runnable onReady) { - run(onReady); - } - - public void setScrollBounds(@Nullable Rect scrollBounds) { - mScrollBounds = scrollBounds; - } - - public void setDelay(long delayMillis) { - mDelayMillis = delayMillis; - } - - protected Rect getScrollBounds() { - return mScrollBounds; - } - - protected void run(Runnable r) { - delay(); - r.run(); - } - - protected void delay() { - if (mDelayMillis > 0) { - try { - Thread.sleep(mDelayMillis); - } catch (InterruptedException e) { - // Ignore - } - } - } - } - - static class RepeatingCaptureCallback extends FakeScrollCaptureCallback { - private int mRepeatCount; - - RepeatingCaptureCallback(int repeatCount) { - mRepeatCount = repeatCount; - } - - protected void run(Runnable r) { - delay(); - for (int i = 0; i < mRepeatCount; i++) { - r.run(); - } - } - } - - /** Response to async calls on an arbitrary background thread */ - static class BackgroundTestCallback extends FakeScrollCaptureCallback { - static int sCount = 0; - private void runOnBackgroundThread(Runnable r) { - final Runnable target = () -> { - delay(); - r.run(); - }; - Thread t = new Thread(target); - synchronized (BackgroundTestCallback.this) { - sCount++; - } - t.setName("Background-Thread-" + sCount); - t.start(); - } - - @Override - protected void run(Runnable r) { - runOnBackgroundThread(r); - } - } -} diff --git a/core/tests/coretests/src/android/view/SoundEffectConstantsTest.java b/core/tests/coretests/src/android/view/SoundEffectConstantsTest.java new file mode 100644 index 000000000000..45ffb1221515 --- /dev/null +++ b/core/tests/coretests/src/android/view/SoundEffectConstantsTest.java @@ -0,0 +1,54 @@ +/* + * 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.view; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link SoundEffectConstants} + * + * Build/Install/Run: + * atest FrameworksCoreTests:SoundEffectConstantsTest + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SoundEffectConstantsTest { + + @Test + public void testIsNavigationRepeat() { + assertTrue(SoundEffectConstants.isNavigationRepeat( + SoundEffectConstants.NAVIGATION_REPEAT_RIGHT)); + assertTrue(SoundEffectConstants.isNavigationRepeat( + SoundEffectConstants.NAVIGATION_REPEAT_LEFT)); + assertTrue( + SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_REPEAT_UP)); + assertTrue(SoundEffectConstants.isNavigationRepeat( + SoundEffectConstants.NAVIGATION_REPEAT_DOWN)); + assertFalse(SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_RIGHT)); + assertFalse(SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_LEFT)); + assertFalse(SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_UP)); + assertFalse(SoundEffectConstants.isNavigationRepeat(SoundEffectConstants.NAVIGATION_DOWN)); + assertFalse(SoundEffectConstants.isNavigationRepeat(-1)); + } +} diff --git a/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java b/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java index 4983bed742fd..36104cf0f71d 100644 --- a/core/tests/uwbtests/src/android/uwb/UwbManagerTest.java +++ b/core/tests/coretests/src/android/view/SurfaceControlFpsListenerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Android Open Source Project + * 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. @@ -14,36 +14,33 @@ * limitations under the License. */ -package android.uwb; +package android.view; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import android.platform.test.annotations.Presubmit; -import android.content.Context; - -import androidx.test.InstrumentationRegistry; -import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; -/** - * Test of {@link UwbManager}. - */ -@SmallTest @RunWith(AndroidJUnit4.class) -public class UwbManagerTest { - - public final Context mContext = InstrumentationRegistry.getContext(); +@SmallTest +@Presubmit +public class SurfaceControlFpsListenerTest { @Test - public void testServiceAvailable() { - UwbManager manager = mContext.getSystemService(UwbManager.class); - if (UwbTestUtils.isUwbSupported(mContext)) { - assertNotNull(manager); - } else { - assertNull(manager); - } + public void registersAndUnregisters() { + + SurfaceControlFpsListener listener = new SurfaceControlFpsListener() { + @Override + public void onFpsReported(float fps) { + // Ignore + } + }; + + listener.register(new SurfaceControl()); + + listener.unregister(); } } diff --git a/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java b/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java new file mode 100644 index 000000000000..1520c6e34a95 --- /dev/null +++ b/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java @@ -0,0 +1,86 @@ +/* + * 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.view; + +import static org.junit.Assert.*; + +import android.graphics.Rect; +import android.os.CancellationSignal; + +import androidx.annotation.NonNull; + +import java.util.function.Consumer; + +class TestScrollCaptureCallback implements ScrollCaptureCallback { + private Consumer<Rect> mSearchConsumer; + private Runnable mStartOnReady; + private Consumer<Rect> mImageOnComplete; + private Runnable mOnEndReady; + private volatile int mModCount; + + @Override + public void onScrollCaptureSearch(@NonNull CancellationSignal signal, + @NonNull Consumer<Rect> onReady) { + mSearchConsumer = onReady; + mModCount++; + } + + @Override + public void onScrollCaptureStart(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Runnable onReady) { + mStartOnReady = onReady; + mModCount++; + } + + @Override + public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Rect captureArea, + @NonNull Consumer<Rect> onComplete) { + mImageOnComplete = onComplete; + mModCount++; + } + + @Override + public void onScrollCaptureEnd(@NonNull Runnable onReady) { + mOnEndReady = onReady; + } + + void completeSearchRequest(Rect scrollBounds) { + assertNotNull("Did not receive search request", mSearchConsumer); + mSearchConsumer.accept(scrollBounds); + mModCount++; + } + + void verifyZeroInteractions() { + assertEquals("Expected zero interactions", 0, mModCount); + } + + void completeStartRequest() { + assertNotNull("Did not receive start request", mStartOnReady); + mStartOnReady.run(); + } + + void completeImageRequest(Rect captured) { + assertNotNull("Did not receive image request", mImageOnComplete); + mImageOnComplete.accept(captured); + } + + void completeEndRequest() { + assertNotNull("Did not receive end request", mOnEndReady); + mOnEndReady.run(); + } +} diff --git a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java index 3af0533e763c..41cd4c562bd8 100644 --- a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java +++ b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java @@ -19,7 +19,9 @@ package android.view; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 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.testng.AssertJUnit.assertSame; @@ -27,19 +29,20 @@ import android.annotation.Nullable; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; +import android.os.CancellationSignal; import android.platform.test.annotations.Presubmit; +import androidx.annotation.NonNull; import androidx.test.filters.FlakyTest; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import java.util.LinkedList; -import java.util.Queue; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Exercises Scroll Capture search in {@link ViewGroup}. @@ -50,10 +53,7 @@ import java.util.Queue; @RunWith(MockitoJUnitRunner.class) public class ViewGroupScrollCaptureTest { - @Mock - ScrollCaptureCallback mMockCallback; - @Mock - ScrollCaptureCallback mMockCallback2; + private static final Executor DIRECT_EXECUTOR = Runnable::run; /** Make sure the hint flags are saved and loaded correctly. */ @Test @@ -103,25 +103,24 @@ public class ViewGroupScrollCaptureTest { public void testDispatchScrollCaptureSearch_noCallback_hintAuto() throws Exception { final Context context = getInstrumentation().getContext(); final MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200); + TestScrollCaptureCallback callback = new TestScrollCaptureCallback(); // When system internal scroll capture is requested, this callback is returned. - viewGroup.setScrollCaptureCallbackInternalForTest(mMockCallback); + viewGroup.setScrollCaptureCallbackInternalForTest(callback); Rect localVisibleRect = new Rect(0, 0, 200, 200); Point windowOffset = new Point(); - LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR); // Dispatch - viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList); - - // Verify the system checked for fallback support - viewGroup.assertDispatchScrollCaptureCount(1); - viewGroup.assertLastDispatchScrollCaptureArgs(localVisibleRect, windowOffset); + viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget); + callback.completeSearchRequest(new Rect(1, 2, 3, 4)); + assertTrue(results.isComplete()); // Verify the target is as expected. - assertEquals(1, targetList.size()); - ScrollCaptureTarget target = targetList.get(0); - assertSame("Target has the wrong callback", mMockCallback, target.getCallback()); + ScrollCaptureTarget target = results.getTopResult(); + assertNotNull("Target not found", target); + assertSame("Target has the wrong callback", callback, target.getCallback()); assertSame("Target has the wrong View", viewGroup, target.getContainingView()); assertEquals("Target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO, target.getContainingView().getScrollCaptureHint()); @@ -139,18 +138,22 @@ public class ViewGroupScrollCaptureTest { final MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200, View.SCROLL_CAPTURE_HINT_EXCLUDE); + TestScrollCaptureCallback callback = new TestScrollCaptureCallback(); + // When system internal scroll capture is requested, this callback is returned. - viewGroup.setScrollCaptureCallbackInternalForTest(mMockCallback); + viewGroup.setScrollCaptureCallbackInternalForTest(callback); Rect localVisibleRect = new Rect(0, 0, 200, 200); Point windowOffset = new Point(); - LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR); + assertTrue(results.isComplete()); // Dispatch - viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList); + viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget); + callback.verifyZeroInteractions(); // Verify the results. - assertEquals("Target list size should be zero.", 0, targetList.size()); + assertTrue("Results should be empty.", results.isEmpty()); } /** @@ -164,27 +167,34 @@ public class ViewGroupScrollCaptureTest { final Context context = getInstrumentation().getContext(); MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200); + TestScrollCaptureCallback callback = new TestScrollCaptureCallback(); + TestScrollCaptureCallback callback2 = new TestScrollCaptureCallback(); + // With an already provided scroll capture callback - viewGroup.setScrollCaptureCallback(mMockCallback); + viewGroup.setScrollCaptureCallback(callback); // When system internal scroll capture is requested, this callback is returned. - viewGroup.setScrollCaptureCallbackInternalForTest(mMockCallback); + viewGroup.setScrollCaptureCallbackInternalForTest(callback2); Rect localVisibleRect = new Rect(0, 0, 200, 200); Point windowOffset = new Point(); - LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR); // Dispatch to the ViewGroup - viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList); - - // Confirm that framework support was not requested, - // because this view already had a callback set. - viewGroup.assertCreateScrollCaptureCallbackInternalCount(0); + viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget); + callback.completeSearchRequest(new Rect(1, 2, 3, 4)); // Verify the target is as expected. - assertEquals(1, targetList.size()); - ScrollCaptureTarget target = targetList.get(0); - assertSame("Target has the wrong callback", mMockCallback, target.getCallback()); + assertFalse(results.isEmpty()); + assertTrue(results.isComplete()); + + // internal framework callback was not requested + callback2.verifyZeroInteractions(); + + ScrollCaptureTarget target = results.getTopResult(); + + assertNotNull("Target not found", target); + assertSame("Target has the wrong callback", callback, target.getCallback()); assertSame("Target has the wrong View", viewGroup, target.getContainingView()); assertEquals("Target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO, target.getContainingView().getScrollCaptureHint()); @@ -201,22 +211,22 @@ public class ViewGroupScrollCaptureTest { final Context context = getInstrumentation().getContext(); MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200, View.SCROLL_CAPTURE_HINT_EXCLUDE); + + TestScrollCaptureCallback callback = new TestScrollCaptureCallback(); + // With an already provided scroll capture callback - viewGroup.setScrollCaptureCallback(mMockCallback); + viewGroup.setScrollCaptureCallback(callback); Rect localVisibleRect = new Rect(0, 0, 200, 200); Point windowOffset = new Point(); - LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR); // Dispatch to the ViewGroup itself - viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList); - - // Confirm that framework support was not requested, because this view is excluded. - // (And because this view has a callback set.) - viewGroup.assertCreateScrollCaptureCallbackInternalCount(0); + viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget); + callback.verifyZeroInteractions(); // Has callback, but hint=excluded, so excluded. - assertTrue(targetList.isEmpty()); + assertNull(results.getTopResult()); } /** @@ -252,37 +262,43 @@ public class ViewGroupScrollCaptureTest { // | | | // +---------------+----------+ (200,200) - // View 1 is clipped and not visible. + // View 1 is fully clipped and not visible. final MockView view1 = new MockView(context, 0, 0, 200, 25); viewGroup.addView(view1); - // View 2 is partially visible. + // View 2 is partially visible. (75x75) final MockView view2 = new MockView(context, 0, 25, 150, 100); viewGroup.addView(view2); - // View 3 is partially visible. + TestScrollCaptureCallback callback1 = new TestScrollCaptureCallback(); + + // View 3 is partially visible (175x50) // Pretend View3 can scroll by having framework provide fallback support final MockView view3 = new MockView(context, 0, 100, 200, 200); // When system internal scroll capture is requested for this view, return this callback. - view3.setScrollCaptureCallbackInternalForTest(mMockCallback); + view3.setScrollCaptureCallbackInternalForTest(callback1); viewGroup.addView(view3); // View 4 is invisible and should be ignored. final MockView view4 = new MockView(context, 150, 25, 200, 100, View.INVISIBLE); viewGroup.addView(view4); - // View 4 is invisible and should be ignored. + TestScrollCaptureCallback callback2 = new TestScrollCaptureCallback(); + + // View 5 is partially visible and explicitly included via flag. (25x50) final MockView view5 = new MockView(context, 150, 100, 200, 200); - // When system internal scroll capture is requested for this view, return this callback. - view5.setScrollCaptureCallback(mMockCallback2); + view5.setScrollCaptureCallback(callback2); view5.setScrollCaptureHint(View.SCROLL_CAPTURE_HINT_INCLUDE); viewGroup.addView(view5); // Where targets are added - final LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); + final ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR); // Dispatch to the ViewGroup - viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList); + viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget); + callback1.completeSearchRequest(new Rect(0, 0, 200, 100)); + callback2.completeSearchRequest(new Rect(0, 0, 50, 100)); + assertTrue(results.isComplete()); // View 1 is entirely clipped by the parent and not visible, dispatch // skips this view entirely. @@ -317,18 +333,14 @@ public class ViewGroupScrollCaptureTest { view5.assertCreateScrollCaptureCallbackInternalCount(0); // 2 views should have been returned, view3 & view5 - assertEquals(2, targetList.size()); - - ScrollCaptureTarget target = targetList.get(0); - assertSame("First target has the wrong View", view3, target.getContainingView()); - assertSame("First target has the wrong callback", mMockCallback, target.getCallback()); - assertEquals("First target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO, - target.getContainingView().getScrollCaptureHint()); - - target = targetList.get(1); - assertSame("Second target has the wrong View", view5, target.getContainingView()); - assertSame("Second target has the wrong callback", mMockCallback2, target.getCallback()); - assertEquals("Second target hint is incorrect", View.SCROLL_CAPTURE_HINT_INCLUDE, + assertFalse(results.isEmpty()); + assertTrue(results.isComplete()); + + ScrollCaptureTarget target = results.getTopResult(); + assertNotNull("Target not found", target); + assertSame("Result is the wrong View", view5, target.getContainingView()); + assertSame("Result is the wrong callback", callback2, target.getCallback()); + assertEquals("First target hint is incorrect", View.SCROLL_CAPTURE_HINT_INCLUDE, target.getContainingView().getScrollCaptureHint()); } @@ -371,7 +383,7 @@ public class ViewGroupScrollCaptureTest { } void assertCreateScrollCaptureCallbackInternalCount(int count) { - assertEquals("Unexpected number of calls to createScrollCaptureCallackInternal", + assertEquals("Unexpected number of calls to createScrollCaptureCallbackInternal", count, mCreateScrollCaptureCallbackInternalCount); } @@ -385,11 +397,11 @@ public class ViewGroupScrollCaptureTest { @Override public void dispatchScrollCaptureSearch(Rect localVisibleRect, Point windowOffset, - Queue<ScrollCaptureTarget> targets) { + Consumer<ScrollCaptureTarget> results) { mDispatchScrollCaptureSearchNumCalls++; mDispatchScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect); mDispatchScrollCaptureSearchLastWindowOffset = new Point(windowOffset); - super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets); + super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results); } @Override @@ -401,13 +413,31 @@ public class ViewGroupScrollCaptureTest { } } + static class CallbackStub implements ScrollCaptureCallback { + + @Override + public void onScrollCaptureSearch(@NonNull CancellationSignal signal, + @NonNull Consumer<Rect> onReady) { + } + + @Override + public void onScrollCaptureStart(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Runnable onReady) { + } + + @Override + public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session, + @NonNull CancellationSignal signal, @NonNull Rect captureArea, + Consumer<Rect> onComplete) { + } + + @Override + public void onScrollCaptureEnd(@NonNull Runnable onReady) { + } + }; + public static final class MockViewGroup extends ViewGroup { private ScrollCaptureCallback mInternalCallback; - private int mDispatchScrollCaptureSearchNumCalls; - private Rect mDispatchScrollCaptureSearchLastLocalVisibleRect; - private Point mDispatchScrollCaptureSearchLastWindowOffset; - private int mCreateScrollCaptureCallbackInternalCount; - MockViewGroup(Context context) { this(context, /* left */ 0, /* top */0, /* right */ 0, /* bottom */0); @@ -428,16 +458,10 @@ public class ViewGroupScrollCaptureTest { mInternalCallback = internal; } - void assertDispatchScrollCaptureSearchCount(int count) { - assertEquals("Unexpected number of calls to dispatchScrollCaptureSearch", - count, mDispatchScrollCaptureSearchNumCalls); - } - @Override @Nullable public ScrollCaptureCallback createScrollCaptureCallbackInternal(Rect localVisibleRect, Point offsetInWindow) { - mCreateScrollCaptureCallbackInternalCount++; return mInternalCallback; } @@ -445,36 +469,5 @@ public class ViewGroupScrollCaptureTest { protected void onLayout(boolean changed, int l, int t, int r, int b) { // We don't layout this view. } - - void assertDispatchScrollCaptureCount(int count) { - assertEquals(count, mDispatchScrollCaptureSearchNumCalls); - } - - void assertLastDispatchScrollCaptureArgs(Rect localVisibleRect, Point windowOffset) { - assertEquals("arg localVisibleRect to dispatchScrollCaptureCallback was incorrect.", - localVisibleRect, mDispatchScrollCaptureSearchLastLocalVisibleRect); - assertEquals("arg windowOffset to dispatchScrollCaptureCallback was incorrect.", - windowOffset, mDispatchScrollCaptureSearchLastWindowOffset); - } - void assertCreateScrollCaptureCallbackInternalCount(int count) { - assertEquals("Unexpected number of calls to createScrollCaptureCallackInternal", - count, mCreateScrollCaptureCallbackInternalCount); - } - - void reset() { - mDispatchScrollCaptureSearchNumCalls = 0; - mDispatchScrollCaptureSearchLastWindowOffset = null; - mDispatchScrollCaptureSearchLastLocalVisibleRect = null; - mCreateScrollCaptureCallbackInternalCount = 0; - } - - @Override - public void dispatchScrollCaptureSearch(Rect localVisibleRect, Point windowOffset, - Queue<ScrollCaptureTarget> targets) { - mDispatchScrollCaptureSearchNumCalls++; - mDispatchScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect); - mDispatchScrollCaptureSearchLastWindowOffset = new Point(windowOffset); - super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets); - } } } diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index c67174f0ae1e..7746bc2e273a 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -207,7 +207,7 @@ public class ViewRootImplTest { final CountDownLatch latch = new CountDownLatch(1); mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() { @Override - public void onUnavailable() { + public void onScrollCaptureResponse(ScrollCaptureResponse response) { latch.countDown(); } }); @@ -220,6 +220,37 @@ public class ViewRootImplTest { } /** + * Ensure scroll capture request handles a ViewRootImpl with no view tree. + */ + @Test + public void requestScrollCapture_timeout() { + final View view = new View(mContext); + view.setScrollCaptureCallback(new TestScrollCaptureCallback()); // Does nothing + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + WindowManager.LayoutParams wmlp = + new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); + // Set a fake token to bypass 'is your activity running' check + wmlp.token = new Binder(); + view.setLayoutParams(wmlp); + mViewRootImpl.setView(view, wmlp, null); + }); + + final CountDownLatch latch = new CountDownLatch(1); + mViewRootImpl.setScrollCaptureRequestTimeout(100); + mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() { + @Override + public void onScrollCaptureResponse(ScrollCaptureResponse response) { + latch.countDown(); + } + }); + try { + if (!latch.await(2500, TimeUnit.MILLISECONDS)) { + fail("requestScrollCapture timeout did not occur"); + } + } catch (InterruptedException e) { /* ignore */ } + } + + /** * When window doesn't have focus, keys should be dropped. */ @Test diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java index 10aaf317fb49..9cb7876b3e5a 100644 --- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java @@ -105,7 +105,8 @@ public class FrameTrackerTest { mTracker = Mockito.spy( new FrameTracker(session, handler, mRenderer, mViewRootWrapper, mSurfaceControlWrapper, mChoreographer, mWrapper, - /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1)); + /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1, + null)); doNothing().when(mTracker).triggerPerfetto(); } diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java index c4c475b6a0e9..8f4948c02a74 100644 --- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java @@ -96,7 +96,7 @@ public class InteractionJankMonitorTest { new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(), mock(FrameTracker.ChoreographerWrapper.class), new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1, - /*traceThresholdFrameTimeMillis=*/ -1)); + /*traceThresholdFrameTimeMillis=*/ -1, null)); doReturn(tracker).when(monitor).createFrameTracker(any(), any()); // Simulate a trace session and see if begin / end are invoked. @@ -123,21 +123,12 @@ public class InteractionJankMonitorTest { @Test public void testCheckInitState() { InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker); + View view = new View(mActivity); + assertThat(view.isAttachedToWindow()).isFalse(); - // Should return false if invoking begin / end without init invocation. - assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); + // Should return false if the view passed in is not attached to window yet. + assertThat(monitor.begin(view, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse(); - - // Everything should be fine if invoking init first. - boolean thrown = false; - try { - assertThat(monitor.begin(mView, CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue(); - assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue(); - } catch (Exception ex) { - thrown = true; - } finally { - assertThat(thrown).isFalse(); - } } @Test @@ -152,7 +143,7 @@ public class InteractionJankMonitorTest { new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(), mock(FrameTracker.ChoreographerWrapper.class), new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1, - /*traceThresholdFrameTimeMillis=*/ -1)); + /*traceThresholdFrameTimeMillis=*/ -1, null)); doReturn(tracker).when(monitor).createFrameTracker(any(), any()); assertThat(monitor.begin(mView, session.getCuj())).isTrue(); diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java index e2a106484848..add04698e372 100644 --- a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java @@ -19,6 +19,7 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; import android.os.BatteryConsumer; +import android.os.BatteryUsageStatsQuery; import android.os.SystemBatteryConsumer; import android.view.Display; @@ -33,18 +34,29 @@ import org.junit.runner.RunWith; @SmallTest public class AmbientDisplayPowerCalculatorTest { private static final double PRECISION = 0.00001; + private static final long MINUTE_IN_MS = 60 * 1000; @Rule public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() - .setAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY, 360.0); + .setAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY, 10.0); @Test - public void testTimerBasedModel() { + public void testMeasuredEnergyBasedModel() { BatteryStatsImpl stats = mStatsRule.getBatteryStats(); - stats.noteScreenStateLocked(Display.STATE_ON, 1000, 1000, 1000); - stats.noteScreenStateLocked(Display.STATE_DOZE, 2000, 2000, 2000); - stats.noteScreenStateLocked(Display.STATE_OFF, 3000, 3000, 3000); + stats.updateDisplayEnergyLocked(300_000_000, Display.STATE_ON, 0); + + stats.noteScreenStateLocked(Display.STATE_DOZE, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS, + 30 * MINUTE_IN_MS); + + stats.updateDisplayEnergyLocked(200_000_000, Display.STATE_DOZE, + 30 * MINUTE_IN_MS); + + stats.noteScreenStateLocked(Display.STATE_OFF, 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS, + 120 * MINUTE_IN_MS); + + stats.updateDisplayEnergyLocked(100_000_000, Display.STATE_OFF, + 120 * MINUTE_IN_MS); AmbientDisplayPowerCalculator calculator = new AmbientDisplayPowerCalculator(mStatsRule.getPowerProfile()); @@ -55,8 +67,32 @@ public class AmbientDisplayPowerCalculatorTest { mStatsRule.getSystemBatteryConsumer( SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY); assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) - .isEqualTo(1000); + .isEqualTo(90 * MINUTE_IN_MS); + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + .isWithin(PRECISION).of(7.5075075); + } + + @Test + public void testPowerProfileBasedModel() { + BatteryStatsImpl stats = mStatsRule.getBatteryStats(); + + stats.noteScreenStateLocked(Display.STATE_DOZE, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS, + 30 * MINUTE_IN_MS); + stats.noteScreenStateLocked(Display.STATE_OFF, 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS, + 120 * MINUTE_IN_MS); + + AmbientDisplayPowerCalculator calculator = + new AmbientDisplayPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(), + calculator); + + SystemBatteryConsumer consumer = + mStatsRule.getSystemBatteryConsumer( + SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY); + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + .isEqualTo(90 * MINUTE_IN_MS); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) - .isWithin(PRECISION).of(0.1); + .isWithin(PRECISION).of(15.0); } } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java index 9cac7e794965..ff728d651067 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java @@ -77,6 +77,7 @@ import java.util.Arrays; * bit FrameworksCoreTests:com.android.internal.os.BatteryStatsCpuTimesTest */ @SmallTest +@SkipPresubmit("b/180015146") @RunWith(AndroidJUnit4.class) public class BatteryStatsCpuTimesTest { @Mock diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java new file mode 100644 index 000000000000..263daf0cd625 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java @@ -0,0 +1,115 @@ +/* + * 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.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.BatteryManager; +import android.os.BatteryStats; +import android.os.Process; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class BatteryStatsHistoryIteratorTest { + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); + + @Test + public void testIterator() { + MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + batteryStats.setRecordAllHistoryLocked(true); + batteryStats.forceRecordAllHistory(); + + mStatsRule.setTime(1000, 1000); + batteryStats.setNoAutoReset(true); + + batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100, + /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000, + 1_000_000, 1_000_000); + batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100, + /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0, 2_000_000, + 2_000_000, 2_000_000); + + batteryStats.noteAlarmStartLocked("foo", null, APP_UID, 3_000_000, 2_000_000); + batteryStats.noteAlarmFinishLocked("foo", null, APP_UID, 3_001_000, 2_001_000); + + final BatteryStatsHistoryIterator iterator = + batteryStats.createBatteryStatsHistoryIterator(); + + BatteryStats.HistoryItem item = new BatteryStats.HistoryItem(); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_RESET, BatteryStats.HistoryItem.EVENT_NONE, + null, 0, 3_600_000, 90, 1_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE, + null, 0, 3_600_000, 90, 1_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE, + null, 0, 2_400_000, 80, 2_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE, + null, 0, 2_400_000, 80, 2_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, + BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_START, + "foo", APP_UID, 2_400_000, 80, 3_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, + BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_FINISH, + "foo", APP_UID, 2_400_000, 80, 3_001_000); + + assertThat(iterator.next(item)).isFalse(); + } + + private void assertHistoryItem(BatteryStats.HistoryItem item, int command, int eventCode, + String tag, int uid, int batteryChargeUah, int batteryLevel, + long elapsedTimeMs) { + assertThat(item.cmd).isEqualTo(command); + assertThat(item.eventCode).isEqualTo(eventCode); + if (tag == null) { + assertThat(item.eventTag).isNull(); + } else { + assertThat(item.eventTag.string).isEqualTo(tag); + assertThat(item.eventTag.uid).isEqualTo(uid); + } + assertThat(item.batteryChargeUah).isEqualTo(batteryChargeUah); + assertThat(item.batteryLevel).isEqualTo(batteryLevel); + + assertThat(item.time).isEqualTo(elapsedTimeMs); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java index 4b37dd226e69..24baa93337ba 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java @@ -73,6 +73,7 @@ public class BatteryStatsImplTest { } @Test + @SkipPresubmit("b/180015146") public void testUpdateProcStateCpuTimes() { mBatteryStatsImpl.setOnBatteryInternal(true); mBatteryStatsImpl.updateTimeBasesLocked(false, Display.STATE_ON, 0, 0); @@ -230,6 +231,7 @@ public class BatteryStatsImplTest { } @Test + @SkipPresubmit("b/180015146") public void testCopyFromAllUidsCpuTimes() { mBatteryStatsImpl.setOnBatteryInternal(false); mBatteryStatsImpl.updateTimeBasesLocked(false, Display.STATE_ON, 0, 0); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java index 6652c64c4344..931611ea7478 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java @@ -295,6 +295,7 @@ public class BatteryStatsNoteTest extends TestCase { } @SmallTest + @SkipPresubmit("b/180015146") public void testAlarmStartAndFinishLocked() throws Exception { final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); @@ -332,6 +333,7 @@ public class BatteryStatsNoteTest extends TestCase { } @SmallTest + @SkipPresubmit("b/180015146") public void testAlarmStartAndFinishLocked_workSource() throws Exception { final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java index 3b27f1897bd2..dd814e651ede 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java @@ -56,6 +56,7 @@ public class BatteryStatsSamplingTimerTest extends TestCase { } @SmallTest + @SkipPresubmit("b/180015146") public void testEndSampleAndContinueWhenTimeOrCountDecreases() throws Exception { final MockClocks clocks = new MockClocks(); final BatteryStatsImpl.TimeBase timeBase = Mockito.mock(BatteryStatsImpl.TimeBase.class); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index 2e6e0de8d0c2..74c37ada2054 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -30,6 +30,7 @@ import org.junit.runners.Suite; BatteryStatsDualTimerTest.class, BatteryStatsDurationTimerTest.class, BatteryStatsHelperTest.class, + BatteryStatsHistoryIteratorTest.class, BatteryStatsHistoryTest.class, BatteryStatsImplTest.class, BatteryStatsNoteTest.class, @@ -40,12 +41,15 @@ import org.junit.runners.Suite; BatteryStatsTimeBaseTest.class, BatteryStatsTimerTest.class, BatteryStatsUidTest.class, + BatteryUsageStatsProviderTest.class, BatteryUsageStatsTest.class, BatteryStatsUserLifecycleTests.class, BluetoothPowerCalculatorTest.class, BstatsCpuTimesValidationTest.class, CameraPowerCalculatorTest.class, CpuPowerCalculatorTest.class, + CustomMeasuredPowerCalculatorTest.class, + DischargedPowerCalculatorTest.class, FlashlightPowerCalculatorTest.class, GnssPowerCalculatorTest.class, IdlePowerCalculatorTest.class, @@ -69,9 +73,9 @@ import org.junit.runners.Suite; UserPowerCalculatorTest.class, VideoPowerCalculatorTest.class, WakelockPowerCalculatorTest.class, + WifiPowerCalculatorTest.class, com.android.internal.power.MeasuredEnergyStatsTest.class }) public class BatteryStatsTests { -} - +}
\ No newline at end of file diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java index e7a1bcae459a..e90bcb76e457 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java @@ -78,6 +78,7 @@ public class BatteryStatsUserLifecycleTests { } @Test + @SkipPresubmit("b/180015146") public void testNoCpuDataForRemovedUser() throws Exception { mIam.startUserInBackground(mTestUserId); waitUntilTrue("No uids for started user " + mTestUserId, diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java new file mode 100644 index 000000000000..0f591433a84e --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java @@ -0,0 +1,161 @@ +/* + * 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.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.ActivityManager; +import android.content.Context; +import android.os.BatteryManager; +import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.Parcel; +import android.os.Process; +import android.os.UidBatteryConsumer; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BatteryUsageStatsProviderTest { + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + private static final long MINUTE_IN_MS = 60 * 1000; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); + + @Test + public void test_getBatteryUsageStats() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + batteryStats.noteActivityResumedLocked(APP_UID, + 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); + batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_TOP, + 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); + batteryStats.noteActivityPausedLocked(APP_UID, + 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_SERVICE, + 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_CACHED_EMPTY, + 40 * MINUTE_IN_MS, 40 * MINUTE_IN_MS); + + Context context = InstrumentationRegistry.getContext(); + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats); + + final BatteryUsageStats batteryUsageStats = + provider.getBatteryUsageStats(BatteryUsageStatsQuery.DEFAULT); + + final List<UidBatteryConsumer> uidBatteryConsumers = + batteryUsageStats.getUidBatteryConsumers(); + final UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(0); + assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND)) + .isEqualTo(20 * MINUTE_IN_MS); + assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND)) + .isEqualTo(10 * MINUTE_IN_MS); + } + + @Test + public void testWriteAndReadHistory() { + MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + batteryStats.setRecordAllHistoryLocked(true); + batteryStats.forceRecordAllHistory(); + + batteryStats.setNoAutoReset(true); + + batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100, + /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000, + 1_000_000, 1_000_000); + + batteryStats.noteAlarmStartLocked("foo", null, APP_UID, 3_000_000, 2_000_000); + batteryStats.noteAlarmFinishLocked("foo", null, APP_UID, 3_001_000, 2_001_000); + + Context context = InstrumentationRegistry.getContext(); + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats); + + final BatteryUsageStats batteryUsageStats = + provider.getBatteryUsageStats( + new BatteryUsageStatsQuery.Builder().includeBatteryHistory().build()); + + Parcel in = Parcel.obtain(); + batteryUsageStats.writeToParcel(in, 0); + final byte[] bytes = in.marshall(); + + Parcel out = Parcel.obtain(); + out.unmarshall(bytes, 0, bytes.length); + out.setDataPosition(0); + + BatteryUsageStats unparceled = BatteryUsageStats.CREATOR.createFromParcel(out); + + final BatteryStatsHistoryIterator iterator = + unparceled.iterateBatteryStatsHistory(); + BatteryStats.HistoryItem item = new BatteryStats.HistoryItem(); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_RESET, BatteryStats.HistoryItem.EVENT_NONE, + null, 0, 3_600_000, 90, 1_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE, + null, 0, 3_600_000, 90, 1_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, BatteryStats.HistoryItem.EVENT_NONE, + null, 0, 3_600_000, 90, 2_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, + BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_START, + "foo", APP_UID, 3_600_000, 90, 3_000_000); + + assertThat(iterator.next(item)).isTrue(); + assertHistoryItem(item, + BatteryStats.HistoryItem.CMD_UPDATE, + BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_FINISH, + "foo", APP_UID, 3_600_000, 90, 3_001_000); + + assertThat(iterator.next(item)).isFalse(); + } + + private void assertHistoryItem(BatteryStats.HistoryItem item, int command, int eventCode, + String tag, int uid, int batteryChargeUah, int batteryLevel, long elapsedTimeMs) { + assertThat(item.cmd).isEqualTo(command); + assertThat(item.eventCode).isEqualTo(eventCode); + if (tag == null) { + assertThat(item.eventTag).isNull(); + } else { + assertThat(item.eventTag.string).isEqualTo(tag); + assertThat(item.eventTag.uid).isEqualTo(uid); + } + assertThat(item.batteryChargeUah).isEqualTo(batteryChargeUah); + assertThat(item.batteryLevel).isEqualTo(batteryLevel); + + assertThat(item.time).isEqualTo(elapsedTimeMs); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java index 5edd58fb3eb9..167994200ed7 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java @@ -136,17 +136,23 @@ public class BatteryUsageStatsRule implements TestRule { return mBatteryStats.getUidStatsLocked(uid); } - public void setTime(long realtimeUs, long uptimeUs) { - mMockClocks.realtime = realtimeUs; - mMockClocks.uptime = uptimeUs; + public void setTime(long realtimeMs, long uptimeMs) { + mMockClocks.realtime = realtimeMs; + mMockClocks.uptime = uptimeMs; } - void apply(PowerCalculator... calculators) { - apply(BatteryUsageStatsQuery.DEFAULT, calculators); + BatteryUsageStats apply(PowerCalculator... calculators) { + return apply(BatteryUsageStatsQuery.DEFAULT, calculators); } - void apply(BatteryUsageStatsQuery query, PowerCalculator... calculators) { - BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(0, 0); + BatteryUsageStats apply(BatteryUsageStatsQuery query, PowerCalculator... calculators) { + final long[] customMeasuredEnergiesMicroJoules = + mBatteryStats.getCustomMeasuredEnergiesMicroJoules(); + final int customMeasuredEnergiesCount = customMeasuredEnergiesMicroJoules != null + ? customMeasuredEnergiesMicroJoules.length + : 0; + BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( + customMeasuredEnergiesCount, 0); SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats(); for (int i = 0; i < uidStats.size(); i++) { builder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i)); @@ -158,6 +164,7 @@ public class BatteryUsageStatsRule implements TestRule { } mBatteryUsageStats = builder.build(); + return mBatteryUsageStats; } public UidBatteryConsumer getUidBatteryConsumer(int uid) { diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java index 018a810772be..23ea508d19d3 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java @@ -35,6 +35,7 @@ import org.junit.runner.RunWith; import java.util.List; @SmallTest +@SkipPresubmit("b/180015146") @RunWith(AndroidJUnit4.class) public class BatteryUsageStatsTest { @@ -66,33 +67,36 @@ public class BatteryUsageStatsTest { final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks); final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000); - final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1); - builder.setConsumedPower(100); - builder.setDischargePercentage(20); - - final UidBatteryConsumer.Builder uidBatteryConsumerBuilder = - builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid); - uidBatteryConsumerBuilder.setPackageWithHighestDrain("foo"); - uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 300); - uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400); - uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500); - uidBatteryConsumerBuilder.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, 600); - uidBatteryConsumerBuilder.setUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700); - uidBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 800); - - final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder = - builder.getOrCreateSystemBatteryConsumerBuilder( - SystemBatteryConsumer.DRAIN_TYPE_CAMERA); - systemBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100); - systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200); - systemBatteryConsumerBuilder.setUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_CPU, 10300); - systemBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400); + final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1) + .setDischargePercentage(20) + .setDischargedPowerRange(1000, 2000); + + builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid) + .setPackageWithHighestDrain("foo") + .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000) + .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000) + .setConsumedPower( + BatteryConsumer.POWER_COMPONENT_USAGE, 300) + .setConsumedPower( + BatteryConsumer.POWER_COMPONENT_CPU, 400) + .setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500) + .setUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU, 600) + .setUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700) + .setUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 800); + + builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_CAMERA) + .setConsumedPower( + BatteryConsumer.POWER_COMPONENT_CPU, 10100) + .setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200) + .setUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU, 10300) + .setUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400); return builder.build(); } @@ -100,12 +104,18 @@ public class BatteryUsageStatsTest { public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) { assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(100); assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(20); + assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(1000); + assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(2000); final List<UidBatteryConsumer> uidBatteryConsumers = batteryUsageStats.getUidBatteryConsumers(); for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) { if (uidBatteryConsumer.getUid() == 2000) { assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo"); + assertThat(uidBatteryConsumer.getTimeInStateMs( + UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(1000); + assertThat(uidBatteryConsumer.getTimeInStateMs( + UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(2000); assertThat(uidBatteryConsumer.getConsumedPower( BatteryConsumer.POWER_COMPONENT_USAGE)).isEqualTo(300); assertThat(uidBatteryConsumer.getConsumedPower( diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java index e5594712db10..f6aa08bf0645 100644 --- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java @@ -43,6 +43,7 @@ public class BluetoothPowerCalculatorTest { .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0); @Test + @SkipPresubmit("b/180015146") public void testTimerBasedModel() { setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID) .getOrCreateBluetoothControllerActivityLocked(), @@ -73,6 +74,7 @@ public class BluetoothPowerCalculatorTest { } @Test + @SkipPresubmit("b/180015146") public void testReportedPowerBasedModel() { setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID) .getOrCreateBluetoothControllerActivityLocked(), diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java index a80f5a03ee4e..4fe7d70e86ff 100644 --- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java @@ -382,6 +382,7 @@ public class BstatsCpuTimesValidationTest { } @Test + @SkipPresubmit("b/180015146 flakey") public void testCpuFreqTimes_stateFgService() throws Exception { if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { Log.w(TAG, "Skipping " + testName.getMethodName() @@ -514,6 +515,7 @@ public class BstatsCpuTimesValidationTest { } @Test + @SkipPresubmit("b/180015146") public void testCpuFreqTimes_trackingDisabled() throws Exception { if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) { Log.w(TAG, "Skipping " + testName.getMethodName() diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java index 9cf0d375ff51..e691beb09a70 100644 --- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java @@ -92,6 +92,7 @@ public class CpuPowerCalculatorTest { } @Test + @SkipPresubmit("b/180015146") public void testTimerBasedModel() { when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true); diff --git a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java new file mode 100644 index 000000000000..f298f5988fc3 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java @@ -0,0 +1,77 @@ +/* + * 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.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.BatteryConsumer; +import android.os.Process; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; +import android.util.SparseLongArray; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class CustomMeasuredPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); + + @Test + @SkipPresubmit("b/180015146") + public void testMeasuredEnergyCopiedIntoBatteryConsumers() { + final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + SparseLongArray uidEnergies = new SparseLongArray(); + uidEnergies.put(APP_UID, 30_000_000); + batteryStats.updateCustomMeasuredEnergyDataLocked(0, 100_000_000, uidEnergies); + + uidEnergies.put(APP_UID, 120_000_000); + batteryStats.updateCustomMeasuredEnergyDataLocked(1, 200_000_000, uidEnergies); + + CustomMeasuredPowerCalculator calculator = + new CustomMeasuredPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + UidBatteryConsumer uid = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uid.getConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)) + .isWithin(PRECISION).of(2.252252); + assertThat(uid.getConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1)) + .isWithin(PRECISION).of(9.009009); + + SystemBatteryConsumer systemConsumer = mStatsRule.getSystemBatteryConsumer( + SystemBatteryConsumer.DRAIN_TYPE_CUSTOM); + assertThat(systemConsumer.getConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)) + .isWithin(PRECISION).of(7.5075075); + assertThat(systemConsumer.getConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1)) + .isWithin(PRECISION).of(15.015015); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java new file mode 100644 index 000000000000..bec3d1644e81 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/DischargedPowerCalculatorTest.java @@ -0,0 +1,66 @@ +/* + * 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.internal.os; + + +import static com.google.common.truth.Truth.assertThat; + +import android.os.BatteryManager; +import android.os.BatteryUsageStats; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class DischargedPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_BATTERY_CAPACITY, 4000.0); + + @Test + public void testDischargeTotals() { + final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + mStatsRule.setTime(1000, 1000); + batteryStats.resetAllStatsCmdLocked(); + batteryStats.setNoAutoReset(true); + batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100, + /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0, 1_000_000, + 1_000_000, 1_000_000); + batteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100, + /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0, 2_000_000, + 2_000_000, 2_000_000); + + DischargedPowerCalculator calculator = + new DischargedPowerCalculator(mStatsRule.getPowerProfile()); + + final BatteryUsageStats batteryUsageStats = mStatsRule.apply(calculator); + + assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(10); + assertThat(batteryUsageStats.getDischargedPowerRange().getLower()) + .isWithin(PRECISION).of(360.0); + assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()) + .isWithin(PRECISION).of(400.0); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java index 7dca0cb92f9d..177f34875894 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java @@ -87,6 +87,7 @@ public class KernelCpuUidUserSysTimeReaderTest { } @Test + @SkipPresubmit("b/180015146") public void testThrottler() throws Exception { mReader = new KernelCpuUidUserSysTimeReader( new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), true); diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java index 717fac0c7bf8..86e615c035c7 100644 --- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java @@ -48,7 +48,7 @@ public class ScreenPowerCalculatorTest { .setAveragePower(PowerProfile.POWER_SCREEN_FULL, 48.0); @Test - public void testEnergyBasedModel() { + public void testMeasuredEnergyBasedModel() { BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); batteryStats.noteScreenStateLocked(Display.STATE_ON, 0, 0, 0); diff --git a/core/tests/coretests/src/com/android/internal/os/SkipPresubmit.java b/core/tests/coretests/src/com/android/internal/os/SkipPresubmit.java new file mode 100644 index 000000000000..d03ed663cc89 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/SkipPresubmit.java @@ -0,0 +1,30 @@ +/* + * 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.internal.os; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** Annotation to skip a test from TEST_MAPPING presubmit. */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface SkipPresubmit { + /** The optional reason why the test is ignored. */ + String value() default ""; +} diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java index dfbf28b286c6..b5282e9a625a 100644 --- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -78,6 +78,7 @@ public class SystemServicePowerCalculatorTest { } @Test + @SkipPresubmit("b/180015146") public void testPowerProfileBasedModel() { when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true); diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java new file mode 100644 index 000000000000..e1005457c289 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java @@ -0,0 +1,128 @@ +/* + * 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.internal.os; + + +import static com.google.common.truth.Truth.assertThat; + +import android.net.NetworkCapabilities; +import android.net.NetworkStats; +import android.os.BatteryConsumer; +import android.os.Process; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; +import android.os.WorkSource; +import android.os.connectivity.WifiActivityEnergyInfo; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class WifiPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0) + .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX, 480.0) + .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX, 720.0) + .setAveragePower(PowerProfile.POWER_WIFI_ON, 360.0) + .setAveragePower(PowerProfile.POWER_WIFI_SCAN, 480.0) + .setAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, 720.0) + .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0); + + @Test + public void testPowerControllerBasedModel() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + batteryStats.noteNetworkInterfaceForTransports("wifi", + new int[]{NetworkCapabilities.TRANSPORT_WIFI}); + + NetworkStats networkStats = new NetworkStats(10000, 1) + .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100) + .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111); + mStatsRule.setNetworkStats(networkStats); + + WifiActivityEnergyInfo energyInfo = new WifiActivityEnergyInfo(10000, + WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000); + + batteryStats.updateWifiState(energyInfo, 1000, 1000); + + WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); + mStatsRule.apply(calculator); + + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(1423); + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.2214666); + + SystemBatteryConsumer systemConsumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(5577); + assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.645200); + } + + @Test + public void testTimerBasedModel() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + batteryStats.noteNetworkInterfaceForTransports("wifi", + new int[]{NetworkCapabilities.TRANSPORT_WIFI}); + + NetworkStats networkStats = new NetworkStats(10000, 1) + .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100) + .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111); + mStatsRule.setNetworkStats(networkStats); + + batteryStats.noteWifiScanStartedLocked(APP_UID, 1000, 1000); + batteryStats.noteWifiScanStoppedLocked(APP_UID, 2000, 2000); + batteryStats.noteWifiRunningLocked(new WorkSource(APP_UID), 3000, 3000); + batteryStats.noteWifiStoppedLocked(new WorkSource(APP_UID), 4000, 4000); + batteryStats.noteWifiRunningLocked(new WorkSource(Process.WIFI_UID), 1111, 2222); + batteryStats.noteWifiStoppedLocked(new WorkSource(Process.WIFI_UID), 3333, 4444); + + // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively + // on the packet counts. + batteryStats.updateWifiState(/* energyInfo */ null, 1000, 1000); + + WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); + mStatsRule.apply(calculator); + + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(1000); + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.8231573); + + SystemBatteryConsumer systemConsumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(2222); + assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.8759216); + } +} diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java index 5fd5a7838c3a..d217bce24b9c 100644 --- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java @@ -81,11 +81,11 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); final MeasuredEnergyStats newStats = MeasuredEnergyStats.createFromTemplate(stats); @@ -114,11 +114,11 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); final Parcel parcel = Parcel.obtain(); stats.writeToParcel(parcel); @@ -149,11 +149,11 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); final Parcel parcel = Parcel.obtain(); MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); @@ -185,17 +185,17 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats template = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - template.updateCustomBucket(0, 50, true); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + template.updateCustomBucket(0, 50); final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true); - stats.updateCustomBucket(0, 315, true); - stats.updateCustomBucket(1, 316, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 7); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 63); + stats.updateCustomBucket(0, 315); + stats.updateCustomBucket(1, 316); final Parcel parcel = Parcel.obtain(); MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); @@ -243,8 +243,8 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); // Accumulate energy in one bucket and one custom bucket, the rest should be zero - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200, true); - stats.updateCustomBucket(1, 60, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 200); + stats.updateCustomBucket(1, 60); // Let's try parcelling with including zeros final Parcel includeZerosParcel = Parcel.obtain(); @@ -305,11 +305,11 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); final Parcel parcel = Parcel.obtain(); MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); @@ -331,14 +331,14 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats template = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - template.updateCustomBucket(0, 50, true); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + template.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + template.updateCustomBucket(0, 50); final MeasuredEnergyStats stats = MeasuredEnergyStats.createFromTemplate(template); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 0L, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 7L, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 0L); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 7L); final Parcel parcel = Parcel.obtain(); MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false); @@ -369,14 +369,14 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_DOZE, 30); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); - stats.updateCustomBucket(0, 3, true); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); + stats.updateCustomBucket(0, 3); assertEquals(15, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); assertEquals(ENERGY_DATA_UNAVAILABLE, @@ -409,10 +409,10 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_ENERGY_BUCKETS], 3); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); - stats.updateCustomBucket(2, 13, true); - stats.updateCustomBucket(1, 70, true); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); + stats.updateCustomBucket(2, 13); + stats.updateCustomBucket(1, 70); final long[] output = stats.getAccumulatedCustomBucketEnergies(); assertEquals(3, output.length); @@ -449,11 +449,11 @@ public class MeasuredEnergyStatsTest { final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5, true); - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true); - stats.updateCustomBucket(0, 50, true); - stats.updateCustomBucket(1, 60, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 10); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 5); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_OTHER, 40); + stats.updateCustomBucket(0, 50); + stats.updateCustomBucket(1, 60); MeasuredEnergyStats.resetIfNotNull(stats); // All energy should be reset to 0 @@ -471,10 +471,10 @@ public class MeasuredEnergyStatsTest { } // Values should increase as usual. - stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70, true); + stats.updateStandardBucket(ENERGY_BUCKET_SCREEN_ON, 70); assertEquals(70L, stats.getAccumulatedStandardBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); - stats.updateCustomBucket(1, 12, true); + stats.updateCustomBucket(1, 12); assertEquals(12L, stats.getAccumulatedCustomBucketEnergy(1)); } diff --git a/core/tests/devicestatetests/Android.bp b/core/tests/devicestatetests/Android.bp index 409b77bc399e..7748de57c2bc 100644 --- a/core/tests/devicestatetests/Android.bp +++ b/core/tests/devicestatetests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "FrameworksCoreDeviceStateManagerTests", // Include all test java files @@ -19,6 +28,7 @@ android_test { static_libs: [ "androidx.test.rules", "frameworks-base-testutils", + "mockito-target-minus-junit4", ], libs: ["android.test.runner"], platform_apis: true, diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.java new file mode 100644 index 000000000000..dcef0a7c316d --- /dev/null +++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateInfoTest.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 android.hardware.devicestate; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.os.Parcel; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Unit tests for {@link DeviceStateInfo}. + * <p/> + * Run with <code>atest DeviceStateInfoTest</code>. + */ +@RunWith(JUnit4.class) +@SmallTest +public final class DeviceStateInfoTest { + @Test + public void create() { + final int[] supportedStates = new int[] { 0, 1, 2 }; + final int baseState = 0; + final int currentState = 2; + + final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState); + assertNotNull(info.supportedStates); + assertEquals(supportedStates, info.supportedStates); + assertEquals(baseState, info.baseState); + assertEquals(currentState, info.currentState); + } + + @Test + public void equals() { + final int[] supportedStates = new int[] { 0, 1, 2 }; + final int baseState = 0; + final int currentState = 2; + + final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState); + assertTrue(info.equals(info)); + + final DeviceStateInfo sameInfo = new DeviceStateInfo(supportedStates, baseState, + currentState); + assertTrue(info.equals(sameInfo)); + + final DeviceStateInfo differentInfo = new DeviceStateInfo(new int[]{ 0, 2}, baseState, + currentState); + assertFalse(info.equals(differentInfo)); + } + + @Test + public void diff_sameObject() { + final int[] supportedStates = new int[] { 0, 1, 2 }; + final int baseState = 0; + final int currentState = 2; + + final DeviceStateInfo info = new DeviceStateInfo(supportedStates, baseState, currentState); + assertEquals(0, info.diff(info)); + } + + @Test + public void diff_differentSupportedStates() { + final DeviceStateInfo info = new DeviceStateInfo(new int[] { 1 }, 0, 0); + final DeviceStateInfo otherInfo = new DeviceStateInfo(new int[] { 2 }, 0, 0); + final int diff = info.diff(otherInfo); + assertTrue((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0); + assertFalse((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0); + assertFalse((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0); + } + + @Test + public void diff_differentNonOverrideState() { + final DeviceStateInfo info = new DeviceStateInfo(new int[] { 1 }, 1, 0); + final DeviceStateInfo otherInfo = new DeviceStateInfo(new int[] { 1 }, 2, 0); + final int diff = info.diff(otherInfo); + assertFalse((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0); + assertTrue((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0); + assertFalse((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0); + } + + @Test + public void diff_differentState() { + final DeviceStateInfo info = new DeviceStateInfo(new int[] { 1 }, 0, 1); + final DeviceStateInfo otherInfo = new DeviceStateInfo(new int[] { 1 }, 0, 2); + final int diff = info.diff(otherInfo); + assertFalse((diff & DeviceStateInfo.CHANGED_SUPPORTED_STATES) > 0); + assertFalse((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0); + assertTrue((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0); + } + + @Test + public void writeToParcel() { + final int[] supportedStates = new int[] { 0, 1, 2 }; + final int nonOverrideState = 0; + final int state = 2; + final DeviceStateInfo originalInfo = + new DeviceStateInfo(supportedStates, nonOverrideState, state); + + final Parcel parcel = Parcel.obtain(); + originalInfo.writeToParcel(parcel, 0 /* flags */); + parcel.setDataPosition(0); + + final DeviceStateInfo info = DeviceStateInfo.CREATOR.createFromParcel(parcel); + assertEquals(originalInfo, info); + } +} diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java index e7fdfb8c19e9..79b4d8bc5f1c 100644 --- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java +++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java @@ -16,11 +16,15 @@ package android.hardware.devicestate; -import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import android.annotation.Nullable; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.os.IBinder; import android.os.RemoteException; @@ -32,6 +36,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.mockito.Mockito; import java.util.ArrayList; import java.util.HashSet; @@ -58,42 +63,75 @@ public final class DeviceStateManagerGlobalTest { } @Test - public void registerListener() { - mService.setBaseState(DEFAULT_DEVICE_STATE); - - TestDeviceStateListener listener1 = new TestDeviceStateListener(); - TestDeviceStateListener listener2 = new TestDeviceStateListener(); + public void registerCallback() { + DeviceStateCallback callback1 = mock(DeviceStateCallback.class); + DeviceStateCallback callback2 = mock(DeviceStateCallback.class); - mDeviceStateManagerGlobal.registerDeviceStateListener(listener1, + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback1, ConcurrentUtils.DIRECT_EXECUTOR); - mDeviceStateManagerGlobal.registerDeviceStateListener(listener2, + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback2, ConcurrentUtils.DIRECT_EXECUTOR); - assertEquals(DEFAULT_DEVICE_STATE, listener1.getLastReportedState().intValue()); - assertEquals(DEFAULT_DEVICE_STATE, listener2.getLastReportedState().intValue()); + // Verify initial callbacks + verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedStates())); + verify(callback1).onBaseStateChanged(eq(mService.getBaseState())); + verify(callback1).onStateChanged(eq(mService.getMergedState())); + verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedStates())); + verify(callback2).onBaseStateChanged(eq(mService.getBaseState())); + verify(callback2).onStateChanged(eq(mService.getMergedState())); + + Mockito.reset(callback1); + Mockito.reset(callback2); + + // Change the supported states and verify callback + mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE }); + verify(callback1).onSupportedStatesChanged(eq(mService.getSupportedStates())); + verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedStates())); + mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE }); + + Mockito.reset(callback1); + Mockito.reset(callback2); + + // Change the base state and verify callback mService.setBaseState(OTHER_DEVICE_STATE); - assertEquals(OTHER_DEVICE_STATE, listener1.getLastReportedState().intValue()); - assertEquals(OTHER_DEVICE_STATE, listener2.getLastReportedState().intValue()); + verify(callback1).onBaseStateChanged(eq(mService.getBaseState())); + verify(callback1).onStateChanged(eq(mService.getMergedState())); + verify(callback2).onBaseStateChanged(eq(mService.getBaseState())); + verify(callback2).onStateChanged(eq(mService.getMergedState())); + + Mockito.reset(callback1); + Mockito.reset(callback2); + + // Change the requested state and verify callback + DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build(); + mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); + + verify(callback1).onStateChanged(eq(mService.getMergedState())); + verify(callback2).onStateChanged(eq(mService.getMergedState())); } @Test - public void unregisterListener() { - mService.setBaseState(DEFAULT_DEVICE_STATE); + public void unregisterCallback() { + DeviceStateCallback callback = mock(DeviceStateCallback.class); - TestDeviceStateListener listener = new TestDeviceStateListener(); - - mDeviceStateManagerGlobal.registerDeviceStateListener(listener, + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); - assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue()); - mDeviceStateManagerGlobal.unregisterDeviceStateListener(listener); + // Verify initial callbacks + verify(callback).onSupportedStatesChanged(eq(mService.getSupportedStates())); + verify(callback).onBaseStateChanged(eq(mService.getBaseState())); + verify(callback).onStateChanged(eq(mService.getMergedState())); + Mockito.reset(callback); + + mDeviceStateManagerGlobal.unregisterDeviceStateCallback(callback); + mService.setSupportedStates(new int[]{OTHER_DEVICE_STATE}); mService.setBaseState(OTHER_DEVICE_STATE); - assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue()); + verifyZeroInteractions(callback); } @Test - public void submittingRequestRegisteredCallback() { + public void submittingRequestRegistersCallback() { assertTrue(mService.mCallbacks.isEmpty()); DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build(); @@ -104,37 +142,22 @@ public final class DeviceStateManagerGlobalTest { @Test public void submitRequest() { - mService.setBaseState(DEFAULT_DEVICE_STATE); - - TestDeviceStateListener listener = new TestDeviceStateListener(); - mDeviceStateManagerGlobal.registerDeviceStateListener(listener, + DeviceStateCallback callback = mock(DeviceStateCallback.class); + mDeviceStateManagerGlobal.registerDeviceStateCallback(callback, ConcurrentUtils.DIRECT_EXECUTOR); - assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue()); + verify(callback).onStateChanged(eq(mService.getBaseState())); + Mockito.reset(callback); DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build(); mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */); - assertEquals(OTHER_DEVICE_STATE, listener.getLastReportedState().intValue()); + verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE)); + Mockito.reset(callback); mDeviceStateManagerGlobal.cancelRequest(request); - assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue()); - } - - private final class TestDeviceStateListener implements DeviceStateManager.DeviceStateListener { - @Nullable - private Integer mLastReportedDeviceState; - - @Override - public void onDeviceStateChanged(int deviceState) { - mLastReportedDeviceState = deviceState; - } - - @Nullable - public Integer getLastReportedState() { - return mLastReportedDeviceState; - } + verify(callback).onStateChanged(eq(mService.getBaseState())); } private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub { @@ -150,12 +173,34 @@ public final class DeviceStateManagerGlobalTest { } } + private int[] mSupportedStates = new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE }; private int mBaseState = DEFAULT_DEVICE_STATE; - private int mMergedState = DEFAULT_DEVICE_STATE; private ArrayList<Request> mRequests = new ArrayList<>(); private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>(); + private DeviceStateInfo getInfo() { + final int mergedState = mRequests.isEmpty() + ? mBaseState : mRequests.get(mRequests.size() - 1).state; + return new DeviceStateInfo(mSupportedStates, mBaseState, mergedState); + } + + private void notifyDeviceStateInfoChanged() { + final DeviceStateInfo info = getInfo(); + for (IDeviceStateManagerCallback callback : mCallbacks) { + try { + callback.onDeviceStateInfoChanged(info); + } catch (RemoteException e) { + // Do nothing. Should never happen. + } + } + } + + @Override + public DeviceStateInfo getDeviceStateInfo() { + return getInfo(); + } + @Override public void registerCallback(IDeviceStateManagerCallback callback) { if (mCallbacks.contains(callback)) { @@ -163,16 +208,6 @@ public final class DeviceStateManagerGlobalTest { } mCallbacks.add(callback); - try { - callback.onDeviceStateChanged(mMergedState); - } catch (RemoteException e) { - // Do nothing. Should never happen. - } - } - - @Override - public int[] getSupportedDeviceStates() { - return new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE }; } @Override @@ -190,7 +225,7 @@ public final class DeviceStateManagerGlobalTest { final Request request = new Request(token, state, flags); mRequests.add(request); - notifyStateChangedIfNeeded(); + notifyDeviceStateInfoChanged(); for (IDeviceStateManagerCallback callback : mCallbacks) { try { @@ -223,32 +258,29 @@ public final class DeviceStateManagerGlobalTest { // Do nothing. Should never happen. } } - notifyStateChangedIfNeeded(); + notifyDeviceStateInfoChanged(); + } + + public void setSupportedStates(int[] states) { + mSupportedStates = states; + notifyDeviceStateInfoChanged(); + } + + public int[] getSupportedStates() { + return mSupportedStates; } public void setBaseState(int state) { mBaseState = state; - notifyStateChangedIfNeeded(); + notifyDeviceStateInfoChanged(); } - private void notifyStateChangedIfNeeded() { - final int originalMergedState = mMergedState; - - if (!mRequests.isEmpty()) { - mMergedState = mRequests.get(mRequests.size() - 1).state; - } else { - mMergedState = mBaseState; - } + public int getBaseState() { + return mBaseState; + } - if (mMergedState != originalMergedState) { - for (IDeviceStateManagerCallback callback : mCallbacks) { - try { - callback.onDeviceStateChanged(mMergedState); - } catch (RemoteException e) { - // Do nothing. Should never happen. - } - } - } + public int getMergedState() { + return getInfo().currentState; } } } diff --git a/core/tests/featureflagtests/Android.bp b/core/tests/featureflagtests/Android.bp index 8730b7012c4a..d9f608ea34c4 100644 --- a/core/tests/featureflagtests/Android.bp +++ b/core/tests/featureflagtests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "FrameworksCoreFeatureFlagTests", // We only want this apk build for tests. diff --git a/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp index 4755e0ea5259..f49e05329f62 100644 --- a/core/tests/hdmitests/Android.bp +++ b/core/tests/hdmitests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "HdmiCecTests", // Include all test java files diff --git a/core/tests/hosttests/test-apps/AutoLocTestApp/Android.bp b/core/tests/hosttests/test-apps/AutoLocTestApp/Android.bp index 14424811d3be..b4efce2c822b 100644 --- a/core/tests/hosttests/test-apps/AutoLocTestApp/Android.bp +++ b/core/tests/hosttests/test-apps/AutoLocTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "AutoLocTestApp", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v1/Android.bp b/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v1/Android.bp index 438ed2533716..0e07ddca5edc 100644 --- a/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v1/Android.bp +++ b/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v1/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "AutoLocVersionedTestApp_v1", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v2/Android.bp b/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v2/Android.bp index 2ac72a13fee3..f40870004e92 100644 --- a/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v2/Android.bp +++ b/core/tests/hosttests/test-apps/AutoLocVersionedTestApp_v2/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "AutoLocVersionedTestApp_v2", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/ExternalLocAllPermsTestApp/Android.bp b/core/tests/hosttests/test-apps/ExternalLocAllPermsTestApp/Android.bp index e06e82e38ef4..964401d851af 100644 --- a/core/tests/hosttests/test-apps/ExternalLocAllPermsTestApp/Android.bp +++ b/core/tests/hosttests/test-apps/ExternalLocAllPermsTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ExternalLocAllPermsTestApp", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.bp b/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.bp index c48dcd525232..297ed57a6b34 100644 --- a/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.bp +++ b/core/tests/hosttests/test-apps/ExternalLocPermsFLTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ExternalLocPermFLTestApp", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/ExternalLocTestApp/Android.bp b/core/tests/hosttests/test-apps/ExternalLocTestApp/Android.bp index 5c1c15a4999f..cf8c7e1abcf3 100644 --- a/core/tests/hosttests/test-apps/ExternalLocTestApp/Android.bp +++ b/core/tests/hosttests/test-apps/ExternalLocTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ExternalLocTestApp", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v1/Android.bp b/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v1/Android.bp index 13f5066c7170..909bd7f9d0da 100644 --- a/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v1/Android.bp +++ b/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v1/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ExternalLocVersionedTestApp_v1", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v2/Android.bp b/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v2/Android.bp index e02ffb307ba2..c5f91924d243 100644 --- a/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v2/Android.bp +++ b/core/tests/hosttests/test-apps/ExternalLocVersionedTestApp_v2/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ExternalLocVersionedTestApp_v2", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.bp b/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.bp index de098005b40e..1545f4d0e5f9 100644 --- a/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.bp +++ b/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ExternalSharedPermsTestApp", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.bp b/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.bp index 435144f31edd..8690bdfc2f93 100644 --- a/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.bp +++ b/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ExternalSharedPermsBTTestApp", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.bp b/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.bp index 445bac94f10d..21b7c4c1ea77 100644 --- a/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.bp +++ b/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ExternalSharedPermsDiffKeyTestApp", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.bp b/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.bp index d1da3998ca4a..63bf8dca6968 100644 --- a/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.bp +++ b/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ExternalSharedPermsFLTestApp", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/InternalLocTestApp/Android.bp b/core/tests/hosttests/test-apps/InternalLocTestApp/Android.bp index fab9d4394fc5..f05cde430992 100644 --- a/core/tests/hosttests/test-apps/InternalLocTestApp/Android.bp +++ b/core/tests/hosttests/test-apps/InternalLocTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "InternalLocTestApp", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp index dcf168760d4d..56f10fe075ed 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "MultiDexLegacyTestServicesTests", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/NoLocTestApp/Android.bp b/core/tests/hosttests/test-apps/NoLocTestApp/Android.bp index 50a2de499d37..88e3722a5ed4 100644 --- a/core/tests/hosttests/test-apps/NoLocTestApp/Android.bp +++ b/core/tests/hosttests/test-apps/NoLocTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "NoLocTestApp", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v1/Android.bp b/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v1/Android.bp index 4bc9edcd9f20..fd5ab265d1e2 100644 --- a/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v1/Android.bp +++ b/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v1/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "NoLocVersionedTestApp_v1", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v2/Android.bp b/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v2/Android.bp index dd2952be00dd..fa821d84ace4 100644 --- a/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v2/Android.bp +++ b/core/tests/hosttests/test-apps/NoLocVersionedTestApp_v2/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "NoLocVersionedTestApp_v2", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/SharedUid/32/Android.bp b/core/tests/hosttests/test-apps/SharedUid/32/Android.bp index f3e3eded5fba..6f3d4cf9c027 100644 --- a/core/tests/hosttests/test-apps/SharedUid/32/Android.bp +++ b/core/tests/hosttests/test-apps/SharedUid/32/Android.bp @@ -19,6 +19,15 @@ // Build activity +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"], +} + android_test { name: "PMTest_Java32", srcs: ["**/*.java"], diff --git a/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp b/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp index 9e6c17f0247a..867d65cbe81c 100644 --- a/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp +++ b/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.bp @@ -17,6 +17,15 @@ // This makefile supplies the rules for building a library of JNI code for // use by our example of how to bundle a shared library with an APK. +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"], +} + cc_test_library { // This is the target being built. diff --git a/core/tests/hosttests/test-apps/SharedUid/64/Android.bp b/core/tests/hosttests/test-apps/SharedUid/64/Android.bp index 5d9c0b597ef1..6bb25a82b354 100644 --- a/core/tests/hosttests/test-apps/SharedUid/64/Android.bp +++ b/core/tests/hosttests/test-apps/SharedUid/64/Android.bp @@ -19,6 +19,15 @@ // Build activity +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"], +} + android_test { name: "PMTest_Java64", srcs: ["**/*.java"], diff --git a/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp b/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp index 91da6e44a8be..c032ba1503d1 100644 --- a/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp +++ b/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.bp @@ -17,6 +17,15 @@ // This Android.bp supplies the rules for building a library of JNI code for // use by our example of how to bundle a shared library with an APK. +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"], +} + cc_test_library { // This is the target being built. name: "libpmtest64", diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/Android.bp b/core/tests/hosttests/test-apps/SharedUid/dual/Android.bp index c42192dcdbf2..94dd0c6758c2 100644 --- a/core/tests/hosttests/test-apps/SharedUid/dual/Android.bp +++ b/core/tests/hosttests/test-apps/SharedUid/dual/Android.bp @@ -19,6 +19,15 @@ // Build activity +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"], +} + android_test { name: "PMTest_Java_dual", srcs: ["**/*.java"], diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp b/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp index 662755dd35b0..6b9d7f337fc8 100644 --- a/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp +++ b/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.bp @@ -17,6 +17,15 @@ // This Android.bp supplies the rules for building a library of JNI code for // use by our example of how to bundle a shared library with an APK. +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"], +} + cc_test_library { // This is the target being built. diff --git a/core/tests/hosttests/test-apps/SharedUid/java_only/Android.bp b/core/tests/hosttests/test-apps/SharedUid/java_only/Android.bp index baedc6e35734..6d632671e261 100644 --- a/core/tests/hosttests/test-apps/SharedUid/java_only/Android.bp +++ b/core/tests/hosttests/test-apps/SharedUid/java_only/Android.bp @@ -19,6 +19,15 @@ // Build activity +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"], +} + android_test { name: "PMTest_Java", srcs: ["**/*.java"], diff --git a/core/tests/hosttests/test-apps/SimpleTestApp/Android.bp b/core/tests/hosttests/test-apps/SimpleTestApp/Android.bp index 5f443bd83605..b022bdaa6016 100644 --- a/core/tests/hosttests/test-apps/SimpleTestApp/Android.bp +++ b/core/tests/hosttests/test-apps/SimpleTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "SimpleTestApp", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.bp b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.bp index 800e0833d35d..0ac825b3dd2e 100644 --- a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.bp +++ b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v1_ext/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "UpdateExtToIntLocTestApp_v1_ext", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.bp b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.bp index 299887c9fbfc..6f66a5104499 100644 --- a/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.bp +++ b/core/tests/hosttests/test-apps/UpdateExtToIntLocTestApp_v2_int/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "UpdateExtToIntLocTestApp_v2_int", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.bp b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.bp index 1530422841f6..d88bce04638b 100644 --- a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.bp +++ b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v1_ext/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "UpdateExternalLocTestApp_v1_ext", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.bp b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.bp index 4c7975e5f18a..d1240df8a3ce 100644 --- a/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.bp +++ b/core/tests/hosttests/test-apps/UpdateExternalLocTestApp_v2_none/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "UpdateExternalLocTestApp_v2_none", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.bp b/core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.bp index c6b60c352e38..751071cd947a 100644 --- a/core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.bp +++ b/core/tests/hosttests/test-apps/VersatileTestApp_Auto/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "VersatileTestApp_Auto", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_External/Android.bp b/core/tests/hosttests/test-apps/VersatileTestApp_External/Android.bp index db521efc5935..b9ed015b22eb 100644 --- a/core/tests/hosttests/test-apps/VersatileTestApp_External/Android.bp +++ b/core/tests/hosttests/test-apps/VersatileTestApp_External/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "VersatileTestApp_External", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.bp b/core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.bp index ca059302ff0d..7b725704651c 100644 --- a/core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.bp +++ b/core/tests/hosttests/test-apps/VersatileTestApp_Internal/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "VersatileTestApp_Internal", srcs: ["src/**/*.java"], diff --git a/core/tests/hosttests/test-apps/VersatileTestApp_None/Android.bp b/core/tests/hosttests/test-apps/VersatileTestApp_None/Android.bp index 6e1aac7d6c94..5f67bce361c5 100644 --- a/core/tests/hosttests/test-apps/VersatileTestApp_None/Android.bp +++ b/core/tests/hosttests/test-apps/VersatileTestApp_None/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "VersatileTestApp_None", srcs: ["src/**/*.java"], diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp index ae3ff8612eee..96811be37f5a 100644 --- a/core/tests/mockingcoretests/Android.bp +++ b/core/tests/mockingcoretests/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "FrameworksMockingCoreTests", diff --git a/core/tests/notificationtests/Android.bp b/core/tests/notificationtests/Android.bp index e744d5a2e631..1c0e39d9e022 100644 --- a/core/tests/notificationtests/Android.bp +++ b/core/tests/notificationtests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "NotificationStressTests", // Include all test java files. diff --git a/core/tests/overlaytests/device/Android.bp b/core/tests/overlaytests/device/Android.bp index f86ac9ce37e1..0d3b15a41e8c 100644 --- a/core/tests/overlaytests/device/Android.bp +++ b/core/tests/overlaytests/device/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "OverlayDeviceTests", srcs: ["src/**/*.java"], diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp index 847b4915530b..4ff59fa56a68 100644 --- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp +++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "OverlayDeviceTests_AppOverlayOne", sdk_version: "current", diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp index 7d5f82a71b44..1f5763ee60ae 100644 --- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp +++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "OverlayDeviceTests_AppOverlayTwo", sdk_version: "current", diff --git a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.bp b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.bp index 50dbc6f054d3..178a02009dda 100644 --- a/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.bp +++ b/core/tests/overlaytests/device/test-apps/FrameworkOverlay/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "OverlayDeviceTests_FrameworkOverlay", sdk_version: "current", diff --git a/core/tests/overlaytests/host/Android.bp b/core/tests/overlaytests/host/Android.bp index a2fcef56b780..e4c3fbec8afc 100644 --- a/core/tests/overlaytests/host/Android.bp +++ b/core/tests/overlaytests/host/Android.bp @@ -12,6 +12,15 @@ // 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_test_host { name: "OverlayHostTests", srcs: ["src/**/*.java"], diff --git a/core/tests/overlaytests/remount/Android.bp b/core/tests/overlaytests/remount/Android.bp index 939334c94312..0a6b88bcbb63 100644 --- a/core/tests/overlaytests/remount/Android.bp +++ b/core/tests/overlaytests/remount/Android.bp @@ -12,6 +12,15 @@ // 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_test_host { name: "OverlayRemountedTest", srcs: ["src/**/*.java"], diff --git a/core/tests/overlaytests/remount/test-apps/Overlay/Android.bp b/core/tests/overlaytests/remount/test-apps/Overlay/Android.bp index 032a0cdcb50d..a341c9aa88f7 100644 --- a/core/tests/overlaytests/remount/test-apps/Overlay/Android.bp +++ b/core/tests/overlaytests/remount/test-apps/Overlay/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "OverlayRemountedTest_Overlay", sdk_version: "current", @@ -24,4 +33,4 @@ android_test_helper_app { name: "OverlayRemountedTest_Overlay_SameCert", certificate: ":rro-remounted-test-a", sdk_version: "current", -}
\ No newline at end of file +} diff --git a/core/tests/overlaytests/remount/test-apps/SharedLibrary/Android.bp b/core/tests/overlaytests/remount/test-apps/SharedLibrary/Android.bp index ffb0572c3fd0..bc88d61df77a 100644 --- a/core/tests/overlaytests/remount/test-apps/SharedLibrary/Android.bp +++ b/core/tests/overlaytests/remount/test-apps/SharedLibrary/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "OverlayRemountedTest_SharedLibrary", sdk_version: "current", diff --git a/core/tests/overlaytests/remount/test-apps/SharedLibraryOverlay/Android.bp b/core/tests/overlaytests/remount/test-apps/SharedLibraryOverlay/Android.bp index 0d29aec909d5..88a05ab8427b 100644 --- a/core/tests/overlaytests/remount/test-apps/SharedLibraryOverlay/Android.bp +++ b/core/tests/overlaytests/remount/test-apps/SharedLibraryOverlay/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "OverlayRemountedTest_SharedLibraryOverlay", sdk_version: "current", diff --git a/core/tests/overlaytests/remount/test-apps/Target/Android.bp b/core/tests/overlaytests/remount/test-apps/Target/Android.bp index e4b4eaa8d220..869a75f5225d 100644 --- a/core/tests/overlaytests/remount/test-apps/Target/Android.bp +++ b/core/tests/overlaytests/remount/test-apps/Target/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "OverlayRemountedTest_Target", sdk_version: "test_current", diff --git a/core/tests/overlaytests/remount/test-apps/certs/Android.bp b/core/tests/overlaytests/remount/test-apps/certs/Android.bp index 06114efc1249..f5d89bcabdad 100644 --- a/core/tests/overlaytests/remount/test-apps/certs/Android.bp +++ b/core/tests/overlaytests/remount/test-apps/certs/Android.bp @@ -13,6 +13,17 @@ // limitations under the License. // development/tools/make_key rro-remounted-test-a '/CN=rro_test_a' +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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], +} + android_app_certificate { name: "rro-remounted-test-a", certificate: "rro-remounted-test-a", diff --git a/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp b/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp index e6ebd5ea76d8..3536c4088dd8 100644 --- a/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp +++ b/core/tests/overlaytests/remount/test-apps/overlaid_apex/Android.bp @@ -12,6 +12,17 @@ // 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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], +} + genrule { name: "com.android.overlaytest.overlaid.pem", out: ["com.android.overlaytest.overlaid.pem"], @@ -39,4 +50,5 @@ apex { key: "com.android.overlaytest.overlaid.key", apps: ["OverlayRemountedTest_Target"], installable: false, + updatable: false, } diff --git a/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp b/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp index 07f27ee55d39..f04140409bea 100644 --- a/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp +++ b/core/tests/overlaytests/remount/test-apps/overlay_apex/Android.bp @@ -12,6 +12,17 @@ // 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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], +} + genrule { name: "com.android.overlaytest.overlay.pem", out: ["com.android.overlaytest.overlay.pem"], @@ -39,4 +50,5 @@ apex { key: "com.android.overlaytest.overlay.key", apps: ["OverlayRemountedTest_Overlay"], installable: false, + updatable: false, } diff --git a/core/tests/packagemanagertests/Android.bp b/core/tests/packagemanagertests/Android.bp index 6f39af8bd468..5ce71c902c7c 100644 --- a/core/tests/packagemanagertests/Android.bp +++ b/core/tests/packagemanagertests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "FrameworksCorePackageManagerTests", // We only want this apk build for tests. diff --git a/core/tests/privacytests/Android.bp b/core/tests/privacytests/Android.bp index 7f5699287796..bc3dd810be8c 100644 --- a/core/tests/privacytests/Android.bp +++ b/core/tests/privacytests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "FrameworksPrivacyLibraryTests", srcs: ["src/**/*.java"], diff --git a/core/tests/screenshothelpertests/Android.bp b/core/tests/screenshothelpertests/Android.bp index 3d54b68b7ddc..37af99c58d42 100644 --- a/core/tests/screenshothelpertests/Android.bp +++ b/core/tests/screenshothelpertests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "ScreenshotHelperTests", @@ -25,4 +34,3 @@ android_test { certificate: "platform", } - diff --git a/core/tests/systemproperties/Android.bp b/core/tests/systemproperties/Android.bp index 7ef1b9b8f74b..765ca3e5110a 100644 --- a/core/tests/systemproperties/Android.bp +++ b/core/tests/systemproperties/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "FrameworksCoreSystemPropertiesTests", // Include all test java files. diff --git a/core/tests/utillib/Android.bp b/core/tests/utillib/Android.bp index 1f742c208ba2..d40d1d2bb6e2 100644 --- a/core/tests/utillib/Android.bp +++ b/core/tests/utillib/Android.bp @@ -12,6 +12,15 @@ // 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: "frameworks-core-util-lib", diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp index a9b9c41d4719..72d6a2c67739 100644 --- a/core/tests/utiltests/Android.bp +++ b/core/tests/utiltests/Android.bp @@ -2,6 +2,15 @@ // Build FrameworksUtilTests package //######################################################################## +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"], +} + android_test { name: "FrameworksUtilTests", diff --git a/core/tests/utiltests/jni/Android.bp b/core/tests/utiltests/jni/Android.bp index 6b75471a358a..a9c695ed09f3 100644 --- a/core/tests/utiltests/jni/Android.bp +++ b/core/tests/utiltests/jni/Android.bp @@ -11,6 +11,15 @@ // WITHOUT WARRANTIES 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"], +} + cc_library_shared {
name: "libmemoryintarraytest",
diff --git a/core/tests/uwbtests/Android.bp b/core/tests/uwbtests/Android.bp index 8ee86f470c9e..31f446f7a60d 100644 --- a/core/tests/uwbtests/Android.bp +++ b/core/tests/uwbtests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "UwbManagerTests", static_libs: [ diff --git a/core/tests/uwbtests/src/android/uwb/AngleMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/AngleMeasurementTest.java deleted file mode 100644 index 7769c28202f2..000000000000 --- a/core/tests/uwbtests/src/android/uwb/AngleMeasurementTest.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.uwb; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import android.os.Parcel; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Test of {@link AngleMeasurement}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class AngleMeasurementTest { - private static final double EPSILON = 0.00000000001; - - @Test - public void testBuilder() { - double radians = 0.1234; - double errorRadians = 0.5678; - double confidence = 0.5; - - AngleMeasurement.Builder builder = new AngleMeasurement.Builder(); - tryBuild(builder, false); - - builder.setRadians(radians); - tryBuild(builder, false); - - builder.setErrorRadians(errorRadians); - tryBuild(builder, false); - - builder.setConfidenceLevel(confidence); - AngleMeasurement measurement = tryBuild(builder, true); - - assertEquals(measurement.getRadians(), radians, 0); - assertEquals(measurement.getErrorRadians(), errorRadians, 0); - assertEquals(measurement.getConfidenceLevel(), confidence, 0); - } - - private AngleMeasurement tryBuild(AngleMeasurement.Builder builder, boolean expectSuccess) { - AngleMeasurement measurement = null; - try { - measurement = builder.build(); - if (!expectSuccess) { - fail("Expected AngleMeasurement.Builder.build() to fail, but it succeeded"); - } - } catch (IllegalStateException e) { - if (expectSuccess) { - fail("Expected AngleMeasurement.Builder.build() to succeed, but it failed"); - } - } - return measurement; - } - - @Test - public void testParcel() { - Parcel parcel = Parcel.obtain(); - AngleMeasurement measurement = UwbTestUtils.getAngleMeasurement(); - measurement.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - AngleMeasurement fromParcel = AngleMeasurement.CREATOR.createFromParcel(parcel); - assertEquals(measurement, fromParcel); - } -} diff --git a/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java deleted file mode 100644 index 9394dec7f46f..000000000000 --- a/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java +++ /dev/null @@ -1,89 +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.uwb; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import android.os.Parcel; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Test of {@link AngleOfArrivalMeasurement}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class AngleOfArrivalMeasurementTest { - - @Test - public void testBuilder() { - AngleMeasurement azimuth = UwbTestUtils.getAngleMeasurement(); - AngleMeasurement altitude = UwbTestUtils.getAngleMeasurement(); - - AngleOfArrivalMeasurement.Builder builder = new AngleOfArrivalMeasurement.Builder(); - tryBuild(builder, false); - - builder.setAltitude(altitude); - tryBuild(builder, false); - - builder.setAzimuth(azimuth); - AngleOfArrivalMeasurement measurement = tryBuild(builder, true); - - assertEquals(azimuth, measurement.getAzimuth()); - assertEquals(altitude, measurement.getAltitude()); - } - - private AngleMeasurement getAngleMeasurement(double radian, double error, double confidence) { - return new AngleMeasurement.Builder() - .setRadians(radian) - .setErrorRadians(error) - .setConfidenceLevel(confidence) - .build(); - } - - private AngleOfArrivalMeasurement tryBuild(AngleOfArrivalMeasurement.Builder builder, - boolean expectSuccess) { - AngleOfArrivalMeasurement measurement = null; - try { - measurement = builder.build(); - if (!expectSuccess) { - fail("Expected AngleOfArrivalMeasurement.Builder.build() to fail"); - } - } catch (IllegalStateException e) { - if (expectSuccess) { - fail("Expected AngleOfArrivalMeasurement.Builder.build() to succeed"); - } - } - return measurement; - } - - @Test - public void testParcel() { - Parcel parcel = Parcel.obtain(); - AngleOfArrivalMeasurement measurement = UwbTestUtils.getAngleOfArrivalMeasurement(); - measurement.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - AngleOfArrivalMeasurement fromParcel = - AngleOfArrivalMeasurement.CREATOR.createFromParcel(parcel); - assertEquals(measurement, fromParcel); - } -} diff --git a/core/tests/uwbtests/src/android/uwb/DistanceMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/DistanceMeasurementTest.java deleted file mode 100644 index 439c884723be..000000000000 --- a/core/tests/uwbtests/src/android/uwb/DistanceMeasurementTest.java +++ /dev/null @@ -1,87 +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.uwb; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import android.os.Parcel; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Test of {@link DistanceMeasurement}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class DistanceMeasurementTest { - private static final double EPSILON = 0.00000000001; - - @Test - public void testBuilder() { - double meters = 0.12; - double error = 0.54; - double confidence = 0.99; - - DistanceMeasurement.Builder builder = new DistanceMeasurement.Builder(); - tryBuild(builder, false); - - builder.setMeters(meters); - tryBuild(builder, false); - - builder.setErrorMeters(error); - tryBuild(builder, false); - - builder.setConfidenceLevel(confidence); - DistanceMeasurement measurement = tryBuild(builder, true); - - assertEquals(meters, measurement.getMeters(), 0); - assertEquals(error, measurement.getErrorMeters(), 0); - assertEquals(confidence, measurement.getConfidenceLevel(), 0); - } - - private DistanceMeasurement tryBuild(DistanceMeasurement.Builder builder, - boolean expectSuccess) { - DistanceMeasurement measurement = null; - try { - measurement = builder.build(); - if (!expectSuccess) { - fail("Expected DistanceMeasurement.Builder.build() to fail"); - } - } catch (IllegalStateException e) { - if (expectSuccess) { - fail("Expected DistanceMeasurement.Builder.build() to succeed"); - } - } - return measurement; - } - - @Test - public void testParcel() { - Parcel parcel = Parcel.obtain(); - DistanceMeasurement measurement = UwbTestUtils.getDistanceMeasurement(); - measurement.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - DistanceMeasurement fromParcel = - DistanceMeasurement.CREATOR.createFromParcel(parcel); - assertEquals(measurement, fromParcel); - } -} diff --git a/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java deleted file mode 100644 index edd4d08992ba..000000000000 --- a/core/tests/uwbtests/src/android/uwb/RangingMeasurementTest.java +++ /dev/null @@ -1,95 +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.uwb; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import android.os.Parcel; -import android.os.SystemClock; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Test of {@link RangingMeasurement}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class RangingMeasurementTest { - - @Test - public void testBuilder() { - int status = RangingMeasurement.RANGING_STATUS_SUCCESS; - UwbAddress address = UwbTestUtils.getUwbAddress(false); - long time = SystemClock.elapsedRealtimeNanos(); - AngleOfArrivalMeasurement angleMeasurement = UwbTestUtils.getAngleOfArrivalMeasurement(); - DistanceMeasurement distanceMeasurement = UwbTestUtils.getDistanceMeasurement(); - - RangingMeasurement.Builder builder = new RangingMeasurement.Builder(); - - builder.setStatus(status); - tryBuild(builder, false); - - builder.setElapsedRealtimeNanos(time); - tryBuild(builder, false); - - builder.setAngleOfArrivalMeasurement(angleMeasurement); - tryBuild(builder, false); - - builder.setDistanceMeasurement(distanceMeasurement); - tryBuild(builder, false); - - builder.setRemoteDeviceAddress(address); - RangingMeasurement measurement = tryBuild(builder, true); - - assertEquals(status, measurement.getStatus()); - assertEquals(address, measurement.getRemoteDeviceAddress()); - assertEquals(time, measurement.getElapsedRealtimeNanos()); - assertEquals(angleMeasurement, measurement.getAngleOfArrivalMeasurement()); - assertEquals(distanceMeasurement, measurement.getDistanceMeasurement()); - } - - private RangingMeasurement tryBuild(RangingMeasurement.Builder builder, - boolean expectSuccess) { - RangingMeasurement measurement = null; - try { - measurement = builder.build(); - if (!expectSuccess) { - fail("Expected RangingMeasurement.Builder.build() to fail"); - } - } catch (IllegalStateException e) { - if (expectSuccess) { - fail("Expected DistanceMeasurement.Builder.build() to succeed"); - } - } - return measurement; - } - - @Test - public void testParcel() { - Parcel parcel = Parcel.obtain(); - RangingMeasurement measurement = UwbTestUtils.getRangingMeasurement(); - measurement.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - RangingMeasurement fromParcel = RangingMeasurement.CREATOR.createFromParcel(parcel); - assertEquals(measurement, fromParcel); - } -} diff --git a/core/tests/uwbtests/src/android/uwb/RangingReportTest.java b/core/tests/uwbtests/src/android/uwb/RangingReportTest.java deleted file mode 100644 index 64c48ba4b6f4..000000000000 --- a/core/tests/uwbtests/src/android/uwb/RangingReportTest.java +++ /dev/null @@ -1,90 +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.uwb; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import android.os.Parcel; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.List; - -/** - * Test of {@link RangingReport}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class RangingReportTest { - - @Test - public void testBuilder() { - List<RangingMeasurement> measurements = UwbTestUtils.getRangingMeasurements(5); - - RangingReport.Builder builder = new RangingReport.Builder(); - builder.addMeasurements(measurements); - RangingReport report = tryBuild(builder, true); - verifyMeasurementsEqual(measurements, report.getMeasurements()); - - - builder = new RangingReport.Builder(); - for (RangingMeasurement measurement : measurements) { - builder.addMeasurement(measurement); - } - report = tryBuild(builder, true); - verifyMeasurementsEqual(measurements, report.getMeasurements()); - } - - private void verifyMeasurementsEqual(List<RangingMeasurement> expected, - List<RangingMeasurement> actual) { - assertEquals(expected.size(), actual.size()); - for (int i = 0; i < expected.size(); i++) { - assertEquals(expected.get(i), actual.get(i)); - } - } - - private RangingReport tryBuild(RangingReport.Builder builder, - boolean expectSuccess) { - RangingReport report = null; - try { - report = builder.build(); - if (!expectSuccess) { - fail("Expected RangingReport.Builder.build() to fail"); - } - } catch (IllegalStateException e) { - if (expectSuccess) { - fail("Expected RangingReport.Builder.build() to succeed"); - } - } - return report; - } - - @Test - public void testParcel() { - Parcel parcel = Parcel.obtain(); - RangingReport report = UwbTestUtils.getRangingReports(5); - report.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - RangingReport fromParcel = RangingReport.CREATOR.createFromParcel(parcel); - assertEquals(report, fromParcel); - } -} diff --git a/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java b/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java deleted file mode 100644 index e5eea26f5d11..000000000000 --- a/core/tests/uwbtests/src/android/uwb/RangingSessionTest.java +++ /dev/null @@ -1,378 +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.uwb; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.os.PersistableBundle; -import android.os.RemoteException; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.util.concurrent.Executor; - -/** - * Test of {@link RangingSession}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class RangingSessionTest { - private static final Executor EXECUTOR = UwbTestUtils.getExecutor(); - private static final PersistableBundle PARAMS = new PersistableBundle(); - private static final @RangingSession.Callback.Reason int REASON = - RangingSession.Callback.REASON_GENERIC_ERROR; - - @Test - public void testOnRangingOpened_OnOpenSuccessCalled() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - verifyOpenState(session, false); - - session.onRangingOpened(); - verifyOpenState(session, true); - - // Verify that the onOpenSuccess callback was invoked - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(0)).onClosed(anyInt(), any()); - } - - @Test - public void testOnRangingOpened_CannotOpenClosedSession() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - - session.onRangingOpened(); - verifyOpenState(session, true); - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(0)).onClosed(anyInt(), any()); - - session.onRangingClosed(REASON, PARAMS); - verifyOpenState(session, false); - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(1)).onClosed(anyInt(), any()); - - // Now invoke the ranging started callback and ensure the session remains closed - session.onRangingOpened(); - verifyOpenState(session, false); - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(1)).onClosed(anyInt(), any()); - } - - @Test - public void testOnRangingClosed_OnClosedCalledWhenSessionNotOpen() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - verifyOpenState(session, false); - - session.onRangingClosed(REASON, PARAMS); - verifyOpenState(session, false); - - // Verify that the onOpenSuccess callback was invoked - verify(callback, times(0)).onOpened(eq(session)); - verify(callback, times(1)).onClosed(anyInt(), any()); - } - - @Test - public void testOnRangingClosed_OnClosedCalled() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - session.onRangingStarted(PARAMS); - session.onRangingClosed(REASON, PARAMS); - verify(callback, times(1)).onClosed(anyInt(), any()); - - verifyOpenState(session, false); - session.onRangingClosed(REASON, PARAMS); - verify(callback, times(2)).onClosed(anyInt(), any()); - } - - @Test - public void testOnRangingResult_OnReportReceivedCalled() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - verifyOpenState(session, false); - - session.onRangingStarted(PARAMS); - verifyOpenState(session, true); - - RangingReport report = UwbTestUtils.getRangingReports(1); - session.onRangingResult(report); - verify(callback, times(1)).onReportReceived(eq(report)); - } - - @Test - public void testStart_CannotStartIfAlreadyStarted() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); - session.onRangingOpened(); - - session.start(PARAMS); - verify(callback, times(1)).onStarted(any()); - - // Calling start again should throw an illegal state - verifyThrowIllegalState(() -> session.start(PARAMS)); - verify(callback, times(1)).onStarted(any()); - } - - @Test - public void testStop_CannotStopIfAlreadyStopped() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); - doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any()); - session.onRangingOpened(); - session.start(PARAMS); - - verifyNoThrowIllegalState(session::stop); - verify(callback, times(1)).onStopped(); - - // Calling stop again should throw an illegal state - verifyThrowIllegalState(session::stop); - verify(callback, times(1)).onStopped(); - } - - @Test - public void testReconfigure_OnlyWhenOpened() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); - doAnswer(new ReconfigureAnswer(session)).when(adapter).reconfigureRanging(any(), any()); - - verifyThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(0)).onReconfigured(any()); - verifyOpenState(session, false); - - session.onRangingOpened(); - verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(1)).onReconfigured(any()); - verifyOpenState(session, true); - - session.onRangingStarted(PARAMS); - verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(2)).onReconfigured(any()); - verifyOpenState(session, true); - - session.onRangingStopped(); - verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(3)).onReconfigured(any()); - verifyOpenState(session, true); - - - session.onRangingClosed(REASON, PARAMS); - verifyThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(3)).onReconfigured(any()); - verifyOpenState(session, false); - } - - @Test - public void testClose_NoCallbackUntilInvoked() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - session.onRangingOpened(); - - // Calling close multiple times should invoke closeRanging until the session receives - // the onClosed callback. - int totalCallsBeforeOnRangingClosed = 3; - for (int i = 1; i <= totalCallsBeforeOnRangingClosed; i++) { - session.close(); - verifyOpenState(session, true); - verify(adapter, times(i)).closeRanging(handle); - verify(callback, times(0)).onClosed(anyInt(), any()); - } - - // After onClosed is invoked, then the adapter should no longer be called for each call to - // the session's close. - final int totalCallsAfterOnRangingClosed = 2; - for (int i = 1; i <= totalCallsAfterOnRangingClosed; i++) { - session.onRangingClosed(REASON, PARAMS); - verifyOpenState(session, false); - verify(adapter, times(totalCallsBeforeOnRangingClosed)).closeRanging(handle); - verify(callback, times(i)).onClosed(anyInt(), any()); - } - } - - @Test - public void testClose_OnClosedCalled() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any()); - session.onRangingOpened(); - - session.close(); - verify(callback, times(1)).onClosed(anyInt(), any()); - } - - @Test - public void testClose_CannotInteractFurther() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any()); - session.close(); - - verifyThrowIllegalState(() -> session.start(PARAMS)); - verifyThrowIllegalState(() -> session.reconfigure(PARAMS)); - verifyThrowIllegalState(() -> session.stop()); - verifyNoThrowIllegalState(() -> session.close()); - } - - @Test - public void testOnRangingResult_OnReportReceivedCalledWhenOpen() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - - assertFalse(session.isOpen()); - session.onRangingStarted(PARAMS); - assertTrue(session.isOpen()); - - // Verify that the onReportReceived callback was invoked - RangingReport report = UwbTestUtils.getRangingReports(1); - session.onRangingResult(report); - verify(callback, times(1)).onReportReceived(report); - } - - @Test - public void testOnRangingResult_OnReportReceivedNotCalledWhenNotOpen() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - - assertFalse(session.isOpen()); - - // Verify that the onReportReceived callback was invoked - RangingReport report = UwbTestUtils.getRangingReports(1); - session.onRangingResult(report); - verify(callback, times(0)).onReportReceived(report); - } - - private void verifyOpenState(RangingSession session, boolean expected) { - assertEquals(expected, session.isOpen()); - } - - private void verifyThrowIllegalState(Runnable runnable) { - try { - runnable.run(); - fail(); - } catch (IllegalStateException e) { - // Pass - } - } - - private void verifyNoThrowIllegalState(Runnable runnable) { - try { - runnable.run(); - } catch (IllegalStateException e) { - fail(); - } - } - - abstract class AdapterAnswer implements Answer { - protected RangingSession mSession; - - protected AdapterAnswer(RangingSession session) { - mSession = session; - } - } - - class StartAnswer extends AdapterAnswer { - StartAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - mSession.onRangingStarted(PARAMS); - return null; - } - } - - class ReconfigureAnswer extends AdapterAnswer { - ReconfigureAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - mSession.onRangingReconfigured(PARAMS); - return null; - } - } - - class StopAnswer extends AdapterAnswer { - StopAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - mSession.onRangingStopped(); - return null; - } - } - - class CloseAnswer extends AdapterAnswer { - CloseAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - mSession.onRangingClosed(REASON, PARAMS); - return null; - } - } -} diff --git a/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java b/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java deleted file mode 100644 index 8b42ff7f62a7..000000000000 --- a/core/tests/uwbtests/src/android/uwb/SessionHandleTest.java +++ /dev/null @@ -1,52 +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.uwb; - -import static org.junit.Assert.assertEquals; - -import android.os.Parcel; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Test of {@link SessionHandle}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class SessionHandleTest { - - @Test - public void testBasic() { - int handleId = 12; - SessionHandle handle = new SessionHandle(handleId); - assertEquals(handle.getId(), handleId); - } - - @Test - public void testParcel() { - Parcel parcel = Parcel.obtain(); - SessionHandle handle = new SessionHandle(10); - handle.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - SessionHandle fromParcel = SessionHandle.CREATOR.createFromParcel(parcel); - assertEquals(handle, fromParcel); - } -} diff --git a/core/tests/uwbtests/src/android/uwb/UwbAddressTest.java b/core/tests/uwbtests/src/android/uwb/UwbAddressTest.java deleted file mode 100644 index ccc88a9a5399..000000000000 --- a/core/tests/uwbtests/src/android/uwb/UwbAddressTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.uwb; - -import static org.junit.Assert.assertEquals; - -import android.os.Parcel; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Test of {@link UwbAddress}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class UwbAddressTest { - - @Test - public void testFromBytes_Short() { - runFromBytes(UwbAddress.SHORT_ADDRESS_BYTE_LENGTH); - } - - @Test - public void testFromBytes_Extended() { - runFromBytes(UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH); - } - - private void runFromBytes(int len) { - byte[] addressBytes = getByteArray(len); - UwbAddress address = UwbAddress.fromBytes(addressBytes); - assertEquals(address.size(), len); - assertEquals(addressBytes, address.toBytes()); - } - - private byte[] getByteArray(int len) { - byte[] res = new byte[len]; - for (int i = 0; i < len; i++) { - res[i] = (byte) i; - } - return res; - } - - @Test - public void testParcel_Short() { - runParcel(true); - } - - @Test - public void testParcel_Extended() { - runParcel(false); - } - - private void runParcel(boolean useShortAddress) { - Parcel parcel = Parcel.obtain(); - UwbAddress address = UwbTestUtils.getUwbAddress(useShortAddress); - address.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - UwbAddress fromParcel = UwbAddress.CREATOR.createFromParcel(parcel); - assertEquals(address, fromParcel); - } -} diff --git a/core/xsd/Android.bp b/core/xsd/Android.bp index 738f33076ac9..5387f85bffb2 100644 --- a/core/xsd/Android.bp +++ b/core/xsd/Android.bp @@ -1,3 +1,12 @@ +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"], +} + xsd_config { name: "permission", srcs: ["permission.xsd"], diff --git a/core/xsd/vts/Android.bp b/core/xsd/vts/Android.bp index ca655f18149c..5d8407f584bc 100644 --- a/core/xsd/vts/Android.bp +++ b/core/xsd/vts/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + cc_test { name: "vts_permission_validate_test", srcs: [ diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 201f649cde52..85b60f8cc4b0 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -15,6 +15,15 @@ // Sysconfig 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: "framework-sysconfig.xml", sub_dir: "sysconfig", diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index 41a6fea4f9de..2f41b9084f40 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -16,6 +16,15 @@ // 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_android.car.cluster.loggingrenderer", sub_dir: "permissions", @@ -164,3 +173,10 @@ prebuilt_etc { 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, +} diff --git a/packages/SystemUI/res/drawable/toast_background.xml b/data/etc/car/com.android.car.activityresolver.xml index 5c45e8346e3c..d48bc15b1678 100644 --- a/packages/SystemUI/res/drawable/toast_background.xml +++ b/data/etc/car/com.android.car.activityresolver.xml @@ -14,8 +14,8 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <solid android:color="#FFFFFFFF" /> - <corners android:radius="@dimen/toast_bg_radius" /> -</shape> +<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 index 432a838a90e8..c3642d886180 100644 --- a/data/etc/car/com.android.car.bugreport.xml +++ b/data/etc/car/com.android.car.bugreport.xml @@ -20,5 +20,6 @@ <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"/> </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 index 489ce1b47ffa..cdeb8e48a59b 100644 --- a/data/etc/car/com.google.android.car.networking.preferenceupdater.xml +++ b/data/etc/car/com.google.android.car.networking.preferenceupdater.xml @@ -16,12 +16,21 @@ --> <permissions> <privapp-permissions package="com.google.android.car.networking.preferenceupdater"> - <permission name="android.permission.ACCESS_NETWORK_STATE"/> + <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.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> + <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/com.android.emergency.xml b/data/etc/com.android.emergency.xml index fa92b6da9460..2d6ae2ebfb0a 100644 --- a/data/etc/com.android.emergency.xml +++ b/data/etc/com.android.emergency.xml @@ -21,5 +21,7 @@ <permission name="android.permission.MANAGE_USERS"/> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/> + <!-- Required to update emergency gesture settings --> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> </privapp-permissions> </permissions> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 8fd5d804adc0..3900d7e674ca 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -406,8 +406,6 @@ applications that come with the platform <permission name="android.permission.SET_WALLPAPER" /> <permission name="android.permission.SET_WALLPAPER_COMPONENT" /> <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" /> - <!-- Permissions required for Incremental CTS tests --> - <permission name="com.android.permission.USE_INSTALLER_V2"/> <permission name="android.permission.LOADER_USAGE_STATS"/> <!-- Permission required to test system only camera devices. --> <permission name="android.permission.SYSTEM_CAMERA" /> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 84da930aac54..cabfad44cfb9 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -259,6 +259,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1834214907": { + "message": "createNonAppWindowAnimations()", + "level": "DEBUG", + "group": "WM_DEBUG_REMOTE_ANIMATIONS", + "at": "com\/android\/server\/wm\/RemoteAnimationController.java" + }, "-1824578273": { "message": "Reporting new frame to %s: %s", "level": "VERBOSE", @@ -811,6 +817,18 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, + "-1153814764": { + "message": "onAnimationCancelled", + "level": "DEBUG", + "group": "WM_DEBUG_REMOTE_ANIMATIONS", + "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java" + }, + "-1144293044": { + "message": "SURFACE SET FREEZE LAYER: %s", + "level": "INFO", + "group": "WM_SHOW_TRANSACTIONS", + "at": "com\/android\/server\/wm\/WindowStateAnimator.java" + }, "-1142279614": { "message": "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s", "level": "VERBOSE", @@ -1303,6 +1321,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" }, + "-567946587": { + "message": "Requested redraw for orientation change: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/WindowState.java" + }, "-561092364": { "message": "onPointerDownOutsideFocusLocked called on %s", "level": "INFO", diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp index ee5c5b0591cc..f90a74d939f4 100644 --- a/data/fonts/Android.bp +++ b/data/fonts/Android.bp @@ -12,6 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_base_data_fonts_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_data_fonts_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + prebuilt_font { name: "DroidSansMono.ttf", src: "DroidSansMono.ttf", diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 2db4c5d6bf2a..4f188cc03282 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -223,7 +223,7 @@ <alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" /> <family name="serif"> - <font weight="400" style="normal">NotoSerif-Regular.ttf</font> + <font weight="400" style="normal">NotoSerif.ttf</font> <font weight="700" style="normal">NotoSerif-Bold.ttf</font> <font weight="400" style="italic">NotoSerif-Italic.ttf</font> <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font> @@ -275,144 +275,144 @@ <!-- fallback fonts --> <family lang="und-Arab" variant="elegant"> - <font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font> + <font weight="400" style="normal">NotoNaskhArabic.ttf</font> <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font> </family> <family lang="und-Arab" variant="compact"> - <font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font> + <font weight="400" style="normal">NotoNaskhArabicUI.ttf</font> <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font> </family> <family lang="und-Ethi"> - <font weight="400" style="normal">NotoSansEthiopic-VF.ttf + <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansEthiopic-VF.ttf + <font weight="500" style="normal">NotoSansEthiopic-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansEthiopic-VF.ttf + <font weight="600" style="normal">NotoSansEthiopic-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansEthiopic-VF.ttf + <font weight="700" style="normal">NotoSansEthiopic-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Hebr"> - <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font> + <font weight="400" style="normal">NotoSansHebrew.ttf</font> <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font> <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font> <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font> </family> <family lang="und-Thai" variant="elegant"> - <font weight="400" style="normal">NotoSansThai-Regular.ttf</font> + <font weight="400" style="normal">NotoSansThai.ttf</font> <font weight="700" style="normal">NotoSansThai-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifThai-Regular.ttf</font> + <font weight="400" style="normal" fallbackFor="serif">NotoSerifThai.ttf</font> <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font> </family> <family lang="und-Thai" variant="compact"> - <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font> + <font weight="400" style="normal">NotoSansThaiUI.ttf</font> <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font> </family> <family lang="und-Armn"> - <font weight="400" style="normal">NotoSansArmenian-VF.ttf + <font weight="400" style="normal">NotoSansArmenian-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansArmenian-VF.ttf + <font weight="500" style="normal">NotoSansArmenian-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansArmenian-VF.ttf + <font weight="600" style="normal">NotoSansArmenian-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansArmenian-VF.ttf + <font weight="700" style="normal">NotoSansArmenian-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Geor,und-Geok"> - <font weight="400" style="normal">NotoSansGeorgian-VF.ttf + <font weight="400" style="normal">NotoSansGeorgian-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansGeorgian-VF.ttf + <font weight="500" style="normal">NotoSansGeorgian-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansGeorgian-VF.ttf + <font weight="600" style="normal">NotoSansGeorgian-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansGeorgian-VF.ttf + <font weight="700" style="normal">NotoSansGeorgian-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifGeorgian-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifGeorgian-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Deva" variant="elegant"> - <font weight="400" style="normal">NotoSansDevanagari-VF.ttf + <font weight="400" style="normal">NotoSansDevanagari-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansDevanagari-VF.ttf + <font weight="500" style="normal">NotoSansDevanagari-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansDevanagari-VF.ttf + <font weight="600" style="normal">NotoSansDevanagari-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansDevanagari-VF.ttf + <font weight="700" style="normal">NotoSansDevanagari-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifDevanagari-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifDevanagari-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Deva" variant="compact"> - <font weight="400" style="normal">NotoSansDevanagariUI-VF.ttf + <font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansDevanagariUI-VF.ttf + <font weight="500" style="normal">NotoSansDevanagariUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansDevanagariUI-VF.ttf + <font weight="600" style="normal">NotoSansDevanagariUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansDevanagariUI-VF.ttf + <font weight="700" style="normal">NotoSansDevanagariUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> @@ -421,347 +421,347 @@ danda characters. --> <family lang="und-Gujr" variant="elegant"> - <font weight="400" style="normal">NotoSansGujarati-Regular.ttf</font> + <font weight="400" style="normal">NotoSansGujarati.ttf</font> <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifGujarati-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifGujarati-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Gujr" variant="compact"> - <font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font> + <font weight="400" style="normal">NotoSansGujaratiUI.ttf</font> <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font> </family> <family lang="und-Guru" variant="elegant"> - <font weight="400" style="normal">NotoSansGurmukhi-VF.ttf + <font weight="400" style="normal">NotoSansGurmukhi-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansGurmukhi-VF.ttf + <font weight="500" style="normal">NotoSansGurmukhi-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansGurmukhi-VF.ttf + <font weight="600" style="normal">NotoSansGurmukhi-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansGurmukhi-VF.ttf + <font weight="700" style="normal">NotoSansGurmukhi-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifGurmukhi-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Guru" variant="compact"> - <font weight="400" style="normal">NotoSansGurmukhiUI-VF.ttf + <font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansGurmukhiUI-VF.ttf + <font weight="500" style="normal">NotoSansGurmukhiUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansGurmukhiUI-VF.ttf + <font weight="600" style="normal">NotoSansGurmukhiUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansGurmukhiUI-VF.ttf + <font weight="700" style="normal">NotoSansGurmukhiUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Taml" variant="elegant"> - <font weight="400" style="normal">NotoSansTamil-VF.ttf + <font weight="400" style="normal">NotoSansTamil-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansTamil-VF.ttf + <font weight="500" style="normal">NotoSansTamil-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansTamil-VF.ttf + <font weight="600" style="normal">NotoSansTamil-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansTamil-VF.ttf + <font weight="700" style="normal">NotoSansTamil-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifTamil-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifTamil-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Taml" variant="compact"> - <font weight="400" style="normal">NotoSansTamilUI-VF.ttf + <font weight="400" style="normal">NotoSansTamilUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansTamilUI-VF.ttf + <font weight="500" style="normal">NotoSansTamilUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansTamilUI-VF.ttf + <font weight="600" style="normal">NotoSansTamilUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansTamilUI-VF.ttf + <font weight="700" style="normal">NotoSansTamilUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Mlym" variant="elegant"> - <font weight="400" style="normal">NotoSansMalayalam-VF.ttf + <font weight="400" style="normal">NotoSansMalayalam-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansMalayalam-VF.ttf + <font weight="500" style="normal">NotoSansMalayalam-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansMalayalam-VF.ttf + <font weight="600" style="normal">NotoSansMalayalam-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansMalayalam-VF.ttf + <font weight="700" style="normal">NotoSansMalayalam-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifMalayalam-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifMalayalam-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Mlym" variant="compact"> - <font weight="400" style="normal">NotoSansMalayalamUI-VF.ttf + <font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansMalayalamUI-VF.ttf + <font weight="500" style="normal">NotoSansMalayalamUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansMalayalamUI-VF.ttf + <font weight="600" style="normal">NotoSansMalayalamUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansMalayalamUI-VF.ttf + <font weight="700" style="normal">NotoSansMalayalamUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Beng" variant="elegant"> - <font weight="400" style="normal">NotoSansBengali-VF.ttf + <font weight="400" style="normal">NotoSansBengali-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansBengali-VF.ttf + <font weight="500" style="normal">NotoSansBengali-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansBengali-VF.ttf + <font weight="600" style="normal">NotoSansBengali-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansBengali-VF.ttf + <font weight="700" style="normal">NotoSansBengali-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifBengali-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifBengali-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Beng" variant="compact"> - <font weight="400" style="normal">NotoSansBengaliUI-VF.ttf + <font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansBengaliUI-VF.ttf + <font weight="500" style="normal">NotoSansBengaliUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansBengaliUI-VF.ttf + <font weight="600" style="normal">NotoSansBengaliUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansBengaliUI-VF.ttf + <font weight="700" style="normal">NotoSansBengaliUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Telu" variant="elegant"> - <font weight="400" style="normal">NotoSansTelugu-VF.ttf + <font weight="400" style="normal">NotoSansTelugu-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansTelugu-VF.ttf + <font weight="500" style="normal">NotoSansTelugu-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansTelugu-VF.ttf + <font weight="600" style="normal">NotoSansTelugu-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansTelugu-VF.ttf + <font weight="700" style="normal">NotoSansTelugu-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifTelugu-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifTelugu-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Telu" variant="compact"> - <font weight="400" style="normal">NotoSansTeluguUI-VF.ttf + <font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansTeluguUI-VF.ttf + <font weight="500" style="normal">NotoSansTeluguUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansTeluguUI-VF.ttf + <font weight="600" style="normal">NotoSansTeluguUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansTeluguUI-VF.ttf + <font weight="700" style="normal">NotoSansTeluguUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Knda" variant="elegant"> - <font weight="400" style="normal">NotoSansKannada-VF.ttf + <font weight="400" style="normal">NotoSansKannada-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansKannada-VF.ttf + <font weight="500" style="normal">NotoSansKannada-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansKannada-VF.ttf + <font weight="600" style="normal">NotoSansKannada-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansKannada-VF.ttf + <font weight="700" style="normal">NotoSansKannada-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifKannada-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifKannada-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Knda" variant="compact"> - <font weight="400" style="normal">NotoSansKannadaUI-VF.ttf + <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansKannadaUI-VF.ttf + <font weight="500" style="normal">NotoSansKannadaUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansKannadaUI-VF.ttf + <font weight="600" style="normal">NotoSansKannadaUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansKannadaUI-VF.ttf + <font weight="700" style="normal">NotoSansKannadaUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Orya" variant="elegant"> - <font weight="400" style="normal">NotoSansOriya-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOriya.ttf</font> <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font> </family> <family lang="und-Orya" variant="compact"> - <font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOriyaUI.ttf</font> <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font> </family> <family lang="und-Sinh" variant="elegant"> - <font weight="400" style="normal">NotoSansSinhala-VF.ttf + <font weight="400" style="normal">NotoSansSinhala-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansSinhala-VF.ttf + <font weight="500" style="normal">NotoSansSinhala-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansSinhala-VF.ttf + <font weight="600" style="normal">NotoSansSinhala-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansSinhala-VF.ttf + <font weight="700" style="normal">NotoSansSinhala-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf + <font weight="400" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf + <font weight="500" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf + <font weight="600" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal" fallbackFor="serif">NotoSerifSinhala-VF.ttf + <font weight="700" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Sinh" variant="compact"> - <font weight="400" style="normal">NotoSansSinhalaUI-VF.ttf + <font weight="400" style="normal">NotoSansSinhalaUI-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansSinhalaUI-VF.ttf + <font weight="500" style="normal">NotoSansSinhalaUI-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansSinhalaUI-VF.ttf + <font weight="600" style="normal">NotoSansSinhalaUI-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansSinhalaUI-VF.ttf + <font weight="700" style="normal">NotoSansSinhalaUI-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Khmr" variant="elegant"> - <font weight="100" style="normal">NotoSansKhmer-VF.ttf + <font weight="100" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="26.0"/> </font> - <font weight="200" style="normal">NotoSansKhmer-VF.ttf + <font weight="200" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="39.0"/> </font> - <font weight="300" style="normal">NotoSansKhmer-VF.ttf + <font weight="300" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="58.0"/> </font> - <font weight="400" style="normal">NotoSansKhmer-VF.ttf + <font weight="400" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="90.0"/> </font> - <font weight="500" style="normal">NotoSansKhmer-VF.ttf + <font weight="500" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="108.0"/> </font> - <font weight="600" style="normal">NotoSansKhmer-VF.ttf + <font weight="600" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="128.0"/> </font> - <font weight="700" style="normal">NotoSansKhmer-VF.ttf + <font weight="700" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="151.0"/> </font> - <font weight="800" style="normal">NotoSansKhmer-VF.ttf + <font weight="800" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="169.0"/> </font> - <font weight="900" style="normal">NotoSansKhmer-VF.ttf + <font weight="900" style="normal">NotoSansKhmer-Regular.ttf <axis tag="wdth" stylevalue="100.0"/> <axis tag="wght" stylevalue="190.0"/> </font> @@ -769,17 +769,17 @@ <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font> </family> <family lang="und-Khmr" variant="compact"> - <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font> + <font weight="400" style="normal">NotoSansKhmerUI.ttf</font> <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font> </family> <family lang="und-Laoo" variant="elegant"> - <font weight="400" style="normal">NotoSansLao-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLao.ttf</font> <font weight="700" style="normal">NotoSansLao-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif">NotoSerifLao-Regular.ttf</font> + <font weight="400" style="normal" fallbackFor="serif">NotoSerifLao.ttf</font> <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font> </family> <family lang="und-Laoo" variant="compact"> - <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLaoUI.ttf</font> <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font> </family> <family lang="und-Mymr" variant="elegant"> @@ -795,56 +795,56 @@ <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font> </family> <family lang="und-Thaa"> - <font weight="400" style="normal">NotoSansThaana-Regular.ttf</font> + <font weight="400" style="normal">NotoSansThaana.ttf</font> <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font> </family> <family lang="und-Cham"> - <font weight="400" style="normal">NotoSansCham-Regular.ttf</font> + <font weight="400" style="normal">NotoSansCham.ttf</font> <font weight="700" style="normal">NotoSansCham-Bold.ttf</font> </family> <family lang="und-Ahom"> <font weight="400" style="normal">NotoSansAhom-Regular.otf</font> </family> <family lang="und-Adlm"> - <font weight="400" style="normal">NotoSansAdlam-VF.ttf + <font weight="400" style="normal">NotoSansAdlam-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansAdlam-VF.ttf + <font weight="500" style="normal">NotoSansAdlam-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansAdlam-VF.ttf + <font weight="600" style="normal">NotoSansAdlam-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansAdlam-VF.ttf + <font weight="700" style="normal">NotoSansAdlam-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Avst"> - <font weight="400" style="normal">NotoSansAvestan-Regular.ttf</font> + <font weight="400" style="normal">NotoSansAvestan.ttf</font> </family> <family lang="und-Bali"> - <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font> + <font weight="400" style="normal">NotoSansBalinese.ttf</font> </family> <family lang="und-Bamu"> - <font weight="400" style="normal">NotoSansBamum-Regular.ttf</font> + <font weight="400" style="normal">NotoSansBamum.ttf</font> </family> <family lang="und-Batk"> - <font weight="400" style="normal">NotoSansBatak-Regular.ttf</font> + <font weight="400" style="normal">NotoSansBatak.ttf</font> </family> <family lang="und-Brah"> - <font weight="400" style="normal">NotoSansBrahmi-Regular.ttf</font> + <font weight="400" style="normal">NotoSansBrahmi.ttf</font> </family> <family lang="und-Bugi"> - <font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font> + <font weight="400" style="normal">NotoSansBuginese.ttf</font> </family> <family lang="und-Buhd"> - <font weight="400" style="normal">NotoSansBuhid-Regular.ttf</font> + <font weight="400" style="normal">NotoSansBuhid.ttf</font> </family> <family lang="und-Cans"> - <font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font> + <font weight="400" style="normal">NotoSansCanadianAboriginal.ttf</font> </family> <family lang="und-Cari"> - <font weight="400" style="normal">NotoSansCarian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansCarian.ttf</font> </family> <family lang="und-Cakm"> <font weight="400" style="normal">NotoSansChakma-Regular.otf</font> @@ -853,164 +853,164 @@ <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font> </family> <family lang="und-Copt"> - <font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansCoptic.ttf</font> </family> <family lang="und-Xsux"> - <font weight="400" style="normal">NotoSansCuneiform-Regular.ttf</font> + <font weight="400" style="normal">NotoSansCuneiform.ttf</font> </family> <family lang="und-Cprt"> - <font weight="400" style="normal">NotoSansCypriot-Regular.ttf</font> + <font weight="400" style="normal">NotoSansCypriot.ttf</font> </family> <family lang="und-Dsrt"> - <font weight="400" style="normal">NotoSansDeseret-Regular.ttf</font> + <font weight="400" style="normal">NotoSansDeseret.ttf</font> </family> <family lang="und-Egyp"> - <font weight="400" style="normal">NotoSansEgyptianHieroglyphs-Regular.ttf</font> + <font weight="400" style="normal">NotoSansEgyptianHieroglyphs.ttf</font> </family> <family lang="und-Elba"> <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font> </family> <family lang="und-Glag"> - <font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansGlagolitic.ttf</font> </family> <family lang="und-Goth"> - <font weight="400" style="normal">NotoSansGothic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansGothic.ttf</font> </family> <family lang="und-Hano"> - <font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font> + <font weight="400" style="normal">NotoSansHanunoo.ttf</font> </family> <family lang="und-Armi"> - <font weight="400" style="normal">NotoSansImperialAramaic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansImperialAramaic.ttf</font> </family> <family lang="und-Phli"> - <font weight="400" style="normal">NotoSansInscriptionalPahlavi-Regular.ttf</font> + <font weight="400" style="normal">NotoSansInscriptionalPahlavi.ttf</font> </family> <family lang="und-Prti"> - <font weight="400" style="normal">NotoSansInscriptionalParthian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansInscriptionalParthian.ttf</font> </family> <family lang="und-Java"> <font weight="400" style="normal">NotoSansJavanese-Regular.otf</font> </family> <family lang="und-Kthi"> - <font weight="400" style="normal">NotoSansKaithi-Regular.ttf</font> + <font weight="400" style="normal">NotoSansKaithi.ttf</font> </family> <family lang="und-Kali"> - <font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font> + <font weight="400" style="normal">NotoSansKayahLi.ttf</font> </family> <family lang="und-Khar"> - <font weight="400" style="normal">NotoSansKharoshthi-Regular.ttf</font> + <font weight="400" style="normal">NotoSansKharoshthi.ttf</font> </family> <family lang="und-Lepc"> - <font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLepcha.ttf</font> </family> <family lang="und-Limb"> - <font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLimbu.ttf</font> </family> <family lang="und-Linb"> - <font weight="400" style="normal">NotoSansLinearB-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLinearB.ttf</font> </family> <family lang="und-Lisu"> - <font weight="400" style="normal">NotoSansLisu-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLisu.ttf</font> </family> <family lang="und-Lyci"> - <font weight="400" style="normal">NotoSansLycian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLycian.ttf</font> </family> <family lang="und-Lydi"> - <font weight="400" style="normal">NotoSansLydian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansLydian.ttf</font> </family> <family lang="und-Mand"> - <font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansMandaic.ttf</font> </family> <family lang="und-Mtei"> - <font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font> + <font weight="400" style="normal">NotoSansMeeteiMayek.ttf</font> </family> <family lang="und-Talu"> - <font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font> + <font weight="400" style="normal">NotoSansNewTaiLue.ttf</font> </family> <family lang="und-Nkoo"> - <font weight="400" style="normal">NotoSansNKo-Regular.ttf</font> + <font weight="400" style="normal">NotoSansNKo.ttf</font> </family> <family lang="und-Ogam"> - <font weight="400" style="normal">NotoSansOgham-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOgham.ttf</font> </family> <family lang="und-Olck"> - <font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOlChiki.ttf</font> </family> <family lang="und-Ital"> - <font weight="400" style="normal">NotoSansOldItalic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOldItalic.ttf</font> </family> <family lang="und-Xpeo"> - <font weight="400" style="normal">NotoSansOldPersian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOldPersian.ttf</font> </family> <family lang="und-Sarb"> - <font weight="400" style="normal">NotoSansOldSouthArabian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOldSouthArabian.ttf</font> </family> <family lang="und-Orkh"> - <font weight="400" style="normal">NotoSansOldTurkic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOldTurkic.ttf</font> </family> <family lang="und-Osge"> <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font> </family> <family lang="und-Osma"> - <font weight="400" style="normal">NotoSansOsmanya-Regular.ttf</font> + <font weight="400" style="normal">NotoSansOsmanya.ttf</font> </family> <family lang="und-Phnx"> - <font weight="400" style="normal">NotoSansPhoenician-Regular.ttf</font> + <font weight="400" style="normal">NotoSansPhoenician.ttf</font> </family> <family lang="und-Rjng"> - <font weight="400" style="normal">NotoSansRejang-Regular.ttf</font> + <font weight="400" style="normal">NotoSansRejang.ttf</font> </family> <family lang="und-Runr"> - <font weight="400" style="normal">NotoSansRunic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansRunic.ttf</font> </family> <family lang="und-Samr"> - <font weight="400" style="normal">NotoSansSamaritan-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSamaritan.ttf</font> </family> <family lang="und-Saur"> - <font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSaurashtra.ttf</font> </family> <family lang="und-Shaw"> - <font weight="400" style="normal">NotoSansShavian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansShavian.ttf</font> </family> <family lang="und-Sund"> - <font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSundanese.ttf</font> </family> <family lang="und-Sylo"> - <font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSylotiNagri.ttf</font> </family> <!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. --> <family lang="und-Syre"> - <font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSyriacEstrangela.ttf</font> </family> <family lang="und-Syrn"> - <font weight="400" style="normal">NotoSansSyriacEastern-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSyriacEastern.ttf</font> </family> <family lang="und-Syrj"> - <font weight="400" style="normal">NotoSansSyriacWestern-Regular.ttf</font> + <font weight="400" style="normal">NotoSansSyriacWestern.ttf</font> </family> <family lang="und-Tglg"> - <font weight="400" style="normal">NotoSansTagalog-Regular.ttf</font> + <font weight="400" style="normal">NotoSansTagalog.ttf</font> </family> <family lang="und-Tagb"> - <font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font> + <font weight="400" style="normal">NotoSansTagbanwa.ttf</font> </family> <family lang="und-Lana"> - <font weight="400" style="normal">NotoSansTaiTham-Regular.ttf</font> + <font weight="400" style="normal">NotoSansTaiTham.ttf</font> </family> <family lang="und-Tavt"> - <font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font> + <font weight="400" style="normal">NotoSansTaiViet.ttf</font> </family> <family lang="und-Tibt"> - <font weight="400" style="normal">NotoSerifTibetan-VF.ttf + <font weight="400" style="normal">NotoSerifTibetan-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSerifTibetan-VF.ttf + <font weight="500" style="normal">NotoSerifTibetan-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSerifTibetan-VF.ttf + <font weight="600" style="normal">NotoSerifTibetan-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSerifTibetan-VF.ttf + <font weight="700" style="normal">NotoSerifTibetan-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> @@ -1018,29 +1018,29 @@ <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font> </family> <family lang="und-Ugar"> - <font weight="400" style="normal">NotoSansUgaritic-Regular.ttf</font> + <font weight="400" style="normal">NotoSansUgaritic.ttf</font> </family> <family lang="und-Vaii"> - <font weight="400" style="normal">NotoSansVai-Regular.ttf</font> + <font weight="400" style="normal">NotoSansVai.ttf</font> </family> <family> <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font> </family> <family lang="zh-Hans"> - <font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font> - <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font> + <font weight="400" style="normal" index="2">NotoSansCJKjp-Regular.otc</font> + <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font> </family> <family lang="zh-Hant,zh-Bopo"> - <font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font> - <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font> + <font weight="400" style="normal" index="3">NotoSansCJKjp-Regular.otc</font> + <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font> </family> <family lang="ja"> - <font weight="400" style="normal" index="0">NotoSansCJK-Regular.ttc</font> - <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font> + <font weight="400" style="normal" index="0">NotoSansCJKjp-Regular.otc</font> + <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font> </family> <family lang="ko"> - <font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font> - <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font> + <font weight="400" style="normal" index="1">NotoSansCJKjp-Regular.otc</font> + <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font> </family> <family lang="und-Zsye"> <font weight="400" style="normal">NotoColorEmoji.ttf</font> @@ -1053,16 +1053,16 @@ override the East Asian punctuation for Chinese. --> <family lang="und-Tale"> - <font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font> + <font weight="400" style="normal">NotoSansTaiLe.ttf</font> </family> <family lang="und-Yiii"> - <font weight="400" style="normal">NotoSansYi-Regular.ttf</font> + <font weight="400" style="normal">NotoSansYi.ttf</font> </family> <family lang="und-Mong"> - <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font> + <font weight="400" style="normal">NotoSansMongolian.ttf</font> </family> <family lang="und-Phag"> - <font weight="400" style="normal">NotoSansPhagsPa-Regular.ttf</font> + <font weight="400" style="normal">NotoSansPhagsPa.ttf</font> </family> <family lang="und-Hluw"> <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font> @@ -1152,72 +1152,72 @@ <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font> </family> <family lang="und-Medf"> - <font weight="400" style="normal">NotoSansMedefaidrin-VF.ttf + <font weight="400" style="normal">NotoSansMedefaidrin-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansMedefaidrin-VF.ttf + <font weight="500" style="normal">NotoSansMedefaidrin-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansMedefaidrin-VF.ttf + <font weight="600" style="normal">NotoSansMedefaidrin-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansMedefaidrin-VF.ttf + <font weight="700" style="normal">NotoSansMedefaidrin-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Soyo"> - <font weight="400" style="normal">NotoSansSoyombo-VF.ttf + <font weight="400" style="normal">NotoSansSoyombo-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansSoyombo-VF.ttf + <font weight="500" style="normal">NotoSansSoyombo-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansSoyombo-VF.ttf + <font weight="600" style="normal">NotoSansSoyombo-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansSoyombo-VF.ttf + <font weight="700" style="normal">NotoSansSoyombo-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Takr"> - <font weight="400" style="normal">NotoSansTakri-VF.ttf + <font weight="400" style="normal">NotoSansTakri-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSansTakri-VF.ttf + <font weight="500" style="normal">NotoSansTakri-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSansTakri-VF.ttf + <font weight="600" style="normal">NotoSansTakri-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSansTakri-VF.ttf + <font weight="700" style="normal">NotoSansTakri-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Hmnp"> - <font weight="400" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf + <font weight="400" style="normal">NotoSerifHmongNyiakeng-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf + <font weight="500" style="normal">NotoSerifHmongNyiakeng-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf + <font weight="600" style="normal">NotoSerifHmongNyiakeng-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSerifNyiakengPuachueHmong-VF.ttf + <font weight="700" style="normal">NotoSerifHmongNyiakeng-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> <family lang="und-Yezi"> - <font weight="400" style="normal">NotoSerifYezidi-VF.ttf + <font weight="400" style="normal">NotoSerifYezidi-Regular.ttf <axis tag="wght" stylevalue="400"/> </font> - <font weight="500" style="normal">NotoSerifYezidi-VF.ttf + <font weight="500" style="normal">NotoSerifYezidi-Regular.ttf <axis tag="wght" stylevalue="500"/> </font> - <font weight="600" style="normal">NotoSerifYezidi-VF.ttf + <font weight="600" style="normal">NotoSerifYezidi-Regular.ttf <axis tag="wght" stylevalue="600"/> </font> - <font weight="700" style="normal">NotoSerifYezidi-VF.ttf + <font weight="700" style="normal">NotoSerifYezidi-Regular.ttf <axis tag="wght" stylevalue="700"/> </font> </family> diff --git a/data/keyboards/Android.mk b/data/keyboards/Android.mk index 7949c77ab558..6ae88007f0f5 100644 --- a/data/keyboards/Android.mk +++ b/data/keyboards/Android.mk @@ -22,6 +22,9 @@ include $(LOCAL_PATH)/common.mk include $(CLEAR_VARS) LOCAL_MODULE := validate_framework_keymaps +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE intermediates := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE),,COMMON) LOCAL_BUILT_MODULE := $(intermediates)/stamp diff --git a/data/keyboards/OWNERS b/data/keyboards/OWNERS index c4f6df824a39..0ce83507160c 100644 --- a/data/keyboards/OWNERS +++ b/data/keyboards/OWNERS @@ -1,5 +1,3 @@ set noparent -michaelwr@google.com -svv@google.com -lzye@google.com +include /services/core/java/com/android/server/input/OWNERS diff --git a/data/keyboards/Vendor_057e_Product_2009.kl b/data/keyboards/Vendor_057e_Product_2009.kl index 3c6b11e4640c..7491ee562b59 100644 --- a/data/keyboards/Vendor_057e_Product_2009.kl +++ b/data/keyboards/Vendor_057e_Product_2009.kl @@ -74,3 +74,11 @@ key 0x135 BUTTON_MODE # Home key key 0x13c HOME + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/drm/jni/Android.bp b/drm/jni/Android.bp index 68757d86fb89..669f10994365 100644 --- a/drm/jni/Android.bp +++ b/drm/jni/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + cc_library_shared { name: "libdrmframework_jni", diff --git a/errorprone/Android.bp b/errorprone/Android.bp index c60191c5b980..d1e94dfed4a8 100644 --- a/errorprone/Android.bp +++ b/errorprone/Android.bp @@ -1,4 +1,13 @@ +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_plugin { name: "error_prone_android_framework", diff --git a/graphics/java/android/graphics/BlurShader.java b/graphics/java/android/graphics/BlurShader.java deleted file mode 100644 index 2e4bd7d9be43..000000000000 --- a/graphics/java/android/graphics/BlurShader.java +++ /dev/null @@ -1,89 +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.graphics; - -import android.annotation.NonNull; -import android.annotation.Nullable; - -/** - * A subclass of shader that blurs input from another {@link android.graphics.Shader} instance - * or all the drawing commands with the {@link android.graphics.Paint} that this shader is - * attached to. - */ -public final class BlurShader extends Shader { - - private final float mRadiusX; - private final float mRadiusY; - private final Shader mInputShader; - private final TileMode mEdgeTreatment; - - private long mNativeInputShader = 0; - - /** - * Create a {@link BlurShader} that blurs the contents of the optional input shader - * with the specified radius along the x and y axis. If no input shader is provided - * then all drawing commands issued with a {@link android.graphics.Paint} that this - * shader is installed in will be blurred. - * - * This uses a default {@link TileMode#DECAL} for edge treatment - * - * @param radiusX Radius of blur along the X axis - * @param radiusY Radius of blur along the Y axis - * @param inputShader Input shader that provides the content to be blurred - */ - public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader) { - this(radiusX, radiusY, inputShader, TileMode.DECAL); - } - - /** - * Create a {@link BlurShader} that blurs the contents of the optional input shader - * with the specified radius along the x and y axis. If no input shader is provided - * then all drawing commands issued with a {@link android.graphics.Paint} that this - * shader is installed in will be blurred - * @param radiusX Radius of blur along the X axis - * @param radiusY Radius of blur along the Y axis - * @param inputShader Input shader that provides the content to be blurred - * @param edgeTreatment Policy for how to blur content near edges of the blur shader - */ - public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader, - @NonNull TileMode edgeTreatment) { - mRadiusX = radiusX; - mRadiusY = radiusY; - mInputShader = inputShader; - mEdgeTreatment = edgeTreatment; - } - - /** @hide **/ - @Override - protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) { - mNativeInputShader = mInputShader != null - ? mInputShader.getNativeInstance(filterFromPaint) : 0; - return nativeCreate(nativeMatrix, mRadiusX, mRadiusY, mNativeInputShader, - mEdgeTreatment.nativeInt); - } - - /** @hide **/ - @Override - protected boolean shouldDiscardNativeInstance(boolean filterFromPaint) { - long currentNativeInstance = mInputShader != null - ? mInputShader.getNativeInstance(filterFromPaint) : 0; - return mNativeInputShader != currentNativeInstance; - } - - private static native long nativeCreate(long nativeMatrix, float radiusX, float radiusY, - long inputShader, int edgeTreatment); -} diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java index d59abb5916a0..189be53a397f 100644 --- a/graphics/java/android/graphics/FrameInfo.java +++ b/graphics/java/android/graphics/FrameInfo.java @@ -69,28 +69,26 @@ public final class FrameInfo { // animation & drawing system public static final int VSYNC = 3; - // The time of the oldest input event - public static final int OLDEST_INPUT_EVENT = 4; - - // The time of the newest input event - public static final int NEWEST_INPUT_EVENT = 5; + // The id of the input event that caused the current frame + public static final int INPUT_EVENT_ID = 4; // When input event handling started - public static final int HANDLE_INPUT_START = 6; + public static final int HANDLE_INPUT_START = 5; // When animation evaluations started - public static final int ANIMATION_START = 7; + public static final int ANIMATION_START = 6; // When ViewRootImpl#performTraversals() started - public static final int PERFORM_TRAVERSALS_START = 8; + public static final int PERFORM_TRAVERSALS_START = 7; // When View:draw() started - public static final int DRAW_START = 9; + public static final int DRAW_START = 8; // When the frame needs to be ready by - public static final int FRAME_DEADLINE = 10; + public static final int FRAME_DEADLINE = 9; // Must be the last one + // This value must be in sync with `UI_THREAD_FRAME_INFO_SIZE` in FrameInfo.h private static final int FRAME_INFO_SIZE = FRAME_DEADLINE + 1; /** checkstyle */ diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index f1f9a5fc92ea..e22257071dd2 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -153,6 +153,13 @@ public class Paint { * resource bitmaps often are) the filtering will already have been * done.</p> * + * <p>On devices running {@link Build.VERSION_CODES#O} and below, hardware + * accelerated drawing always uses bilinear sampling on scaled bitmaps, + * regardless of this flag. On devices running {@link Build.VERSION_CODES#Q} + * and above, this flag defaults to being set on a new {@code Paint}. It can + * be cleared with {@link #setFlags} or {@link #setFilterBitmap}.</p> + * + * @see #Paint() * @see #Paint(int) * @see #setFlags(int) */ @@ -558,6 +565,12 @@ public class Paint { /** * Create a new paint with default settings. + * + * <p>On devices running {@link Build.VERSION_CODES#O} and below, hardware + * accelerated drawing always acts as if {@link #FILTER_BITMAP_FLAG} is set. + * On devices running {@link Build.VERSION_CODES#Q} and above, + * {@code FILTER_BITMAP_FLAG} is set by this constructor, and it can be + * cleared with {@link #setFlags} or {@link #setFilterBitmap}.</p> */ public Paint() { this(0); @@ -567,6 +580,13 @@ public class Paint { * Create a new paint with the specified flags. Use setFlags() to change * these after the paint is created. * + * <p>On devices running {@link Build.VERSION_CODES#O} and below, hardware + * accelerated drawing always acts as if {@link #FILTER_BITMAP_FLAG} is set. + * On devices running {@link Build.VERSION_CODES#Q} and above, + * {@code FILTER_BITMAP_FLAG} is always set by this constructor, regardless + * of the value of {@code flags}. It can be cleared with {@link #setFlags} or + * {@link #setFilterBitmap}.</p> + * * @param flags initial flag bits, as if they were passed via setFlags(). */ public Paint(int flags) { @@ -991,6 +1011,7 @@ public class Paint { * device pixels. That is dependent on dithering and xfermodes. * * @see #setFilterBitmap(boolean) setFilterBitmap() + * @see #FILTER_BITMAP_FLAG */ public final boolean isFilterBitmap() { return (getFlags() & FILTER_BITMAP_FLAG) != 0; @@ -1004,6 +1025,7 @@ public class Paint { * * @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's * flags, false to clear it. + * @see #FILTER_BITMAP_FLAG */ public void setFilterBitmap(boolean filter) { nSetFilterBitmap(mNativePaint, filter); diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java index 4a92cf11fa5c..f6f770be3de8 100644 --- a/graphics/java/android/graphics/RenderNode.java +++ b/graphics/java/android/graphics/RenderNode.java @@ -272,6 +272,16 @@ public final class RenderNode { void positionChanged(long frameNumber, int left, int top, int right, int bottom); /** + * Call to apply a stretch effect to any child SurfaceControl layers + * + * TODO: Fold this into positionChanged & have HWUI do the ASurfaceControl calls? + * + * @hide + */ + default void applyStretch(long frameNumber, float left, float top, float right, + float bottom, float vecX, float vecY, float maxStretch) { } + + /** * Called by native on RenderThread to notify that the view is no longer in the * draw tree. UI thread is blocked at this point. * @@ -312,6 +322,14 @@ public final class RenderNode { pul.positionLost(frameNumber); } } + + @Override + public void applyStretch(long frameNumber, float left, float top, float right, float bottom, + float vecX, float vecY, float maxStretch) { + for (PositionUpdateListener pul : mListeners) { + pul.applyStretch(frameNumber, left, top, right, bottom, vecX, vecY, maxStretch); + } + } } /** @@ -707,7 +725,7 @@ public final class RenderNode { if (1.0 < vecY || vecY < -1.0) { throw new IllegalArgumentException("vecY must be in the range [-1, 1], was " + vecY); } - if (top <= bottom || right <= left) { + if (top >= bottom || left >= right) { throw new IllegalArgumentException( "Stretch region must not be empty, got " + new RectF(left, top, right, bottom).toString()); diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 32c777cf498c..c80788269c24 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -172,9 +172,9 @@ public class Typeface { * @hide */ @UnsupportedAppUsage - public long native_instance; + public final long native_instance; - private Runnable mCleaner; + private final Runnable mCleaner; /** @hide */ @IntDef(value = {NORMAL, BOLD, ITALIC, BOLD_ITALIC}) @@ -189,9 +189,9 @@ public class Typeface { /** @hide */ public static final int STYLE_MASK = 0x03; @UnsupportedAppUsage - private @Style int mStyle = 0; + private @Style final int mStyle; - private @IntRange(from = 0, to = FontStyle.FONT_WEIGHT_MAX) int mWeight = 0; + private @IntRange(from = 0, to = FontStyle.FONT_WEIGHT_MAX) final int mWeight; // Value for weight and italic. Indicates the value is resolved by font metadata. // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp @@ -207,6 +207,7 @@ public class Typeface { private static final int STYLE_NORMAL = 0; private static final int STYLE_ITALIC = 1; + @GuardedBy("this") private int[] mSupportedAxes; private static final int[] EMPTY_AXES = {}; diff --git a/graphics/proto/Android.bp b/graphics/proto/Android.bp index ea79b731a365..1b192668b4a3 100644 --- a/graphics/proto/Android.bp +++ b/graphics/proto/Android.bp @@ -1,3 +1,12 @@ +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_static { name: "updatable-driver-protos", host_supported: true, diff --git a/keystore/Android.bp b/keystore/Android.bp new file mode 100644 index 000000000000..5db668e48431 --- /dev/null +++ b/keystore/Android.bp @@ -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 { + default_applicable_licenses: ["frameworks_base_keystore_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_keystore_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java new file mode 100644 index 000000000000..ed789f03f9ba --- /dev/null +++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java @@ -0,0 +1,124 @@ +/* + * 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.security; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.ServiceManager; +import android.os.ServiceSpecificException; +import android.security.usermanager.IKeystoreUserManager; +import android.system.keystore2.Domain; +import android.system.keystore2.ResponseCode; +import android.util.Log; + +/** + * @hide This is the client side for IKeystoreUserManager AIDL. + * It shall only be used by the LockSettingsService. + */ +public class AndroidKeyStoreMaintenance { + private static final String TAG = "AndroidKeyStoreMaintenance"; + + public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR; + + private static IKeystoreUserManager getService() { + return IKeystoreUserManager.Stub.asInterface( + ServiceManager.checkService("android.security.usermanager")); + } + + /** + * Informs Keystore 2.0 about adding a user + * + * @param userId - Android user id of the user being added + * @return 0 if successful or a {@code ResponseCode} + * @hide + */ + public static int onUserAdded(@NonNull int userId) { + if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; + try { + getService().onUserAdded(userId); + return 0; + } catch (ServiceSpecificException e) { + Log.e(TAG, "onUserAdded failed", e); + return e.errorCode; + } catch (Exception e) { + Log.e(TAG, "Can not connect to keystore", e); + return SYSTEM_ERROR; + } + } + + /** + * Informs Keystore 2.0 about removing a usergit mer + * + * @param userId - Android user id of the user being removed + * @return 0 if successful or a {@code ResponseCode} + * @hide + */ + public static int onUserRemoved(int userId) { + if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; + try { + getService().onUserRemoved(userId); + return 0; + } catch (ServiceSpecificException e) { + Log.e(TAG, "onUserRemoved failed", e); + return e.errorCode; + } catch (Exception e) { + Log.e(TAG, "Can not connect to keystore", e); + return SYSTEM_ERROR; + } + } + + /** + * Informs Keystore 2.0 about changing user's password + * + * @param userId - Android user id of the user + * @param password - a secret derived from the synthetic password provided by the + * LockSettingService + * @return 0 if successful or a {@code ResponseCode} + * @hide + */ + public static int onUserPasswordChanged(int userId, @Nullable byte[] password) { + if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; + try { + getService().onUserPasswordChanged(userId, password); + return 0; + } catch (ServiceSpecificException e) { + Log.e(TAG, "onUserPasswordChanged failed", e); + return e.errorCode; + } catch (Exception e) { + Log.e(TAG, "Can not connect to keystore", e); + return SYSTEM_ERROR; + } + } + + /** + * Informs Keystore 2.0 that an app was uninstalled and the corresponding namspace is to + * be cleared. + */ + public static int clearNamespace(@Domain int domain, long namespace) { + if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; + try { + getService().clearNamespace(domain, namespace); + return 0; + } catch (ServiceSpecificException e) { + Log.e(TAG, "clearNamespace failed", e); + return e.errorCode; + } catch (Exception e) { + Log.e(TAG, "Can not connect to keystore", e); + return SYSTEM_ERROR; + } + } +} diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java index 21d23b1b2575..50a90820117d 100644 --- a/keystore/java/android/security/Authorization.java +++ b/keystore/java/android/security/Authorization.java @@ -33,20 +33,12 @@ import android.util.Log; */ public class Authorization { private static final String TAG = "KeystoreAuthorization"; - private static IKeystoreAuthorization sIKeystoreAuthorization; public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR; - public Authorization() { - sIKeystoreAuthorization = null; - } - - private static synchronized IKeystoreAuthorization getService() { - if (sIKeystoreAuthorization == null) { - sIKeystoreAuthorization = IKeystoreAuthorization.Stub.asInterface( + private static IKeystoreAuthorization getService() { + return IKeystoreAuthorization.Stub.asInterface( ServiceManager.checkService("android.security.authorization")); - } - return sIKeystoreAuthorization; } /** @@ -55,12 +47,12 @@ public class Authorization { * @param authToken created by Android authenticators. * @return 0 if successful or {@code ResponseCode.SYSTEM_ERROR}. */ - public int addAuthToken(@NonNull HardwareAuthToken authToken) { + public static int addAuthToken(@NonNull HardwareAuthToken authToken) { if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; try { getService().addAuthToken(authToken); return 0; - } catch (RemoteException e) { + } catch (RemoteException | NullPointerException e) { Log.w(TAG, "Can not connect to keystore", e); return SYSTEM_ERROR; } catch (ServiceSpecificException e) { @@ -73,7 +65,7 @@ public class Authorization { * @param authToken * @return 0 if successful or a {@code ResponseCode}. */ - public int addAuthToken(@NonNull byte[] authToken) { + public static int addAuthToken(@NonNull byte[] authToken) { return addAuthToken(AuthTokenUtils.toHardwareAuthToken(authToken)); } @@ -86,7 +78,7 @@ public class Authorization { * * @return 0 if successful or a {@code ResponseCode}. */ - public int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId, + public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId, @Nullable byte[] syntheticPassword) { if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0; try { @@ -96,7 +88,7 @@ public class Authorization { getService().onLockScreenEvent(LockScreenEvent.UNLOCK, userId, syntheticPassword); } return 0; - } catch (RemoteException e) { + } catch (RemoteException | NullPointerException e) { Log.w(TAG, "Can not connect to keystore", e); return SYSTEM_ERROR; } catch (ServiceSpecificException e) { diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index f708298a2cbd..684eebe6ffde 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -37,8 +37,6 @@ interface IKeyChainService { void setUserSelectable(String alias, boolean isUserSelectable); int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec); - int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags, - out KeymasterCertificateChain chain); boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain); // APIs used by CertInstaller and DevicePolicyManager @@ -65,6 +63,7 @@ interface IKeyChainService { AppUriAuthenticationPolicy getCredentialManagementAppPolicy(); String getPredefinedAliasForPackageAndUri(String packageName, in Uri uri); void removeCredentialManagementApp(); + boolean isCredentialManagementApp(String packageName); // APIs used by KeyChainActivity void setGrant(int uid, String alias, boolean value); diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index 97819c56fd5a..65a81cd57f41 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -44,6 +44,8 @@ import android.os.UserManager; import android.security.keystore.AndroidKeyStoreProvider; import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; +import android.system.keystore2.Domain; +import android.system.keystore2.KeyDescriptor; import android.util.Log; import com.android.org.conscrypt.TrustedCertificateStore; @@ -421,6 +423,15 @@ public final class KeyChain { * credentials. This is limited to unmanaged devices. The authentication policy must be * provided to be able to make this request successfully. * + * <p> This intent should be started using {@link Activity#startActivityForResult(Intent, int)} + * to verify whether the request was successful and whether the user accepted or denied the + * request. If the user successfully receives and accepts the request, the result code will be + * {@link Activity#RESULT_OK}, otherwise the result code will be + * {@link Activity#RESULT_CANCELED}. + * + * <p> {@link KeyChain#isCredentialManagementApp(Context)} should be used to determine whether + * an app is already the credential management app. + * * @param policy The authentication policy determines which alias for a private key and * certificate pair should be used for authentication. */ @@ -589,6 +600,55 @@ public final class KeyChain { } /** + * Check whether the caller is the credential management app {@link CredentialManagementApp}. + * The credential management app has the ability to manage the user's KeyChain credentials + * on unmanaged devices. + * + * <p> {@link KeyChain#createManageCredentialsIntent} should be used by an app to request to + * become the credential management app. The user must approve this request before the app can + * manage the user's credentials. There can only be one credential management on the device. + * + * @return {@code true} if the caller is the credential management app. + */ + public static boolean isCredentialManagementApp(@NonNull Context context) { + boolean isCredentialManagementApp = false; + try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) { + isCredentialManagementApp = keyChainConnection.getService() + .isCredentialManagementApp(context.getPackageName()); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted while checking whether the caller is the " + + "credential management app.", e); + } catch (SecurityException e) { + isCredentialManagementApp = false; + } + return isCredentialManagementApp; + } + + /** + * Called by the credential management app to get the authentication policy + * {@link AppUriAuthenticationPolicy}. + * + * @return the credential management app's authentication policy. + * @throws SecurityException if the caller is not the credential management app. + */ + @NonNull + public static AppUriAuthenticationPolicy getCredentialManagementAppPolicy( + @NonNull Context context) throws SecurityException { + AppUriAuthenticationPolicy policy = null; + try (KeyChainConnection keyChainConnection = KeyChain.bind(context)) { + policy = keyChainConnection.getService().getCredentialManagementAppPolicy(); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } catch (InterruptedException e) { + throw new RuntimeException( + "Interrupted while getting credential management app policy.", e); + } + return policy; + } + + /** * Set a credential management app. The credential management app has the ability to manage * the user's KeyChain credentials on unmanaged devices. * @@ -682,6 +742,33 @@ public final class KeyChain { return null; } + /** + * This prefix is used to disambiguate grant aliase strings from normal key alias strings. + * Technically, a key alias string can use the same prefix. However, a collision does not + * lead to privilege escalation, because grants are access controlled in the Keystore daemon. + * @hide + */ + public static final String GRANT_ALIAS_PREFIX = "ks2_keychain_grant_id:"; + + private static KeyDescriptor getGrantDescriptor(String keyid) { + KeyDescriptor result = new KeyDescriptor(); + result.domain = Domain.GRANT; + result.blob = null; + result.alias = null; + try { + result.nspace = Long.parseUnsignedLong( + keyid.substring(GRANT_ALIAS_PREFIX.length()), 16 /* radix */); + } catch (NumberFormatException e) { + return null; + } + return result; + } + + /** @hide */ + public static String getGrantString(KeyDescriptor key) { + return String.format(GRANT_ALIAS_PREFIX + "%016X", key.nspace); + } + /** @hide */ @Nullable @WorkerThread public static KeyPair getKeyPair(@NonNull Context context, @NonNull String alias) @@ -705,11 +792,23 @@ public final class KeyChain { if (keyId == null) { return null; + } + + if (AndroidKeyStoreProvider.isKeystore2Enabled()) { + try { + return android.security.keystore2.AndroidKeyStoreProvider + .loadAndroidKeyStoreKeyPairFromKeystore( + KeyStore2.getInstance(), + getGrantDescriptor(keyId)); + } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { + throw new KeyChainException(e); + } } else { try { return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore( KeyStore.getInstance(), keyId, KeyStore.UID_SELF); - } catch (RuntimeException | UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) { + } catch (RuntimeException | UnrecoverableKeyException + | KeyPermanentlyInvalidatedException e) { throw new KeyChainException(e); } } @@ -827,11 +926,8 @@ public final class KeyChain { @Deprecated public static boolean isBoundKeyAlgorithm( @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) { - if (!isKeyAlgorithmSupported(algorithm)) { - return false; - } - - return KeyStore.getInstance().isHardwareBacked(algorithm); + // All supported algorithms are hardware backed. Individual keys may not be. + return true; } /** @hide */ diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index e19d88c182ff..93658e69eac8 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -43,6 +43,7 @@ import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; import android.security.keystore.KeystoreResponse; import android.security.keystore.UserNotAuthenticatedException; +import android.system.keystore2.Domain; import android.util.Log; import com.android.internal.org.bouncycastle.asn1.ASN1InputStream; @@ -466,6 +467,9 @@ public class KeyStore { public boolean clearUid(int uid) { try { + if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) { + return AndroidKeyStoreMaintenance.clearNamespace(Domain.APP, uid) == 0; + } return mBinder.clear_uid(uid) == NO_ERROR; } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); @@ -996,7 +1000,7 @@ public class KeyStore { */ public int addAuthToken(byte[] authToken) { try { - new Authorization().addAuthToken(authToken); + Authorization.addAuthToken(authToken); return mBinder.addAuthToken(authToken); } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java index f7477bf92c81..6ac3821d0f9c 100644 --- a/keystore/java/android/security/KeyStore2.java +++ b/keystore/java/android/security/KeyStore2.java @@ -24,6 +24,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.security.keymaster.KeymasterDefs; +import android.system.keystore2.Domain; import android.system.keystore2.IKeystoreService; import android.system.keystore2.KeyDescriptor; import android.system.keystore2.KeyEntryResponse; @@ -107,7 +108,6 @@ public class KeyStore2 { try { return request.execute(service); } catch (ServiceSpecificException e) { - Log.e(TAG, "KeyStore exception", e); throw getKeyStoreException(e.errorCode); } catch (RemoteException e) { if (firstTry) { @@ -158,6 +158,50 @@ public class KeyStore2 { } /** + * Grant string prefix as used by the keystore boringssl engine. Must be kept in sync + * with system/security/keystore-engine. Note: The prefix here includes the 0x which + * std::stringstream used in keystore-engine needs to identify the number as hex represented. + * Here we include it in the prefix, because Long#parseUnsignedLong does not understand it + * and gets the radix as explicit argument. + * @hide + */ + private static final String KEYSTORE_ENGINE_GRANT_ALIAS_PREFIX = + "ks2_keystore-engine_grant_id:0x"; + + /** + * This function turns a grant identifier into a specific string that is understood by the + * keystore-engine in system/security/keystore-engine. Is only used by VPN and WI-FI components + * to allow certain system components like racoon or vendor components like WPA supplicant + * to use keystore keys with boring ssl. + * + * @param grantId the grant id as returned by {@link #grant} in the {@code nspace} filed of + * the resulting {@code KeyDescriptor}. + * @return The grant descriptor string. + * @hide + */ + public static String makeKeystoreEngineGrantString(long grantId) { + return String.format("%s%016X", KEYSTORE_ENGINE_GRANT_ALIAS_PREFIX, grantId); + } + + /** + * Convenience function to turn a keystore engine grant string as returned by + * {@link #makeKeystoreEngineGrantString(long)} back into a grant KeyDescriptor. + * + * @param grantString As string returned by {@link #makeKeystoreEngineGrantString(long)} + * @return The grant key descriptor. + * @hide + */ + public static KeyDescriptor keystoreEngineGrantString2KeyDescriptor(String grantString) { + KeyDescriptor key = new KeyDescriptor(); + key.domain = Domain.GRANT; + key.nspace = Long.parseUnsignedLong( + grantString.substring(KEYSTORE_ENGINE_GRANT_ALIAS_PREFIX.length()), 16); + key.alias = null; + key.blob = null; + return key; + } + + /** * Create a grant that allows the grantee identified by {@code granteeUid} to use * the key specified by {@code descriptor} withint the restrictions given by * {@code accessVectore}. diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java index c20cf01a993e..a6e33664f2b1 100644 --- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java @@ -59,7 +59,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeString(mSpec.getKeystoreAlias()); out.writeInt(mSpec.getPurposes()); - out.writeInt(mSpec.getUid()); + out.writeInt(mSpec.getNamespace()); out.writeInt(mSpec.getKeySize()); // Only needs to support RSAKeyGenParameterSpec and ECGenParameterSpec. @@ -125,7 +125,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { private ParcelableKeyGenParameterSpec(Parcel in) { final String keystoreAlias = in.readString(); final int purposes = in.readInt(); - final int uid = in.readInt(); + final int namespace = in.readInt(); final int keySize = in.readInt(); final int keySpecType = in.readInt(); @@ -177,7 +177,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable { // KeyGenParameterSpec constructor (whereas using a builder would silently drop them). mSpec = new KeyGenParameterSpec( keystoreAlias, - uid, + namespace, keySize, algorithmSpec, certificateSubject, diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java index b3bfd6a3a97a..e401add9ece7 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java @@ -154,7 +154,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private KeyGenParameterSpec mSpec; private String mEntryAlias; - private int mEntryUid; + private int mEntryNamespace; private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm; private int mKeymasterAlgorithm = -1; private int mKeySizeBits; @@ -218,7 +218,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato } mEntryAlias = spec.getKeystoreAlias(); - mEntryUid = spec.getUid(); + mEntryNamespace = spec.getNamespace(); mSpec = spec; mKeymasterAlgorithm = keymasterAlgorithm; mKeySizeBits = spec.getKeySize(); @@ -439,7 +439,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato private void resetAll() { mEntryAlias = null; - mEntryUid = KeyProperties.NAMESPACE_APPLICATION; + mEntryNamespace = KeyProperties.NAMESPACE_APPLICATION; mJcaKeyAlgorithm = null; mKeymasterAlgorithm = -1; mKeymasterPurposes = null; @@ -541,10 +541,10 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato KeyDescriptor descriptor = new KeyDescriptor(); descriptor.alias = mEntryAlias; - descriptor.domain = mEntryUid == KeyProperties.NAMESPACE_APPLICATION + descriptor.domain = mEntryNamespace == KeyProperties.NAMESPACE_APPLICATION ? Domain.APP : Domain.SELINUX; - descriptor.nspace = mEntryUid; + descriptor.nspace = mEntryNamespace; descriptor.blob = null; boolean success = false; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index e1011155248e..d36695b9b410 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -43,6 +43,7 @@ import java.security.interfaces.RSAPublicKey; import javax.crypto.Cipher; import javax.crypto.Mac; +import javax.crypto.SecretKey; /** * A provider focused on providing JCA interfaces for the Android KeyStore. @@ -273,10 +274,10 @@ public class AndroidKeyStoreProvider extends Provider { /** @hide **/ @NonNull public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore( - @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace) + @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor) throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { AndroidKeyStoreKey key = - loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace); + loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor); if (key instanceof AndroidKeyStorePublicKey) { AndroidKeyStorePublicKey publicKey = (AndroidKeyStorePublicKey) key; return new KeyPair(publicKey, publicKey.getPrivateKey()); @@ -299,13 +300,26 @@ public class AndroidKeyStoreProvider extends Provider { } } + /** @hide **/ + @NonNull + public static SecretKey loadAndroidKeyStoreSecretKeyFromKeystore( + @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor) + throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { + + AndroidKeyStoreKey key = + loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor); + if (key instanceof SecretKey) { + return (SecretKey) key; + } else { + throw new UnrecoverableKeyException("No secret key found by the given alias."); + } + } @NonNull private static AndroidKeyStoreSecretKey makeAndroidKeyStoreSecretKeyFromKeyEntryResponse( @NonNull KeyDescriptor descriptor, @NonNull KeyEntryResponse response, int algorithm, int digest) throws UnrecoverableKeyException { - @KeyProperties.KeyAlgorithmEnum String keyAlgorithmString; try { keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm( @@ -336,8 +350,7 @@ public class AndroidKeyStoreProvider extends Provider { @NonNull public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore( @NonNull KeyStore2 keyStore, @NonNull String alias, int namespace) - throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { - + throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { KeyDescriptor descriptor = new KeyDescriptor(); if (namespace == KeyProperties.NAMESPACE_APPLICATION) { descriptor.nspace = KeyProperties.NAMESPACE_APPLICATION; // ignored; @@ -348,6 +361,18 @@ public class AndroidKeyStoreProvider extends Provider { } descriptor.alias = alias; descriptor.blob = null; + + final AndroidKeyStoreKey key = loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor); + if (key instanceof AndroidKeyStorePublicKey) { + return ((AndroidKeyStorePublicKey) key).getPrivateKey(); + } else { + return key; + } + } + + private static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore( + @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor) + throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { KeyEntryResponse response = null; try { response = keyStore.getKeyEntry(descriptor); @@ -397,7 +422,7 @@ public class AndroidKeyStoreProvider extends Provider { keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) { return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata, new KeyStoreSecurityLevel(response.iSecurityLevel), - keymasterAlgorithm).getPrivateKey(); + keymasterAlgorithm); } else { throw new UnrecoverableKeyException("Key algorithm unknown"); } diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp index e9b22c140742..2315a8568c64 100644 --- a/keystore/tests/Android.bp +++ b/keystore/tests/Android.bp @@ -12,6 +12,15 @@ // 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_keystore_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_keystore_license"], +} + android_test { name: "KeystoreTests", // LOCAL_MODULE := keystore diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp index 4612ba28d1db..d8f00bbc1ad3 100644 --- a/libs/WindowManager/Jetpack/Android.bp +++ b/libs/WindowManager/Jetpack/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + // Sidecar android_library_import { name: "window-sidecar", diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 0540aee1d6d9..1b5dc8bdbcaa 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + // Begin ProtoLog java_library { name: "wm_shell_protolog-groups", diff --git a/libs/WindowManager/Shell/res/drawable/pip_expand.xml b/libs/WindowManager/Shell/res/drawable/pip_expand.xml index c99d81934aab..d36c4f72ecd0 100644 --- a/libs/WindowManager/Shell/res/drawable/pip_expand.xml +++ b/libs/WindowManager/Shell/res/drawable/pip_expand.xml @@ -14,8 +14,8 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="36dp" - android:height="36dp" + android:width="@dimen/pip_expand_action_inner_size" + android:height="@dimen/pip_expand_action_inner_size" android:viewportWidth="36" android:viewportHeight="36"> @@ -25,4 +25,4 @@ android:fillColor="#FFFFFF" android:pathData="M10 21H7v8h8v-3h-5v-5zm-3-6h3v-5h5V7H7v8zm19 11h-5v3h8v-8h-3v5zM21 7v3h5v5h3V7h-8z" /> -</vector>
\ No newline at end of file +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml index bcc850a854de..60456267afef 100644 --- a/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml +++ b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml @@ -15,8 +15,8 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24.0dp" - android:height="24.0dp" + android:width="@dimen/pip_action_inner_size" + android:height="@dimen/pip_action_inner_size" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml index ef9b2d9c1c63..0c469f7abb78 100644 --- a/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml +++ b/libs/WindowManager/Shell/res/drawable/pip_ic_pause_white.xml @@ -15,8 +15,8 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" + android:width="@dimen/pip_action_inner_size" + android:height="@dimen/pip_action_inner_size" android:viewportWidth="24" android:viewportHeight="24"> diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml index f12d2cbebc87..8567afa3232c 100644 --- a/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml +++ b/libs/WindowManager/Shell/res/drawable/pip_ic_play_arrow_white.xml @@ -15,8 +15,8 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" + android:width="@dimen/pip_action_inner_size" + android:height="@dimen/pip_action_inner_size" android:viewportWidth="24" android:viewportHeight="24"> diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml index b61e98ce2f9f..73ec167f1f6f 100644 --- a/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml +++ b/libs/WindowManager/Shell/res/drawable/pip_ic_settings.xml @@ -15,8 +15,8 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" + android:width="@dimen/pip_action_inner_size" + android:height="@dimen/pip_action_inner_size" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_skip_next_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_skip_next_white.xml index 040c7e642241..6c5542131ab8 100644 --- a/libs/WindowManager/Shell/res/drawable/pip_ic_skip_next_white.xml +++ b/libs/WindowManager/Shell/res/drawable/pip_ic_skip_next_white.xml @@ -15,8 +15,8 @@ Copyright (C) 2017 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" + android:width="@dimen/pip_action_inner_size" + android:height="@dimen/pip_action_inner_size" android:viewportWidth="24" android:viewportHeight="24"> @@ -25,4 +25,4 @@ Copyright (C) 2017 The Android Open Source Project android:pathData="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z" /> <path android:pathData="M0 0h24v24H0z" /> -</vector>
\ No newline at end of file +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_skip_previous_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_skip_previous_white.xml index b9b94b73a00f..6b5382b662b1 100644 --- a/libs/WindowManager/Shell/res/drawable/pip_ic_skip_previous_white.xml +++ b/libs/WindowManager/Shell/res/drawable/pip_ic_skip_previous_white.xml @@ -15,8 +15,8 @@ Copyright (C) 2017 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" + android:width="@dimen/pip_action_inner_size" + android:height="@dimen/pip_action_inner_size" android:viewportWidth="24" android:viewportHeight="24"> @@ -25,4 +25,4 @@ Copyright (C) 2017 The Android Open Source Project android:pathData="M6 6h2v12H6zm3.5 6l8.5 6V6z" /> <path android:pathData="M0 0h24v24H0z" /> -</vector>
\ No newline at end of file +</vector> diff --git a/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml b/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml index dc54caf0f14a..0190aad2d0ef 100644 --- a/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml +++ b/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml @@ -54,8 +54,8 @@ android:layout_height="wrap_content" android:layout_marginTop="6dp" android:layout_marginBottom="0dp" - android:layout_marginStart="86dp" - android:layout_marginEnd="86dp" + android:layout_marginStart="46dp" + android:layout_marginEnd="46dp" android:gravity="center_horizontal" android:fontFamily="roboto-regular" android:text="@string/one_handed_tutorial_description" diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml index b581f555c234..9fe024748610 100644 --- a/libs/WindowManager/Shell/res/layout/pip_menu.xml +++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml @@ -40,7 +40,7 @@ android:layout_height="@dimen/pip_expand_action_size" android:layout_gravity="center" android:contentDescription="@string/pip_phone_expand" - android:padding="10dp" + android:gravity="center" android:src="@drawable/pip_expand" android:background="?android:selectableItemBackgroundBorderless" /> </FrameLayout> @@ -72,8 +72,8 @@ android:id="@+id/settings" android:layout_width="@dimen/pip_action_size" android:layout_height="@dimen/pip_action_size" - android:padding="@dimen/pip_action_padding" android:contentDescription="@string/pip_phone_settings" + android:gravity="center" android:src="@drawable/pip_ic_settings" android:background="?android:selectableItemBackgroundBorderless" /> @@ -81,8 +81,8 @@ android:id="@+id/dismiss" android:layout_width="@dimen/pip_action_size" android:layout_height="@dimen/pip_action_size" - android:padding="@dimen/pip_action_padding" android:contentDescription="@string/pip_phone_close" + android:gravity="center" android:src="@drawable/pip_ic_close_white" android:background="?android:selectableItemBackgroundBorderless" /> </LinearLayout> diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_action.xml b/libs/WindowManager/Shell/res/layout/pip_menu_action.xml index 7a026ca63f50..a733b31d9fb0 100644 --- a/libs/WindowManager/Shell/res/layout/pip_menu_action.xml +++ b/libs/WindowManager/Shell/res/layout/pip_menu_action.xml @@ -14,10 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. --> -<ImageButton +<com.android.wm.shell.pip.phone.PipMenuActionView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="@dimen/pip_action_size" android:layout_height="@dimen/pip_action_size" - android:padding="@dimen/pip_action_padding" android:background="?android:selectableItemBackgroundBorderless" - android:forceHasOverlappingRendering="false" /> + android:forceHasOverlappingRendering="false"> + + <ImageView + android:id="@+id/image" + android:layout_width="@dimen/pip_action_inner_size" + android:layout_height="@dimen/pip_action_inner_size" + android:layout_gravity="center" + android:scaleType="fitXY"/> + +</com.android.wm.shell.pip.phone.PipMenuActionView> diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml index 347c2b47767e..0dea87c6b7fc 100644 --- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml +++ b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml @@ -14,35 +14,52 @@ See the License for the specific language governing permissions and limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.wm.shell.sizecompatui.SizeCompatHintPopup + xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="@android:color/background_light" - android:orientation="vertical"> + android:layout_height="wrap_content"> - <TextView - android:layout_width="180dp" - android:layout_height="wrap_content" - android:paddingLeft="10dp" - android:paddingRight="10dp" - android:paddingTop="10dp" - android:text="@string/restart_button_description" - android:textAlignment="viewStart" - android:textColor="@android:color/primary_text_light" - android:textSize="16sp" /> - - <Button - android:id="@+id/got_it" + <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:includeFontPadding="false" - android:layout_gravity="end" - android:minHeight="36dp" - android:background="?android:attr/selectableItemBackground" - android:text="@string/got_it" - android:textAllCaps="true" - android:textColor="#3c78d8" - android:textSize="16sp" - android:textStyle="bold" /> - -</LinearLayout> + android:gravity="center" + android:clipToPadding="false" + android:padding="@dimen/bubble_elevation"> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@android:color/background_light" + android:elevation="@dimen/bubble_elevation" + android:orientation="vertical"> + + <TextView + android:layout_width="180dp" + android:layout_height="wrap_content" + android:paddingLeft="10dp" + android:paddingRight="10dp" + android:paddingTop="10dp" + android:text="@string/restart_button_description" + android:textAlignment="viewStart" + android:textColor="@android:color/primary_text_light" + android:textSize="16sp"/> + + <Button + android:id="@+id/got_it" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:includeFontPadding="false" + android:layout_gravity="end" + android:minHeight="36dp" + android:background="?android:attr/selectableItemBackground" + android:text="@string/got_it" + android:textAllCaps="true" + android:textColor="#3c78d8" + android:textSize="16sp" + android:textStyle="bold"/> + + </LinearLayout> + + </FrameLayout> + +</com.android.wm.shell.sizecompatui.SizeCompatHintPopup> diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml index ea634cfa907c..c3ae053d156d 100644 --- a/libs/WindowManager/Shell/res/values-af/strings.xml +++ b/libs/WindowManager/Shell/res/values-af/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Tik om hierdie program te herbegin en maak volskerm oop."</string> + <string name="got_it" msgid="4428750913636945527">"Het dit"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml index e4628d7b5278..c889039cffdb 100644 --- a/libs/WindowManager/Shell/res/values-am/strings.xml +++ b/libs/WindowManager/Shell/res/values-am/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string> + <string name="restart_button_description" msgid="5887656107651190519">"ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ እና ወደ ሙሉ ማያ ገጽ ይሂዱ።"</string> + <string name="got_it" msgid="4428750913636945527">"ገባኝ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml index 7b5bda72ccd4..2c89b1d4eb56 100644 --- a/libs/WindowManager/Shell/res/values-ar/strings.xml +++ b/libs/WindowManager/Shell/res/values-ar/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string> + <string name="restart_button_description" msgid="5887656107651190519">"انقر لإعادة تشغيل هذا التطبيق والانتقال إلى وضع ملء الشاشة."</string> + <string name="got_it" msgid="4428750913636945527">"حسنًا"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml index 47294c438729..0a74ac6391ce 100644 --- a/libs/WindowManager/Shell/res/values-as/strings.xml +++ b/libs/WindowManager/Shell/res/values-as/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string> + <string name="restart_button_description" msgid="5887656107651190519">"এপ্টো ৰিষ্টাৰ্ট কৰিবলৈ আৰু পূৰ্ণ স্ক্ৰীন ব্যৱহাৰ কৰিবলৈ টিপক।"</string> + <string name="got_it" msgid="4428750913636945527">"বুজি পালোঁ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml index 923ff79e0627..54483bfa730c 100644 --- a/libs/WindowManager/Shell/res/values-az/strings.xml +++ b/libs/WindowManager/Shell/res/values-az/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Bu tətbiqi sıfırlayaraq tam ekrana keçmək üçün toxunun."</string> + <string name="got_it" msgid="4428750913636945527">"Anladım"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml index 02e609cd5c9b..5ed79c4901cd 100644 --- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml +++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste restartovali aplikaciju i prešli u režim celog ekrana."</string> + <string name="got_it" msgid="4428750913636945527">"Važi"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml index ccea3180f64e..a9a62de6c8f9 100644 --- a/libs/WindowManager/Shell/res/values-be/strings.xml +++ b/libs/WindowManager/Shell/res/values-be/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Націсніце, каб перазапусціць гэту праграму і перайсці ў поўнаэкранны рэжым."</string> + <string name="got_it" msgid="4428750913636945527">"Зразумела"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml index d29660b9c24d..80895dc0c032 100644 --- a/libs/WindowManager/Shell/res/values-bg/strings.xml +++ b/libs/WindowManager/Shell/res/values-bg/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Докоснете, за да рестартирате това приложение в режим на цял екран."</string> + <string name="got_it" msgid="4428750913636945527">"Разбрах"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml index 84bcaf907d91..bdda799b001f 100644 --- a/libs/WindowManager/Shell/res/values-bn/strings.xml +++ b/libs/WindowManager/Shell/res/values-bn/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string> + <string name="restart_button_description" msgid="5887656107651190519">"এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন ও \'ফুল-স্ক্রিন\' মোড ব্যবহার করুন।"</string> + <string name="got_it" msgid="4428750913636945527">"বুঝেছি"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml index 85e08d7ca555..759e9b8c415b 100644 --- a/libs/WindowManager/Shell/res/values-bs/strings.xml +++ b/libs/WindowManager/Shell/res/values-bs/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da ponovo pokrenete ovu aplikaciju i aktivirate prikaz preko cijelog ekrana."</string> + <string name="got_it" msgid="4428750913636945527">"Razumijem"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml index a80b7fbec09a..202ea2083b7e 100644 --- a/libs/WindowManager/Shell/res/values-ca/strings.xml +++ b/libs/WindowManager/Shell/res/values-ca/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Toca per reiniciar aquesta aplicació i passar a pantalla completa."</string> + <string name="got_it" msgid="4428750913636945527">"Entesos"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index e8257bc8ee92..08a4201b2fae 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím aplikaci restartujete a přejdete na režim celé obrazovky"</string> + <string name="got_it" msgid="4428750913636945527">"Rozumím"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml index 17f8286e8069..395f6e75cdbc 100644 --- a/libs/WindowManager/Shell/res/values-da/strings.xml +++ b/libs/WindowManager/Shell/res/values-da/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Tryk for at genstarte denne app, og gå til fuld skærm."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml index f04796aca753..ab3461a1ebf5 100644 --- a/libs/WindowManager/Shell/res/values-de/strings.xml +++ b/libs/WindowManager/Shell/res/values-de/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Tippe, um die App im Vollbildmodus neu zu starten."</string> + <string name="got_it" msgid="4428750913636945527">"Ok"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml index cc329e8f3274..75e4379284b8 100644 --- a/libs/WindowManager/Shell/res/values-el/strings.xml +++ b/libs/WindowManager/Shell/res/values-el/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Πατήστε για επανεκκίνηση αυτής της εφαρμογής και ενεργοποίηση πλήρους οθόνης."</string> + <string name="got_it" msgid="4428750913636945527">"Το κατάλαβα"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml index 90c71c0e11ea..0d7b60ffefc6 100644 --- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml index 90c71c0e11ea..0d7b60ffefc6 100644 --- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml index 90c71c0e11ea..0d7b60ffefc6 100644 --- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml index 90c71c0e11ea..0d7b60ffefc6 100644 --- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml index d8b5b40035f7..4bff89d68963 100644 --- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Tap to restart this app and go full screen."</string> + <string name="got_it" msgid="4428750913636945527">"Got it"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml index 7244b1a1bcf5..90c4d51b995a 100644 --- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml +++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Presiona para reiniciar esta app y acceder al modo de pantalla completa."</string> + <string name="got_it" msgid="4428750913636945527">"Entendido"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml index 65e75bde573d..f3baad7a02e4 100644 --- a/libs/WindowManager/Shell/res/values-es/strings.xml +++ b/libs/WindowManager/Shell/res/values-es/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Toca para reiniciar esta aplicación e ir a la pantalla completa."</string> + <string name="got_it" msgid="4428750913636945527">"Listo"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml index 0ccfcfee85d6..9222a91b044c 100644 --- a/libs/WindowManager/Shell/res/values-et/strings.xml +++ b/libs/WindowManager/Shell/res/values-et/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Puudutage rakenduse taaskäivitamiseks ja täisekraanrežiimi aktiveerimiseks."</string> + <string name="got_it" msgid="4428750913636945527">"Selge"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml index 6682ea80cf42..4a59b59df27b 100644 --- a/libs/WindowManager/Shell/res/values-eu/strings.xml +++ b/libs/WindowManager/Shell/res/values-eu/strings.xml @@ -25,8 +25,8 @@ <string name="pip_notification_message" msgid="8854051911700302620">"Ez baduzu nahi <xliff:g id="NAME">%s</xliff:g> zerbitzuak eginbide hori erabiltzea, sakatu hau ezarpenak ireki eta aukera desaktibatzeko."</string> <string name="pip_play" msgid="3496151081459417097">"Erreproduzitu"</string> <string name="pip_pause" msgid="690688849510295232">"Pausatu"</string> - <string name="pip_skip_to_next" msgid="8403429188794867653">"Saltatu hurrengora"</string> - <string name="pip_skip_to_prev" msgid="7172158111196394092">"Saltatu aurrekora"</string> + <string name="pip_skip_to_next" msgid="8403429188794867653">"Joan hurrengora"</string> + <string name="pip_skip_to_prev" msgid="7172158111196394092">"Joan aurrekora"</string> <string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Aldatu tamaina"</string> <string name="dock_forced_resizable" msgid="1749750436092293116">"Baliteke aplikazioak ez funtzionatzea pantaila zatituan."</string> <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikazioak ez du onartzen pantaila zatitua"</string> @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Saka ezazu aplikazioa berrabiarazteko, eta ezarri pantaila osoko modua."</string> + <string name="got_it" msgid="4428750913636945527">"Ados"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index a41811d53357..fed3ea9b53f9 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string> + <string name="restart_button_description" msgid="5887656107651190519">"برای بازراهاندازی این برنامه و تغییر به حالت تمامصفحه، ضربه بزنید."</string> + <string name="got_it" msgid="4428750913636945527">"متوجهام"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml index fcdc70fc9cda..332dc9b14da2 100644 --- a/libs/WindowManager/Shell/res/values-fi/strings.xml +++ b/libs/WindowManager/Shell/res/values-fi/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Napauta, niin sovellus käynnistyy uudelleen ja siirtyy koko näytön tilaan."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml index ed822373e557..f51fc66b6b83 100644 --- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Touchez pour redémarrer cette application et passer en plein écran."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml index ad98b85d5d5d..8fa06e88cd50 100644 --- a/libs/WindowManager/Shell/res/values-fr/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Appuyez pour redémarrer cette application et activer le mode plein écran."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml index 529825e68151..56188d4e92ba 100644 --- a/libs/WindowManager/Shell/res/values-gl/strings.xml +++ b/libs/WindowManager/Shell/res/values-gl/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Toca o botón para reiniciar esta aplicación e abrila en pantalla completa."</string> + <string name="got_it" msgid="4428750913636945527">"Entendido"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml index ee23e1e967ec..b76e91010dcc 100644 --- a/libs/WindowManager/Shell/res/values-gu/strings.xml +++ b/libs/WindowManager/Shell/res/values-gu/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string> + <string name="restart_button_description" msgid="5887656107651190519">"આ ઍપ ફરીથી ચાલુ કરવા માટે ટૅપ કરીને પૂર્ણ સ્ક્રીન કરો."</string> + <string name="got_it" msgid="4428750913636945527">"સમજાઈ ગયું"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml index 34c1c85211f6..a9693865e2c4 100644 --- a/libs/WindowManager/Shell/res/values-hi/strings.xml +++ b/libs/WindowManager/Shell/res/values-hi/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"प्रबंधित करें"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string> + <string name="restart_button_description" msgid="5887656107651190519">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन पर देखने के लिए टैप करें."</string> + <string name="got_it" msgid="4428750913636945527">"ठीक है"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml index 32b21aadbb2f..769d1d26aed2 100644 --- a/libs/WindowManager/Shell/res/values-hr/strings.xml +++ b/libs/WindowManager/Shell/res/values-hr/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Dodirnite da biste ponovo pokrenuli tu aplikaciju i prikazali je na cijelom zaslonu."</string> + <string name="got_it" msgid="4428750913636945527">"Shvaćam"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml index 123b127bd5a3..05655f1bb607 100644 --- a/libs/WindowManager/Shell/res/values-hu/strings.xml +++ b/libs/WindowManager/Shell/res/values-hu/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Koppintson az alkalmazás újraindításához és a teljes képernyős mód elindításához."</string> + <string name="got_it" msgid="4428750913636945527">"Rendben"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml index b047cf131aa8..5f7495e63613 100644 --- a/libs/WindowManager/Shell/res/values-hy/strings.xml +++ b/libs/WindowManager/Shell/res/values-hy/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string> + <string name="restart_button_description" msgid="5887656107651190519">"Հպեք՝ հավելվածը վերագործարկելու և լիաէկրան ռեժիմին անցնելու համար։"</string> + <string name="got_it" msgid="4428750913636945527">"Եղավ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml index a75cdb4b2b85..2cf50c0644f1 100644 --- a/libs/WindowManager/Shell/res/values-in/strings.xml +++ b/libs/WindowManager/Shell/res/values-in/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Ketuk untuk memulai ulang aplikasi ini dan membuka layar penuh."</string> + <string name="got_it" msgid="4428750913636945527">"Oke"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml index 3b28148e3171..7a3b6a693e7a 100644 --- a/libs/WindowManager/Shell/res/values-is/strings.xml +++ b/libs/WindowManager/Shell/res/values-is/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Ýttu til að endurræsa forritið og sýna það á öllum skjánum."</string> + <string name="got_it" msgid="4428750913636945527">"Ég skil"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml index 8a2b9dbd9ba8..b061d1f592c4 100644 --- a/libs/WindowManager/Shell/res/values-it/strings.xml +++ b/libs/WindowManager/Shell/res/values-it/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Tocca per riavviare l\'app e passare alla modalità a schermo intero."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml index 20114a7fa5f3..b75ee4507e94 100644 --- a/libs/WindowManager/Shell/res/values-iw/strings.xml +++ b/libs/WindowManager/Shell/res/values-iw/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string> + <string name="restart_button_description" msgid="5887656107651190519">"צריך להקיש כדי להפעיל מחדש את האפליקציה הזו ולעבור למסך מלא."</string> + <string name="got_it" msgid="4428750913636945527">"הבנתי"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml index fbb2951a06e1..ab693d2e5045 100644 --- a/libs/WindowManager/Shell/res/values-ja/strings.xml +++ b/libs/WindowManager/Shell/res/values-ja/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string> + <string name="restart_button_description" msgid="5887656107651190519">"タップしてこのアプリを再起動すると、全画面表示になります。"</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml index f978481be23d..ef9a84f4ce2f 100644 --- a/libs/WindowManager/Shell/res/values-ka/strings.xml +++ b/libs/WindowManager/Shell/res/values-ka/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string> + <string name="restart_button_description" msgid="5887656107651190519">"შეეხეთ ამ აპის გადასატვირთად და გადადით სრულ ეკრანზე."</string> + <string name="got_it" msgid="4428750913636945527">"გასაგებია"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml index 2d27fafcc98b..13f3a4eed8c5 100644 --- a/libs/WindowManager/Shell/res/values-kk/strings.xml +++ b/libs/WindowManager/Shell/res/values-kk/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқымалы анықтама өшірілді."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string> + <string name="got_it" msgid="4428750913636945527">"Түсінікті"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml index d503b7a5edca..134d3c2334f0 100644 --- a/libs/WindowManager/Shell/res/values-km/strings.xml +++ b/libs/WindowManager/Shell/res/values-km/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោលសារលេចឡើង។"</string> + <string name="restart_button_description" msgid="5887656107651190519">"ចុចដើម្បីចាប់ផ្ដើមកម្មវិធីនេះឡើងវិញ រួចចូលប្រើពេញអេក្រង់។"</string> + <string name="got_it" msgid="4428750913636945527">"យល់ហើយ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml index 3d61d84f4810..c8b3389a3a60 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string> + <string name="restart_button_description" msgid="5887656107651190519">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಮತ್ತು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್ನಲ್ಲಿ ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> + <string name="got_it" msgid="4428750913636945527">"ಅರ್ಥವಾಯಿತು"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml index ea7ad56bf9d2..b29612e337f2 100644 --- a/libs/WindowManager/Shell/res/values-ko/strings.xml +++ b/libs/WindowManager/Shell/res/values-ko/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string> + <string name="restart_button_description" msgid="5887656107651190519">"탭하여 이 앱을 다시 시작하고 전체 화면으로 이동합니다."</string> + <string name="got_it" msgid="4428750913636945527">"확인"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml index 611b2d60a8c1..530d40a79ca3 100644 --- a/libs/WindowManager/Shell/res/values-ky/strings.xml +++ b/libs/WindowManager/Shell/res/values-ky/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Бул колдонмону өчүрүп күйгүзүп, толук экранга өтүү үчүн таптап коюңуз."</string> + <string name="got_it" msgid="4428750913636945527">"Түшүндүм"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml index a1c998c078de..5ccf164b3d67 100644 --- a/libs/WindowManager/Shell/res/values-lo/strings.xml +++ b/libs/WindowManager/Shell/res/values-lo/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string> + <string name="restart_button_description" msgid="5887656107651190519">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ ແລະ ໃຊ້ແບບເຕັມຈໍ."</string> + <string name="got_it" msgid="4428750913636945527">"ເຂົ້າໃຈແລ້ວ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml index b2ccd5709e21..1433312ded60 100644 --- a/libs/WindowManager/Shell/res/values-lt/strings.xml +++ b/libs/WindowManager/Shell/res/values-lt/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Palieskite, kad paleistumėte iš naujo šią programą ir įjungtumėte viso ekrano režimą."</string> + <string name="got_it" msgid="4428750913636945527">"Supratau"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml index e6d0c7725bbf..fb297b8f2990 100644 --- a/libs/WindowManager/Shell/res/values-lv/strings.xml +++ b/libs/WindowManager/Shell/res/values-lv/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Pieskarieties, lai restartētu šo lietotni un pārietu pilnekrāna režīmā."</string> + <string name="got_it" msgid="4428750913636945527">"Labi"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml index 43f2881fd553..80b33293af34 100644 --- a/libs/WindowManager/Shell/res/values-mk/strings.xml +++ b/libs/WindowManager/Shell/res/values-mk/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Допрете за да ја рестартирате апликацијава и да ја отворите на цел екран."</string> + <string name="got_it" msgid="4428750913636945527">"Сфатив"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml index e675861166a3..5d47911137ec 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"ബബ്ൾ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബ്ൾ ഡിസ്മിസ് ചെയ്തു."</string> + <string name="restart_button_description" msgid="5887656107651190519">"ഈ ആപ്പ് റീസ്റ്റാർട്ട് ചെയ്ത് പൂർണ്ണ സ്ക്രീനിലേക്ക് മാറാൻ ടാപ്പ് ചെയ്യുക."</string> + <string name="got_it" msgid="4428750913636945527">"മനസ്സിലായി"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml index 044fd9fa7544..a5e7f957b829 100644 --- a/libs/WindowManager/Shell/res/values-mn/strings.xml +++ b/libs/WindowManager/Shell/res/values-mn/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Энэ аппыг дахин эхлүүлж, бүтэн дэлгэцэд орохын тулд товшино уу."</string> + <string name="got_it" msgid="4428750913636945527">"Ойлголоо"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml index e838cf59331e..0450189c515d 100644 --- a/libs/WindowManager/Shell/res/values-mr/strings.xml +++ b/libs/WindowManager/Shell/res/values-mr/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string> + <string name="restart_button_description" msgid="5887656107651190519">"हे अॅप रीस्टार्ट करण्यासाठी आणि फुल स्क्रीन करण्यासाठी टॅप करा."</string> + <string name="got_it" msgid="4428750913636945527">"समजले"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml index 6664f38f3879..c0c1cbd6ed3c 100644 --- a/libs/WindowManager/Shell/res/values-ms/strings.xml +++ b/libs/WindowManager/Shell/res/values-ms/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Ketik untuk memulakan semula apl ini dan menggunakan skrin penuh."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml index 9681d14a6a88..0d78f89d9e54 100644 --- a/libs/WindowManager/Shell/res/values-my/strings.xml +++ b/libs/WindowManager/Shell/res/values-my/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string> + <string name="restart_button_description" msgid="5887656107651190519">"ဤအက်ပ်ကို ပြန်စပြီး ဖန်သားပြင်အပြည့်လုပ်ရန် တို့ပါ။"</string> + <string name="got_it" msgid="4428750913636945527">"ရပြီ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index 986e890dfe3a..fab0c0cacef4 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Trykk for å starte denne appen på nytt og vise den i fullskjerm."</string> + <string name="got_it" msgid="4428750913636945527">"Greit"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml index 0369c6dd2831..882ac374fde9 100644 --- a/libs/WindowManager/Shell/res/values-ne/strings.xml +++ b/libs/WindowManager/Shell/res/values-ne/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string> + <string name="restart_button_description" msgid="5887656107651190519">"यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस् र फुल स्क्रिन मोडमा जानुहोस्।"</string> + <string name="got_it" msgid="4428750913636945527">"बुझेँ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml index 26c276e7e690..1527e8983079 100644 --- a/libs/WindowManager/Shell/res/values-nl/strings.xml +++ b/libs/WindowManager/Shell/res/values-nl/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Tik om deze app opnieuw te starten en te openen op het volledige scherm."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml index 27f16226a421..50d20076a26f 100644 --- a/libs/WindowManager/Shell/res/values-or/strings.xml +++ b/libs/WindowManager/Shell/res/values-or/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string> + <string name="restart_button_description" msgid="5887656107651190519">"ଏହି ଆପକୁ ରିଷ୍ଟାର୍ଟ କରି ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string> + <string name="got_it" msgid="4428750913636945527">"ବୁଝିଗଲି"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml index 96688b952d66..dd3d26e56b4c 100644 --- a/libs/WindowManager/Shell/res/values-pa/strings.xml +++ b/libs/WindowManager/Shell/res/values-pa/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string> + <string name="restart_button_description" msgid="5887656107651190519">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਪੂਰੀ ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਓ।"</string> + <string name="got_it" msgid="4428750913636945527">"ਸਮਝ ਲਿਆ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml index 6b640b54f898..ca2bdcb270da 100644 --- a/libs/WindowManager/Shell/res/values-pl/strings.xml +++ b/libs/WindowManager/Shell/res/values-pl/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string> + <string name="restart_button_description" msgid="5887656107651190519">"Kliknij, by uruchomić tę aplikację ponownie i przejść w tryb pełnoekranowy."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml index 465d2d17a5e7..bdd0d4b8baec 100644 --- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string> + <string name="got_it" msgid="4428750913636945527">"Ok"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml index df841bf3eda4..6661b0506c5e 100644 --- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar esta app e ficar em ecrã inteiro."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml index 465d2d17a5e7..bdd0d4b8baec 100644 --- a/libs/WindowManager/Shell/res/values-pt/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Toque para reiniciar o app e usar tela cheia."</string> + <string name="got_it" msgid="4428750913636945527">"Ok"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml index 55a437668b22..9112543c8f60 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Atingeți ca să reporniți aplicația și să treceți în modul ecran complet."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml index 8ae00d28f896..5120136e3c68 100644 --- a/libs/WindowManager/Shell/res/values-ru/strings.xml +++ b/libs/WindowManager/Shell/res/values-ru/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Нажмите, чтобы перезапустить приложение и перейти в полноэкранный режим."</string> + <string name="got_it" msgid="4428750913636945527">"ОК"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml index 081926fd101b..e1d9a825a004 100644 --- a/libs/WindowManager/Shell/res/values-si/strings.xml +++ b/libs/WindowManager/Shell/res/values-si/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string> + <string name="restart_button_description" msgid="5887656107651190519">"මෙම යෙදුම යළි ඇරඹීමට සහ පූර්ණ තිරයට යාමට තට්ටු කරන්න."</string> + <string name="got_it" msgid="4428750913636945527">"තේරුණා"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml index 24fded7ebb04..c88099b35c0d 100644 --- a/libs/WindowManager/Shell/res/values-sk/strings.xml +++ b/libs/WindowManager/Shell/res/values-sk/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Klepnutím reštartujete túto aplikáciu a prejdete do režimu celej obrazovky."</string> + <string name="got_it" msgid="4428750913636945527">"Dobre"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml index 3f425302a5ac..42d7be7a146d 100644 --- a/libs/WindowManager/Shell/res/values-sl/strings.xml +++ b/libs/WindowManager/Shell/res/values-sl/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Dotaknite se za vnovični zagon te aplikacije in preklop v celozaslonski način."</string> + <string name="got_it" msgid="4428750913636945527">"Razumem"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml index ddae724e7569..1f373b53d0a4 100644 --- a/libs/WindowManager/Shell/res/values-sq/strings.xml +++ b/libs/WindowManager/Shell/res/values-sq/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Trokit për ta rinisur këtë aplikacion dhe për të kaluar në ekranin e plotë."</string> + <string name="got_it" msgid="4428750913636945527">"E kuptova"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml index 74c9ac0867e3..2bbbbf9e0714 100644 --- a/libs/WindowManager/Shell/res/values-sr/strings.xml +++ b/libs/WindowManager/Shell/res/values-sr/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Додирните да бисте рестартовали апликацију и прешли у режим целог екрана."</string> + <string name="got_it" msgid="4428750913636945527">"Важи"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml index 81328a836345..692b5ed8576f 100644 --- a/libs/WindowManager/Shell/res/values-sv/strings.xml +++ b/libs/WindowManager/Shell/res/values-sv/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Tryck för att starta om appen i helskärmsläge."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml index 4559832b1d85..61c95ee183a0 100644 --- a/libs/WindowManager/Shell/res/values-sw/strings.xml +++ b/libs/WindowManager/Shell/res/values-sw/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Gusa ili uzime na uwashe programu hii, kisha nenda kwenye skrini nzima."</string> + <string name="got_it" msgid="4428750913636945527">"Nimeelewa"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml index 586ee94a1098..32a925a7994e 100644 --- a/libs/WindowManager/Shell/res/values-ta/strings.xml +++ b/libs/WindowManager/Shell/res/values-ta/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string> + <string name="restart_button_description" msgid="5887656107651190519">"தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கலாம், முழுத்திரையில் பார்க்கலாம்."</string> + <string name="got_it" msgid="4428750913636945527">"சரி"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index 4e85b4371220..3db12e7d5661 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string> + <string name="restart_button_description" msgid="5887656107651190519">"ఈ యాప్ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేసి, ఆపై పూర్తి స్క్రీన్లోకి వెళ్లండి."</string> + <string name="got_it" msgid="4428750913636945527">"అర్థమైంది"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml index 66c701812ce8..7df76e84cbb7 100644 --- a/libs/WindowManager/Shell/res/values-th/strings.xml +++ b/libs/WindowManager/Shell/res/values-th/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string> + <string name="restart_button_description" msgid="5887656107651190519">"แตะเพื่อรีสตาร์ทแอปนี้และแสดงแบบเต็มหน้าจอ"</string> + <string name="got_it" msgid="4428750913636945527">"รับทราบ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml index a76bf6f1350c..d6c2784f764f 100644 --- a/libs/WindowManager/Shell/res/values-tl/strings.xml +++ b/libs/WindowManager/Shell/res/values-tl/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string> + <string name="restart_button_description" msgid="5887656107651190519">"I-tap para i-restart ang app na ito at mag-full screen."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml index b3276dad50e7..47d5966549ef 100644 --- a/libs/WindowManager/Shell/res/values-tr/strings.xml +++ b/libs/WindowManager/Shell/res/values-tr/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Bu uygulamayı yeniden başlatmak ve tam ekrana geçmek için dokunun."</string> + <string name="got_it" msgid="4428750913636945527">"Anladım"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml index 8e303cf45a39..c57f16f059df 100644 --- a/libs/WindowManager/Shell/res/values-uk/strings.xml +++ b/libs/WindowManager/Shell/res/values-uk/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Натисніть, щоб перезапустити додаток і перейти в повноекранний режим."</string> + <string name="got_it" msgid="4428750913636945527">"Зрозуміло"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml index 4b0adc640ddd..97a22e77c069 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string> + <string name="restart_button_description" msgid="5887656107651190519">"یہ ایپ دوبارہ شروع کرنے کے لیے تھپتھپائیں اور پوری اسکرین پر جائیں۔"</string> + <string name="got_it" msgid="4428750913636945527">"سمجھ آ گئی"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml index 74b135d44522..4e91e7624434 100644 --- a/libs/WindowManager/Shell/res/values-uz/strings.xml +++ b/libs/WindowManager/Shell/res/values-uz/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Bu ilovani qaytadan ishga tushirish va butun ekranda ochish uchun bosing."</string> + <string name="got_it" msgid="4428750913636945527">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml index ce372317b0b8..169e986f7721 100644 --- a/libs/WindowManager/Shell/res/values-vi/strings.xml +++ b/libs/WindowManager/Shell/res/values-vi/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Nhấn để khởi động lại ứng dụng này và xem ở chế độ toàn màn hình."</string> + <string name="got_it" msgid="4428750913636945527">"Tôi hiểu"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml index 3143130fa4ce..1999703ddd2a 100644 --- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string> + <string name="restart_button_description" msgid="5887656107651190519">"点按即可重启此应用并进入全屏模式。"</string> + <string name="got_it" msgid="4428750913636945527">"知道了"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index 4f8bfe016f6f..f82d6d5e771b 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string> + <string name="restart_button_description" msgid="5887656107651190519">"輕按即可重新開啟此應用程式並放大至全螢幕。"</string> + <string name="got_it" msgid="4428750913636945527">"知道了"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml index 6fb8ed963ba7..596e7c717aa2 100644 --- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string> + <string name="restart_button_description" msgid="5887656107651190519">"輕觸即可重新啟動這個應用程式並進入全螢幕模式。"</string> + <string name="got_it" msgid="4428750913636945527">"我知道了"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml index cab277647d26..3fed41f27c0a 100644 --- a/libs/WindowManager/Shell/res/values-zu/strings.xml +++ b/libs/WindowManager/Shell/res/values-zu/strings.xml @@ -69,4 +69,6 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string> + <string name="restart_button_description" msgid="5887656107651190519">"Thepha ukuze uqale kabusha lolu hlelo lokusebenza uphinde uye kusikrini esigcwele."</string> + <string name="got_it" msgid="4428750913636945527">"Ngiyezwa"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 583964b2f4a4..75bed3777a9d 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -21,14 +21,20 @@ <dimen name="floating_dismiss_gradient_height">250dp</dimen> <!-- The padding around a PiP actions. --> - <dimen name="pip_action_padding">12dp</dimen> + <dimen name="pip_action_padding">16dp</dimen> <!-- The height of the PiP actions container in which the actions are vertically centered. --> <dimen name="pip_action_size">48dp</dimen> - <!-- The width and height of the PiP expand action. --> + <!-- The width and height of the PiP action asset drawn within the container. --> + <dimen name="pip_action_inner_size">20dp</dimen> + + <!-- The width and height of the PiP expand action container. --> <dimen name="pip_expand_action_size">60dp</dimen> + <!-- The width and height of the PiP expand action asset drawn within the container. --> + <dimen name="pip_expand_action_inner_size">28dp</dimen> + <!-- The padding between actions in the PiP in landscape Note that the PiP does not reflect the configuration of the device, so we can't use -land resources. --> <dimen name="pip_between_action_padding_land">8dp</dimen> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java index 5992447bd6da..46884fefd69c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java @@ -157,6 +157,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, }); options.setLaunchCookie(launchCookie); options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + options.setRemoveWithTaskOrganizer(true); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java index 59271e9fb63c..e6e6d4a1934f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/WindowManagerShellWrapper.java @@ -22,7 +22,7 @@ import android.os.RemoteException; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PinnedStackListenerForwarder; -import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedStackListener; +import com.android.wm.shell.pip.PinnedStackListenerForwarder.PinnedTaskListener; /** * The singleton wrapper to communicate between WindowManagerService and WMShell features @@ -46,7 +46,7 @@ public class WindowManagerShellWrapper { * Adds a pinned stack listener, which will receive updates from the window manager service * along with any other pinned stack listeners that were added via this method. */ - public void addPinnedStackListener(PinnedStackListener listener) + public void addPinnedStackListener(PinnedTaskListener listener) throws RemoteException { mPinnedStackListenerForwarder.addListener(listener); mPinnedStackListenerForwarder.register(DEFAULT_DISPLAY); @@ -55,7 +55,7 @@ public class WindowManagerShellWrapper { /** * Removes a pinned stack listener. */ - public void removePinnedStackListener(PinnedStackListener listener) { + public void removePinnedStackListener(PinnedTaskListener listener) { mPinnedStackListenerForwarder.removeListener(listener); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index 79f9dcd8a1fb..562b32b41dd2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -34,6 +34,7 @@ import androidx.annotation.Nullable; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitLayout; @@ -57,12 +58,14 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan private final AppPairsController mController; private final SyncTransactionQueue mSyncQueue; private final DisplayController mDisplayController; + private final DisplayImeController mDisplayImeController; private SplitLayout mSplitLayout; AppPair(AppPairsController controller) { mController = controller; mSyncQueue = controller.getSyncTransactionQueue(); mDisplayController = controller.getDisplayController(); + mDisplayImeController = controller.getDisplayImeController(); } int getRootTaskId() { @@ -97,7 +100,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan mSplitLayout = new SplitLayout(TAG + "SplitDivider", mDisplayController.getDisplayContext(mRootTaskInfo.displayId), mRootTaskInfo.configuration, this /* layoutChangeListener */, - b -> b.setParent(mRootTaskLeash)); + b -> b.setParent(mRootTaskLeash), mDisplayImeController); final WindowContainerToken token1 = task1.token; final WindowContainerToken token2 = task2.token; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java index 0415f12496f2..b159333e9a0e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java @@ -23,17 +23,16 @@ import android.util.Slog; import android.util.SparseArray; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import java.io.PrintWriter; -import java.util.concurrent.TimeUnit; /** * Class manages app-pairs multitasking mode and implements the main interface {@link AppPairs}. @@ -50,12 +49,15 @@ public class AppPairsController { // Active app-pairs mapped by root task id key. private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>(); private final DisplayController mDisplayController; + private final DisplayImeController mDisplayImeController; public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, - DisplayController displayController, ShellExecutor mainExecutor) { + DisplayController displayController, ShellExecutor mainExecutor, + DisplayImeController displayImeController) { mTaskOrganizer = organizer; mSyncQueue = syncQueue; mDisplayController = displayController; + mDisplayImeController = displayImeController; mMainExecutor = mainExecutor; } @@ -130,18 +132,22 @@ public class AppPairsController { } } - public ShellTaskOrganizer getTaskOrganizer() { + ShellTaskOrganizer getTaskOrganizer() { return mTaskOrganizer; } - public SyncTransactionQueue getSyncTransactionQueue() { + SyncTransactionQueue getSyncTransactionQueue() { return mSyncQueue; } - public DisplayController getDisplayController() { + DisplayController getDisplayController() { return mDisplayController; } + DisplayImeController getDisplayImeController() { + return mDisplayImeController; + } + public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 0ee1f0642352..8697be9db3fd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -124,6 +124,7 @@ public class Bubble implements BubbleViewProvider { private int mDesiredHeight; @DimenRes private int mDesiredHeightResId; + private int mTaskId; /** for logging **/ @Nullable @@ -162,7 +163,7 @@ public class Bubble implements BubbleViewProvider { */ Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo, final int desiredHeight, final int desiredHeightResId, @Nullable final String title, - Executor mainExecutor) { + int taskId, Executor mainExecutor) { Objects.requireNonNull(key); Objects.requireNonNull(shortcutInfo); mMetadataShortcutId = shortcutInfo.getId(); @@ -178,6 +179,7 @@ public class Bubble implements BubbleViewProvider { mTitle = title; mShowBubbleUpdateDot = false; mMainExecutor = mainExecutor; + mTaskId = taskId; } @VisibleForTesting(visibility = PRIVATE) @@ -197,6 +199,7 @@ public class Bubble implements BubbleViewProvider { }); }; mMainExecutor = mainExecutor; + mTaskId = INVALID_TASK_ID; setEntry(entry); } @@ -520,7 +523,7 @@ public class Bubble implements BubbleViewProvider { */ @Override public int getTaskId() { - return mExpandedView != null ? mExpandedView.getTaskId() : INVALID_TASK_ID; + return mExpandedView != null ? mExpandedView.getTaskId() : mTaskId; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 047df5ba7ca9..1320780bfb8f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1213,7 +1213,7 @@ public class BubbleController { /** PinnedStackListener that dispatches IME visibility updates to the stack. */ //TODO(b/170442945): Better way to do this / insets listener? - private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener { + private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedTaskListener { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { if (mStackView != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt index 3108b02cc010..241755227af0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt @@ -28,7 +28,6 @@ import com.android.wm.shell.bubbles.storage.BubbleEntity import com.android.wm.shell.bubbles.storage.BubblePersistentRepository import com.android.wm.shell.bubbles.storage.BubbleVolatileRepository import com.android.wm.shell.common.ShellExecutor -import com.android.wm.shell.common.annotations.ExternalThread import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -36,8 +35,11 @@ import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.launch import kotlinx.coroutines.yield -internal class BubbleDataRepository(context: Context, private val launcherApps: LauncherApps, - private val mainExecutor : ShellExecutor) { +internal class BubbleDataRepository( + context: Context, + private val launcherApps: LauncherApps, + private val mainExecutor: ShellExecutor +) { private val volatileRepository = BubbleVolatileRepository(launcherApps) private val persistentRepository = BubblePersistentRepository(context) @@ -78,7 +80,8 @@ internal class BubbleDataRepository(context: Context, private val launcherApps: b.key, b.rawDesiredHeight, b.rawDesiredHeightResId, - b.title + b.title, + b.taskId ) } } @@ -168,6 +171,7 @@ internal class BubbleDataRepository(context: Context, private val launcherApps: entity.desiredHeight, entity.desiredHeightResId, entity.title, + entity.taskId, mainExecutor ) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java index 19c3cf9c462a..57a2b6c7b9db 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java @@ -227,24 +227,30 @@ public class BubbleFlyoutView extends FrameLayout { /* * Fade animation for consecutive flyouts. */ - void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, float stackY) { + void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, PointF stackPos, + boolean hideDot) { final Runnable afterFadeOut = () -> { updateFlyoutMessage(flyoutMessage, parentWidth); // Wait for TextViews to layout with updated height. post(() -> { - mFlyoutY = stackY + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f; - fade(true /* in */, () -> {} /* after */); + fade(true /* in */, stackPos, hideDot, () -> {} /* after */); } /* after */ ); }; - fade(false /* in */, afterFadeOut); + fade(false /* in */, stackPos, hideDot, afterFadeOut); } /* * Fade-out above or fade-in from below. */ - private void fade(boolean in, Runnable afterFade) { + private void fade(boolean in, PointF stackPos, boolean hideDot, Runnable afterFade) { + mFlyoutY = stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f; + setAlpha(in ? 0f : 1f); setTranslationY(in ? mFlyoutY + FLYOUT_FADE_Y : mFlyoutY); + updateFlyoutX(stackPos.x); + setTranslationX(mRestingTranslationX); + updateDot(stackPos, hideDot); + animate() .alpha(in ? 1f : 0f) .setDuration(in ? FLYOUT_FADE_IN_DURATION : FLYOUT_FADE_OUT_DURATION) @@ -287,6 +293,33 @@ public class BubbleFlyoutView extends FrameLayout { mMessageText.setText(flyoutMessage.message); } + void updateFlyoutX(float stackX) { + // Calculate the translation required to position the flyout next to the bubble stack, + // with the desired padding. + mRestingTranslationX = mArrowPointingLeft + ? stackX + mBubbleSize + mFlyoutSpaceFromBubble + : stackX - getWidth() - mFlyoutSpaceFromBubble; + } + + void updateDot(PointF stackPos, boolean hideDot) { + // Calculate the difference in size between the flyout and the 'dot' so that we can + // transform into the dot later. + final float newDotSize = hideDot ? 0f : mNewDotSize; + mFlyoutToDotWidthDelta = getWidth() - newDotSize; + mFlyoutToDotHeightDelta = getHeight() - newDotSize; + + // Calculate the translation values needed to be in the correct 'new dot' position. + final float adjustmentForScaleAway = hideDot ? 0f : (mOriginalDotSize / 2f); + final float dotPositionX = stackPos.x + mDotCenter[0] - adjustmentForScaleAway; + final float dotPositionY = stackPos.y + mDotCenter[1] - adjustmentForScaleAway; + + final float distanceFromFlyoutLeftToDotCenterX = mRestingTranslationX - dotPositionX; + final float distanceFromLayoutTopToDotCenterY = mFlyoutY - dotPositionY; + + mTranslationXWhenDot = -distanceFromFlyoutLeftToDotCenterX; + mTranslationYWhenDot = -distanceFromLayoutTopToDotCenterY; + } + /** Configures the flyout, collapsed into dot form. */ void setupFlyoutStartingAsDot( Bubble.FlyoutMessage flyoutMessage, @@ -322,29 +355,8 @@ public class BubbleFlyoutView extends FrameLayout { mFlyoutY = stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f; setTranslationY(mFlyoutY); - - // Calculate the translation required to position the flyout next to the bubble stack, - // with the desired padding. - mRestingTranslationX = mArrowPointingLeft - ? stackPos.x + mBubbleSize + mFlyoutSpaceFromBubble - : stackPos.x - getWidth() - mFlyoutSpaceFromBubble; - - // Calculate the difference in size between the flyout and the 'dot' so that we can - // transform into the dot later. - final float newDotSize = hideDot ? 0f : mNewDotSize; - mFlyoutToDotWidthDelta = getWidth() - newDotSize; - mFlyoutToDotHeightDelta = getHeight() - newDotSize; - - // Calculate the translation values needed to be in the correct 'new dot' position. - final float adjustmentForScaleAway = hideDot ? 0f : (mOriginalDotSize / 2f); - final float dotPositionX = stackPos.x + mDotCenter[0] - adjustmentForScaleAway; - final float dotPositionY = stackPos.y + mDotCenter[1] - adjustmentForScaleAway; - - final float distanceFromFlyoutLeftToDotCenterX = mRestingTranslationX - dotPositionX; - final float distanceFromLayoutTopToDotCenterY = mFlyoutY - dotPositionY; - - mTranslationXWhenDot = -distanceFromFlyoutLeftToDotCenterX; - mTranslationYWhenDot = -distanceFromLayoutTopToDotCenterY; + updateFlyoutX(stackPos.x); + updateDot(stackPos, hideDot); if (onLayoutComplete != null) { onLayoutComplete.run(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index a3edc20e242a..78820a8fa870 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -1539,19 +1539,16 @@ public class BubbleStackView extends FrameLayout * Update bubble order and pointer position. */ public void updateBubbleOrder(List<Bubble> bubbles) { - if (isExpansionAnimating()) { - return; - } final Runnable reorder = () -> { for (int i = 0; i < bubbles.size(); i++) { Bubble bubble = bubbles.get(i); mBubbleContainer.reorderView(bubble.getIconView(), i); } }; - if (mIsExpanded) { + if (mIsExpanded || isExpansionAnimating()) { reorder.run(); updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */); - } else { + } else if (!isExpansionAnimating()) { List<View> bubbleViews = bubbles.stream() .map(b -> b.getIconView()).collect(Collectors.toList()); mStackAnimationController.animateReorder(bubbleViews, reorder); @@ -2434,7 +2431,7 @@ public class BubbleStackView extends FrameLayout if (mFlyout.getVisibility() == View.VISIBLE) { mFlyout.animateUpdate(bubble.getFlyoutMessage(), getWidth(), - mStackAnimationController.getStackPosition().y); + mStackAnimationController.getStackPosition(), !bubble.showDot()); } else { mFlyout.setVisibility(INVISIBLE); mFlyout.setupFlyoutStartingAsDot(bubble.getFlyoutMessage(), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt index aeba302bf487..d5cab5af42e4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleEntity.kt @@ -25,5 +25,6 @@ data class BubbleEntity( val key: String, val desiredHeight: Int, @DimenRes val desiredHeightResId: Int, - val title: String? = null + val title: String? = null, + val taskId: Int ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt index fe72bd301e04..470011b136fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelper.kt @@ -15,6 +15,7 @@ */ package com.android.wm.shell.bubbles.storage +import android.app.ActivityTaskManager.INVALID_TASK_ID import android.util.Xml import com.android.internal.util.FastXmlSerializer import com.android.internal.util.XmlUtils @@ -38,6 +39,7 @@ private const val ATTR_KEY = "key" private const val ATTR_DESIRED_HEIGHT = "h" private const val ATTR_DESIRED_HEIGHT_RES_ID = "hid" private const val ATTR_TITLE = "t" +private const val ATTR_TASK_ID = "tid" /** * Writes the bubbles in xml format into given output stream. @@ -70,6 +72,7 @@ private fun writeXmlEntry(serializer: XmlSerializer, bubble: BubbleEntity) { serializer.attribute(null, ATTR_DESIRED_HEIGHT, bubble.desiredHeight.toString()) serializer.attribute(null, ATTR_DESIRED_HEIGHT_RES_ID, bubble.desiredHeightResId.toString()) bubble.title?.let { serializer.attribute(null, ATTR_TITLE, it) } + serializer.attribute(null, ATTR_TASK_ID, bubble.taskId.toString()) serializer.endTag(null, TAG_BUBBLE) } catch (e: IOException) { throw RuntimeException(e) @@ -103,7 +106,8 @@ private fun readXmlEntry(parser: XmlPullParser): BubbleEntity? { parser.getAttributeWithName(ATTR_KEY) ?: return null, parser.getAttributeWithName(ATTR_DESIRED_HEIGHT)?.toInt() ?: return null, parser.getAttributeWithName(ATTR_DESIRED_HEIGHT_RES_ID)?.toInt() ?: return null, - parser.getAttributeWithName(ATTR_TITLE) + parser.getAttributeWithName(ATTR_TITLE), + parser.getAttributeWithName(ATTR_TASK_ID)?.toInt() ?: INVALID_TASK_ID ) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java index 58a4baf39614..f118b1e0b7a3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java @@ -22,6 +22,8 @@ import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.os.Process.SYSTEM_UID; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; +import static android.util.RotationUtils.rotateBounds; +import static android.util.RotationUtils.rotateInsets; import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; @@ -37,7 +39,6 @@ import android.graphics.Rect; import android.os.SystemProperties; import android.provider.Settings; import android.util.DisplayMetrics; -import android.util.RotationUtils; import android.util.Size; import android.view.Display; import android.view.DisplayCutout; @@ -49,6 +50,7 @@ import com.android.internal.R; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * Contains information about the layout-properties of a display. This refers to internal layout @@ -81,6 +83,31 @@ public class DisplayLayout { private boolean mHasStatusBar = false; private int mNavBarFrameHeight = 0; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof DisplayLayout)) return false; + final DisplayLayout other = (DisplayLayout) o; + return mUiMode == other.mUiMode + && mWidth == other.mWidth + && mHeight == other.mHeight + && Objects.equals(mCutout, other.mCutout) + && mRotation == other.mRotation + && mDensityDpi == other.mDensityDpi + && Objects.equals(mNonDecorInsets, other.mNonDecorInsets) + && Objects.equals(mStableInsets, other.mStableInsets) + && mHasNavigationBar == other.mHasNavigationBar + && mHasStatusBar == other.mHasStatusBar + && mNavBarFrameHeight == other.mNavBarFrameHeight; + } + + @Override + public int hashCode() { + return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi, + mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar, + mNavBarFrameHeight); + } + /** * Create empty layout. */ @@ -241,38 +268,6 @@ public class DisplayLayout { } /** - * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta` - * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and - * remains at 0,0 after rotation. - * - * Only 'bounds' is mutated. - */ - public static void rotateBounds(Rect inOutBounds, Rect parentBounds, int delta) { - int rdelta = ((delta % 4) + 4) % 4; - int origLeft = inOutBounds.left; - switch (rdelta) { - case 0: - return; - case 1: - inOutBounds.left = inOutBounds.top; - inOutBounds.top = parentBounds.right - inOutBounds.right; - inOutBounds.right = inOutBounds.bottom; - inOutBounds.bottom = parentBounds.right - origLeft; - return; - case 2: - inOutBounds.left = parentBounds.right - inOutBounds.right; - inOutBounds.right = parentBounds.right - origLeft; - return; - case 3: - inOutBounds.left = parentBounds.bottom - inOutBounds.bottom; - inOutBounds.bottom = inOutBounds.right; - inOutBounds.right = parentBounds.bottom - inOutBounds.top; - inOutBounds.top = origLeft; - return; - } - } - - /** * Calculates the stable insets if we already have the non-decor insets. */ private static void convertNonDecorInsetsToStableInsets(Resources res, Rect inOutInsets, @@ -359,8 +354,7 @@ public class DisplayLayout { if (rotation == ROTATION_0) { return computeSafeInsets(cutout, displayWidth, displayHeight); } - final Insets waterfallInsets = - RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation); + final Insets waterfallInsets = rotateInsets(cutout.getWaterfallInsets(), rotation); final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); Rect[] cutoutRects = cutout.getBoundingRectsAll(); final Rect[] newBounds = new Rect[cutoutRects.length]; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index fb70cbe502b0..4bb8e9b6581f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -39,12 +39,12 @@ import android.view.IWindowSession; import android.view.IWindowSessionCallback; import android.view.InsetsSourceControl; import android.view.InsetsState; +import android.view.ScrollCaptureResponse; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.view.SurfaceControlViewHost; +import android.view.SurfaceSession; import android.view.View; import android.view.ViewGroup; -import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.WindowlessWindowManager; import android.window.ClientWindowFrames; @@ -371,7 +371,11 @@ public class SystemWindows { @Override public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { try { - callbacks.onUnavailable(); + callbacks.onScrollCaptureResponse( + new ScrollCaptureResponse.Builder() + .setDescription("Not Implemented") + .build()); + } catch (RemoteException ex) { // ignore } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java index e94080aa8db7..3b670057cb1a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java @@ -227,7 +227,7 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler. } @Override - public void onActivityDismissingDockedStack() { + public void onActivityDismissingDockedTask() { mMainHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index c27c92961c2b..b9fdaa1ab1af 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -36,11 +36,13 @@ import androidx.annotation.Nullable; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.common.DisplayImeController; /** - * Stack divider for app pair. + * Divider for multi window splits. */ -public class DividerView extends FrameLayout implements View.OnTouchListener { +public class DividerView extends FrameLayout implements View.OnTouchListener, + DisplayImeController.ImePositionProcessor { public static final long TOUCH_ANIMATION_DURATION = 150; public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200; @@ -56,6 +58,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { private boolean mMoving; private int mStartPos; private GestureDetector mDoubleTapDetector; + private boolean mInteractive; public DividerView(@NonNull Context context) { super(context); @@ -91,12 +94,19 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mTouchElevation = getResources().getDimensionPixelSize( R.dimen.docked_stack_divider_lift_elevation); mDoubleTapDetector = new GestureDetector(getContext(), new DoubleTapListener()); + mInteractive = true; setOnTouchListener(this); } @Override + public void onImeVisibilityChanged(int displayId, boolean isShowing) { + if (displayId != getDisplay().getDisplayId()) return; + setInteractive(!isShowing); + } + + @Override public boolean onTouch(View v, MotionEvent event) { - if (mSplitLayout == null) { + if (mSplitLayout == null || !mInteractive) { return false; } @@ -202,6 +212,13 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mViewHost.relayout(lp); } + private void setInteractive(boolean interactive) { + if (interactive == mInteractive) return; + mInteractive = interactive; + releaseTouching(); + mHandle.setVisibility(mInteractive ? View.VISIBLE : View.INVISIBLE); + } + private boolean isLandscape() { return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 60231df37370..bacff78e6a67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -35,6 +35,7 @@ import androidx.annotation.Nullable; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.common.DisplayImeController; /** * Records and handles layout of splits. Helps to calculate proper bounds when configuration or @@ -59,11 +60,13 @@ public class SplitLayout { public SplitLayout(String windowName, Context context, Configuration configuration, LayoutChangeListener layoutChangeListener, - SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks) { + SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks, + DisplayImeController displayImeController) { mContext = context.createConfigurationContext(configuration); mLayoutChangeListener = layoutChangeListener; mSplitWindowManager = new SplitWindowManager( - windowName, mContext, configuration, parentContainerCallbacks); + windowName, mContext, configuration, parentContainerCallbacks, + displayImeController); mDividerWindowWidth = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.docked_stack_divider_thickness); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 87f0c25c93df..f6efb0120dda 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -46,6 +46,7 @@ import android.view.WindowlessWindowManager; import androidx.annotation.Nullable; import com.android.wm.shell.R; +import com.android.wm.shell.common.DisplayImeController; /** * Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split. @@ -53,23 +54,27 @@ import com.android.wm.shell.R; public final class SplitWindowManager extends WindowlessWindowManager { private static final String TAG = SplitWindowManager.class.getSimpleName(); + private final String mWindowName; + private final DisplayImeController mDisplayImeController; private final ParentContainerCallbacks mParentContainerCallbacks; private Context mContext; private SurfaceControlViewHost mViewHost; private SurfaceControl mLeash; private boolean mResizingSplits; - private final String mWindowName; + private DividerView mDividerView; public interface ParentContainerCallbacks { void attachToParentSurface(SurfaceControl.Builder b); } public SplitWindowManager(String windowName, Context context, Configuration config, - ParentContainerCallbacks parentContainerCallbacks) { + ParentContainerCallbacks parentContainerCallbacks, + DisplayImeController displayImeController) { super(config, null /* rootSurface */, null /* hostInputToken */); mContext = context.createConfigurationContext(config); mParentContainerCallbacks = parentContainerCallbacks; mWindowName = windowName; + mDisplayImeController = displayImeController; } @Override @@ -103,14 +108,16 @@ public final class SplitWindowManager extends WindowlessWindowManager { /** Inflates {@link DividerView} on to the root surface. */ void init(SplitLayout splitLayout) { - if (mViewHost == null) { - mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + if (mDividerView != null || mViewHost != null) { + throw new UnsupportedOperationException( + "Try to inflate divider view again without release first"); } - final Rect dividerBounds = splitLayout.getDividerBounds(); - final DividerView dividerView = (DividerView) LayoutInflater.from(mContext) + mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + mDividerView = (DividerView) LayoutInflater.from(mContext) .inflate(R.layout.split_divider, null /* root */); + final Rect dividerBounds = splitLayout.getDividerBounds(); WindowManager.LayoutParams lp = new WindowManager.LayoutParams( dividerBounds.width(), dividerBounds.height(), TYPE_DOCK_DIVIDER, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH @@ -119,8 +126,9 @@ public final class SplitWindowManager extends WindowlessWindowManager { lp.token = new Binder(); lp.setTitle(mWindowName); lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; - mViewHost.setView(dividerView, lp); - dividerView.setup(splitLayout, mViewHost); + mViewHost.setView(mDividerView, lp); + mDividerView.setup(splitLayout, mViewHost); + mDisplayImeController.addPositionProcessor(mDividerView); } /** @@ -128,6 +136,11 @@ public final class SplitWindowManager extends WindowlessWindowManager { * hierarchy. */ void release() { + if (mDividerView != null) { + mDisplayImeController.removePositionProcessor(mDividerView); + mDividerView = null; + } + if (mViewHost != null){ mViewHost.release(); mViewHost = null; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java index 477ec339f1db..40244fbb4503 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java @@ -18,6 +18,7 @@ package com.android.wm.shell.legacysplitscreen; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.util.RotationUtils.rotateBounds; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_LEFT; @@ -244,7 +245,7 @@ public class LegacySplitDisplayLayout { DividerSnapAlgorithm snap = initSnapAlgorithmForRotation(context, tmpDL, dividerSize); tmpRect.set(bounds); - DisplayLayout.rotateBounds(tmpRect, displayRect, rotation - dl.rotation()); + rotateBounds(tmpRect, displayRect, dl.rotation(), rotation); rotatedDisplayRect.set(0, 0, tmpDL.width(), tmpDL.height()); final int dockSide = getPrimarySplitSide(tmpRect, rotatedDisplayRect, tmpDL.getOrientation()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java index c8f89876222e..82468ad999b4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java @@ -105,7 +105,7 @@ class WindowManagerProxy { synchronized (mDockedRect) { mTouchableRegion.set(region); } - WindowManagerGlobal.getWindowManagerService().setDockedStackDividerTouchRegion( + WindowManagerGlobal.getWindowManagerService().setDockedTaskDividerTouchRegion( mTouchableRegion); } catch (RemoteException e) { Log.w(TAG, "Failed to set touchable region: " + e); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java index 11c11f44a781..4f31c370108d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java @@ -16,13 +16,11 @@ package com.android.wm.shell.onehanded; -import androidx.annotation.NonNull; +import android.content.res.Configuration; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback; -import java.io.PrintWriter; - /** * Interface to engage one handed feature. */ @@ -54,19 +52,33 @@ public interface OneHanded { void stopOneHanded(int uiEvent); /** - * Set navigation 3 button mode enabled or disabled by users. + * Sets navigation 3 button mode enabled or disabled by users. */ void setThreeButtonModeEnabled(boolean enabled); /** - * Register callback to be notified after {@link OneHandedDisplayAreaOrganizer} + * Sets one handed feature temporary locked in enabled or disabled state, this won't change + * settings configuration. + * + * @param locked locked function in disabled(can not trigger) or enabled state. + * @param enabled function in disabled(can not trigger) or enabled state. + */ + void setLockedDisabled(boolean locked, boolean enabled); + + /** + * Registers callback to be notified after {@link OneHandedDisplayAreaOrganizer} * transition start or finish */ void registerTransitionCallback(OneHandedTransitionCallback callback); /** - * Register callback for one handed gesture, this gesture callbcak will be activated on + * Registers callback for one handed gesture, this gesture callback will be activated on * 3 button navigation mode only */ void registerGestureCallback(OneHandedGestureEventCallback callback); + + /** + * Receive onConfigurationChanged() events + */ + void onConfigChanged(Configuration newConfig); } 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 5a3c38b09ec6..a1b1de3faee2 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 @@ -23,6 +23,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.om.IOverlayManager; import android.content.om.OverlayInfo; +import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.Point; import android.os.Handler; @@ -31,6 +32,7 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.provider.Settings; import android.util.Slog; +import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityManager; import androidx.annotation.NonNull; @@ -65,6 +67,7 @@ public class OneHandedController { private volatile boolean mIsOneHandedEnabled; private volatile boolean mIsSwipeToNotificationEnabled; private boolean mTaskChangeToExit; + private boolean mLockedDisabled; private float mOffSetFraction; private final Context mContext; @@ -154,7 +157,7 @@ public class OneHandedController { OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler, mainExecutor); OneHandedGestureHandler gestureHandler = new OneHandedGestureHandler( - context, displayController, mainExecutor); + context, displayController, ViewConfiguration.get(context), mainExecutor); OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer = new OneHandedBackgroundPanelOrganizer(context, displayController, mainExecutor); OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer( @@ -222,8 +225,7 @@ public class OneHandedController { setupGesturalOverlay(); updateSettings(); - mAccessibilityManager = (AccessibilityManager) - context.getSystemService(Context.ACCESSIBILITY_SERVICE); + mAccessibilityManager = AccessibilityManager.getInstance(context); mAccessibilityManager.addAccessibilityStateChangeListener( mAccessibilityStateChangeListener); } @@ -262,6 +264,10 @@ public class OneHandedController { @VisibleForTesting void startOneHanded() { + if (isLockedDisabled()) { + Slog.d(TAG, "Temporary lock disabled"); + return; + } if (!mDisplayAreaOrganizer.isInOneHanded()) { final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction); mDisplayAreaOrganizer.scheduleOffset(0, yOffSet); @@ -345,7 +351,8 @@ public class OneHandedController { }; } - private void onEnabledSettingChanged() { + @VisibleForTesting + void onEnabledSettingChanged() { final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( mContext.getContentResolver()); mOneHandedUiEventLogger.writeEvent(enabled @@ -360,7 +367,8 @@ public class OneHandedController { mContext.getContentResolver())); } - private void onTimeoutSettingChanged() { + @VisibleForTesting + void onTimeoutSettingChanged() { final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout( mContext.getContentResolver()); int metricsId = OneHandedUiEventLogger.OneHandedSettingsTogglesEvent.INVALID.getId(); @@ -388,7 +396,8 @@ public class OneHandedController { } } - private void onTaskChangeExitSettingChanged() { + @VisibleForTesting + void onTaskChangeExitSettingChanged() { final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit( mContext.getContentResolver()); mOneHandedUiEventLogger.writeEvent(enabled @@ -398,7 +407,8 @@ public class OneHandedController { setTaskChangeToExit(enabled); } - private void onSwipeToNotificationEnabledSettingChanged() { + @VisibleForTesting + void onSwipeToNotificationEnabledSettingChanged() { final boolean enabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContext.getContentResolver()); @@ -428,21 +438,35 @@ public class OneHandedController { return displaySize; } + @VisibleForTesting + boolean isLockedDisabled() { + return mLockedDisabled; + } + private void updateOneHandedEnabled() { if (mDisplayAreaOrganizer.isInOneHanded()) { stopOneHanded(); } - // TODO Be aware to unregisterOrganizer() after animation finished - mDisplayAreaOrganizer.unregisterOrganizer(); - mBackgroundPanelOrganizer.unregisterOrganizer(); - if (mIsOneHandedEnabled) { + + mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled); + mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled); + + if (!mIsOneHandedEnabled) { + mDisplayAreaOrganizer.unregisterOrganizer(); + mBackgroundPanelOrganizer.unregisterOrganizer(); + // Do NOT register + unRegister DA in the same call + return; + } + + if (mDisplayAreaOrganizer.getDisplayAreaTokenMap().isEmpty()) { mDisplayAreaOrganizer.registerOrganizer( OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED); + } + + if (mBackgroundPanelOrganizer.getBackgroundSurface() == null) { mBackgroundPanelOrganizer.registerOrganizer( OneHandedBackgroundPanelOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL); } - mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled); - mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled); } private void setupGesturalOverlay() { @@ -452,7 +476,6 @@ public class OneHandedController { OverlayInfo info = null; try { - // TODO(b/157958539) migrate new RRO config file after S+ mOverlayManager.setHighestPriority(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT); info = mOverlayManager.getOverlayInfo(ONE_HANDED_MODE_GESTURAL_OVERLAY, USER_CURRENT); } catch (RemoteException e) { /* Do nothing */ } @@ -472,11 +495,31 @@ public class OneHandedController { } } + @VisibleForTesting + void setLockedDisabled(boolean locked, boolean enabled) { + if (enabled == mIsOneHandedEnabled) { + return; + } + mLockedDisabled = locked && !enabled; + } + + private void onConfigChanged(Configuration newConfig) { + if (mTutorialHandler != null) { + if (!mIsOneHandedEnabled + || newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { + return; + } + mTutorialHandler.onConfigurationChanged(newConfig); + } + } + public void dump(@NonNull PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG + "states: "); pw.print(innerPrefix + "mOffSetFraction="); pw.println(mOffSetFraction); + pw.print(innerPrefix + "mLockedDisabled="); + pw.println(mLockedDisabled); if (mDisplayAreaOrganizer != null) { mDisplayAreaOrganizer.dump(pw); @@ -553,6 +596,13 @@ public class OneHandedController { } @Override + public void setLockedDisabled(boolean locked, boolean enabled) { + mMainExecutor.execute(() -> { + OneHandedController.this.setLockedDisabled(locked, enabled); + }); + } + + @Override public void registerTransitionCallback(OneHandedTransitionCallback callback) { mMainExecutor.execute(() -> { OneHandedController.this.registerTransitionCallback(callback); @@ -565,5 +615,12 @@ public class OneHandedController { OneHandedController.this.registerGestureCallback(callback); }); } + + @Override + public void onConfigChanged(Configuration newConfig) { + mMainExecutor.execute(() -> { + OneHandedController.this.onConfigChanged(newConfig); + }); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index 04d1264bdd9d..4c5cc226b40f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -66,8 +66,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { private boolean mIsInOneHanded; private int mEnterExitAnimationDurationMs; - @VisibleForTesting - ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap(); + private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap(); private DisplayController mDisplayController; private OneHandedAnimationController mAnimationController; private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory @@ -203,6 +202,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { animateWindows(token, leash, fromBounds, toBounds, direction, mEnterExitAnimationDurationMs); wct.setBounds(token, toBounds); + wct.setAppBounds(token, toBounds); }); applyTransaction(wct); } @@ -232,6 +232,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { // DisplayRotationController will applyTransaction() after finish rotating if (wct != null) { wct.setBounds(token, null/* reset */); + wct.setAppBounds(token, null/* reset */); } }); tx.apply(); @@ -298,6 +299,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { return new Rect(0, 0, realSize.x, realSize.y); } + @VisibleForTesting + ArrayMap<WindowContainerToken, SurfaceControl> getDisplayAreaTokenMap() { + return mDisplayAreaTokenMap; + } + /** * Register transition callback */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java index 49b7e050c48b..91e649f98292 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java @@ -87,6 +87,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, * @param displayController {@link DisplayController} */ public OneHandedGestureHandler(Context context, DisplayController displayController, + ViewConfiguration viewConfig, ShellExecutor mainExecutor) { mDisplayController = displayController; mMainExecutor = mainExecutor; @@ -95,7 +96,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, com.android.internal.R.dimen.navigation_bar_gesture_larger_height); mDragDistThreshold = context.getResources().getDimensionPixelSize( R.dimen.gestures_onehanded_drag_threshold); - final float slop = ViewConfiguration.get(context).getScaledTouchSlop(); + final float slop = viewConfig.getScaledTouchSlop(); mSquaredSlop = slop * slop; updateIsEnabled(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java index 492130bebb30..3f72b80a7dce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java @@ -18,6 +18,7 @@ package com.android.wm.shell.onehanded; import android.content.ContentResolver; import android.content.Context; +import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; @@ -55,12 +56,14 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { private final AccessibilityManager mAccessibilityManager; private final String mPackageName; + private Context mContext; private View mTutorialView; private Point mDisplaySize = new Point(); private ContentResolver mContentResolver; private boolean mCanShowTutorial; private String mStartOneHandedDescription; private String mStopOneHandedDescription; + private boolean mIsOneHandedMode; private enum ONE_HANDED_TRIGGER_STATE { UNSET, ENTERING, EXITING @@ -92,19 +95,19 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { mTriggerState = (startValue.top == 0) ? ONE_HANDED_TRIGGER_STATE.ENTERING : ONE_HANDED_TRIGGER_STATE.EXITING; if (mCanShowTutorial && mTriggerState == ONE_HANDED_TRIGGER_STATE.ENTERING) { - createTutorialTarget(); + attachTurtorialTarget(); } } } }; public OneHandedTutorialHandler(Context context, ShellExecutor mainExecutor) { + mContext = context; context.getDisplay().getRealSize(mDisplaySize); mPackageName = context.getPackageName(); mContentResolver = context.getContentResolver(); mWindowManager = context.getSystemService(WindowManager.class); - mAccessibilityManager = (AccessibilityManager) - context.getSystemService(Context.ACCESSIBILITY_SERVICE); + mAccessibilityManager = AccessibilityManager.getInstance(context); mStartOneHandedDescription = context.getResources().getString( R.string.accessibility_action_start_one_handed); @@ -113,6 +116,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { mCanShowTutorial = (Settings.Secure.getInt(mContentResolver, Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT) ? false : true; + mIsOneHandedMode = false; final float offsetPercentageConfig = context.getResources().getFraction( R.fraction.config_one_handed_offset, 1, 1); final int sysPropPercentageConfig = SystemProperties.getInt( @@ -120,11 +124,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { mTutorialAreaHeight = Math.round(mDisplaySize.y * (sysPropPercentageConfig / 100.0f)); mainExecutor.execute(() -> { - mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, - null); - mTargetViewContainer = new FrameLayout(context); - mTargetViewContainer.setClipChildren(false); - mTargetViewContainer.addView(mTutorialView); + recreateTutorialView(mContext); }); } @@ -144,10 +144,20 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET; } + private void recreateTutorialView(Context context) { + mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, + null); + mTargetViewContainer = new FrameLayout(context); + mTargetViewContainer.setClipChildren(false); + mTargetViewContainer.addView(mTutorialView); + mTargetViewContainer.setVisibility(mIsOneHandedMode ? View.VISIBLE : View.GONE); + } + private void updateFinished(int visible, float finalPosition) { if (!canShowTutorial()) { return; } + mIsOneHandedMode = (finalPosition == 0f) ? true : false; mTargetViewContainer.setVisibility(visible); mTargetViewContainer.setTranslationY(finalPosition); } @@ -176,7 +186,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { * Adds the tutorial target view to the WindowManager and update its layout, so it's ready * to be animated in. */ - private void createTutorialTarget() { + private void attachTurtorialTarget() { if (!mTargetViewContainer.isAttachedToWindow()) { try { mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams()); @@ -242,4 +252,17 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback { mTargetViewContainer.setTransitionGroup(true); mTargetViewContainer.setTranslationY(value - mTargetViewContainer.getHeight()); } + + /** + * onConfigurationChanged events for updating tutorial text. + * @param newConfig + */ + public void onConfigurationChanged(Configuration newConfig) { + if (!mCanShowTutorial) { + return; + } + removeTutorialFromWindowManager(); + recreateTutorialView(mContext.createConfigurationContext(newConfig)); + attachTurtorialTarget(); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java index 8f8ec475a85c..b3b1ba7cd1c1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PinnedStackListenerForwarder.java @@ -20,7 +20,7 @@ import android.app.RemoteAction; import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.os.RemoteException; -import android.view.IPinnedStackListener; +import android.view.IPinnedTaskListener; import android.view.WindowManagerGlobal; import androidx.annotation.BinderThread; @@ -32,66 +32,66 @@ import java.util.ArrayList; /** * PinnedStackListener that simply forwards all calls to each listener added via * {@link #addListener}. This is necessary since calling - * {@link com.android.server.wm.WindowManagerService#registerPinnedStackListener} replaces any + * {@link com.android.server.wm.WindowManagerService#registerPinnedTaskListener} replaces any * previously set listener. */ public class PinnedStackListenerForwarder { - private final IPinnedStackListener mListenerImpl = new PinnedStackListenerImpl(); + private final IPinnedTaskListener mListenerImpl = new PinnedTaskListenerImpl(); private final ShellExecutor mMainExecutor; - private final ArrayList<PinnedStackListener> mListeners = new ArrayList<>(); + private final ArrayList<PinnedTaskListener> mListeners = new ArrayList<>(); public PinnedStackListenerForwarder(ShellExecutor mainExecutor) { mMainExecutor = mainExecutor; } /** Adds a listener to receive updates from the WindowManagerService. */ - public void addListener(PinnedStackListener listener) { + public void addListener(PinnedTaskListener listener) { mListeners.add(listener); } /** Removes a listener so it will no longer receive updates from the WindowManagerService. */ - public void removeListener(PinnedStackListener listener) { + public void removeListener(PinnedTaskListener listener) { mListeners.remove(listener); } public void register(int displayId) throws RemoteException { - WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener( + WindowManagerGlobal.getWindowManagerService().registerPinnedTaskListener( displayId, mListenerImpl); } private void onMovementBoundsChanged(boolean fromImeAdjustment) { - for (PinnedStackListener listener : mListeners) { + for (PinnedTaskListener listener : mListeners) { listener.onMovementBoundsChanged(fromImeAdjustment); } } private void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - for (PinnedStackListener listener : mListeners) { + for (PinnedTaskListener listener : mListeners) { listener.onImeVisibilityChanged(imeVisible, imeHeight); } } private void onActionsChanged(ParceledListSlice<RemoteAction> actions) { - for (PinnedStackListener listener : mListeners) { + for (PinnedTaskListener listener : mListeners) { listener.onActionsChanged(actions); } } private void onActivityHidden(ComponentName componentName) { - for (PinnedStackListener listener : mListeners) { + for (PinnedTaskListener listener : mListeners) { listener.onActivityHidden(componentName); } } private void onAspectRatioChanged(float aspectRatio) { - for (PinnedStackListener listener : mListeners) { + for (PinnedTaskListener listener : mListeners) { listener.onAspectRatioChanged(aspectRatio); } } @BinderThread - private class PinnedStackListenerImpl extends IPinnedStackListener.Stub { + private class PinnedTaskListenerImpl extends IPinnedTaskListener.Stub { @Override public void onMovementBoundsChanged(boolean fromImeAdjustment) { mMainExecutor.execute(() -> { @@ -129,10 +129,10 @@ public class PinnedStackListenerForwarder { } /** - * A counterpart of {@link IPinnedStackListener} with empty implementations. + * A counterpart of {@link IPinnedTaskListener} with empty implementations. * Subclasses can ignore those methods they do not intend to take action upon. */ - public static class PinnedStackListener { + public static class PinnedTaskListener { public void onMovementBoundsChanged(boolean fromImeAdjustment) {} public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 5ffa9885a143..a52db24aa184 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -16,6 +16,7 @@ package com.android.wm.shell.pip; +import static android.util.RotationUtils.rotateBounds; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; @@ -33,7 +34,6 @@ import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.wm.shell.animation.Interpolators; -import com.android.wm.shell.common.DisplayLayout; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -90,15 +90,15 @@ public class PipAnimationController { private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; - private PipTransitionAnimator mCurrentAnimator; - - private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = + private final ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = ThreadLocal.withInitial(() -> { AnimationHandler handler = new AnimationHandler(); handler.setProvider(new SfVsyncFrameCallbackProvider()); return handler; }); + private PipTransitionAnimator mCurrentAnimator; + public PipAnimationController(PipSurfaceTransactionHelper helper) { mSurfaceTransactionHelper = helper; } @@ -268,6 +268,7 @@ public class PipAnimationController { if (mPipAnimationCallback != null) { mPipAnimationCallback.onPipAnimationEnd(mTaskInfo, tx, this); } + mTransitionDirection = TRANSITION_DIRECTION_NONE; } @Override @@ -275,6 +276,7 @@ public class PipAnimationController { if (mPipAnimationCallback != null) { mPipAnimationCallback.onPipAnimationCancel(mTaskInfo, this); } + mTransitionDirection = TRANSITION_DIRECTION_NONE; } @Override public void onAnimationRepeat(Animator animation) {} @@ -448,7 +450,7 @@ public class PipAnimationController { // Rotate the end bounds according to the rotation delta because the display will // be rotated to the same orientation. rotatedEndRect = new Rect(endValue); - DisplayLayout.rotateBounds(rotatedEndRect, endValue, rotationDelta); + rotateBounds(rotatedEndRect, endValue, rotationDelta); } else { rotatedEndRect = null; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java index 1a4616c5f591..6afcc06aa1ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java @@ -32,6 +32,7 @@ import android.media.session.MediaController; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.UserHandle; import androidx.annotation.Nullable; @@ -76,6 +77,7 @@ public class PipMediaController { private final Context mContext; private final Handler mMainHandler; + private final HandlerExecutor mHandlerExecutor; private final MediaSessionManager mMediaSessionManager; private MediaController mMediaController; @@ -123,6 +125,7 @@ public class PipMediaController { public PipMediaController(Context context, Handler mainHandler) { mContext = context; mMainHandler = mainHandler; + mHandlerExecutor = new HandlerExecutor(mMainHandler); IntentFilter mediaControlFilter = new IntentFilter(); mediaControlFilter.addAction(ACTION_PLAY); mediaControlFilter.addAction(ACTION_PAUSE); @@ -247,8 +250,8 @@ public class PipMediaController { */ public void registerSessionListenerForCurrentUser() { mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener); - mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionsChangedListener, null, - UserHandle.CURRENT, mMainHandler); + mMediaSessionManager.addOnActiveSessionsChangedListener(null, UserHandle.CURRENT, + mHandlerExecutor, mSessionsChangedListener); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java index f8b4dd9bc621..a0a76d801cf4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUiEventLogger.java @@ -98,7 +98,16 @@ public class PipUiEventLogger { PICTURE_IN_PICTURE_CHANGE_ASPECT_RATIO(609), @UiEvent(doc = "User resize of the picture-in-picture window") - PICTURE_IN_PICTURE_RESIZE(610); + PICTURE_IN_PICTURE_RESIZE(610), + + @UiEvent(doc = "User unstashed picture-in-picture") + PICTURE_IN_PICTURE_STASH_UNSTASHED(709), + + @UiEvent(doc = "User stashed picture-in-picture to the left side") + PICTURE_IN_PICTURE_STASH_LEFT(710), + + @UiEvent(doc = "User stashed picture-in-picture to the right side") + PICTURE_IN_PICTURE_STASH_RIGHT(711); private final int mId; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 4a2a032d8d1c..9a584c67f97c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -65,6 +65,7 @@ import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipUtils; import java.io.PrintWriter; +import java.util.Objects; import java.util.function.Consumer; /** @@ -93,17 +94,20 @@ public class PipController implements PipTransitionController.PipTransitionCallb protected PhonePipMenuController mMenuController; protected PipTaskOrganizer mPipTaskOrganizer; - protected PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener = - new PipControllerPinnedStackListener(); + protected PinnedStackListenerForwarder.PinnedTaskListener mPinnedTaskListener = + new PipControllerPinnedTaskListener(); /** * Handler for display rotation changes. */ private final DisplayChangeController.OnDisplayChangingListener mRotationController = ( int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> { - if (!mPipTaskOrganizer.isInPip() || mPipTaskOrganizer.isDeferringEnterPipAnimation()) { - // Skip if we aren't in PIP or haven't actually entered PIP yet. We still need to update - // the display layout in the bounds handler in this case. + if (!mPipTaskOrganizer.isInPip() + || mPipBoundsState.getDisplayLayout().rotation() == toRotation + || mPipTaskOrganizer.isDeferringEnterPipAnimation()) { + // Skip if the same rotation has been set or we aren't in PIP or haven't actually + // entered PIP yet. We still need to update the display layout in the bounds handler + // in this case. onDisplayRotationChangedNotInPip(mContext, toRotation); // do not forget to update the movement bounds as well. updateMovementBounds(mPipBoundsState.getNormalBounds(), true /* fromRotation */, @@ -178,8 +182,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb /** * Handler for messages from the PIP controller. */ - private class PipControllerPinnedStackListener extends - PinnedStackListenerForwarder.PinnedStackListener { + private class PipControllerPinnedTaskListener extends + PinnedStackListenerForwarder.PinnedTaskListener { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { mPipBoundsState.setImeVisibility(imeVisible, imeHeight); @@ -310,7 +314,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay())); try { - mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener); + mWindowManagerShellWrapper.addPinnedStackListener(mPinnedTaskListener); } catch (RemoteException e) { Slog.e(TAG, "Failed to register pinned stack listener", e); } @@ -378,6 +382,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb } private void onDisplayChanged(DisplayLayout layout, boolean saveRestoreSnapFraction) { + if (Objects.equals(layout, mPipBoundsState.getDisplayLayout())) { + return; + } Runnable updateDisplayLayout = () -> { mPipBoundsState.setDisplayLayout(layout); updateMovementBounds(null /* toBounds */, @@ -476,8 +483,11 @@ public class PipController implements PipTransitionController.PipTransitionCallb int launcherRotation, int shelfHeight) { setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight); onDisplayRotationChangedNotInPip(mContext, launcherRotation); - return mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo, + final Rect entryBounds = mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo, pictureInPictureParams); + // sync mPipBoundsState with the newly calculated bounds. + mPipBoundsState.setNormalBounds(entryBounds); + return entryBounds; } private void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java new file mode 100644 index 000000000000..f11ae422e837 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActionView.java @@ -0,0 +1,48 @@ +/* + * 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.wm.shell.pip.phone; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.android.wm.shell.R; + +/** + * Container layout wraps single action image view drawn in PiP menu and can restrict the size of + * action image view (see pip_menu_action.xml). + */ +public class PipMenuActionView extends FrameLayout { + private ImageView mImageView; + + public PipMenuActionView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mImageView = findViewById(R.id.image); + } + + /** pass through to internal {@link #mImageView} */ + public void setImageDrawable(Drawable drawable) { + mImageView.setImageDrawable(drawable); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 1cf3a48e9575..0d64407f649f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -59,7 +59,6 @@ import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; -import android.widget.ImageButton; import android.widget.LinearLayout; import com.android.wm.shell.R; @@ -407,7 +406,7 @@ public class PipMenuView extends FrameLayout { // Ensure we have as many buttons as actions final LayoutInflater inflater = LayoutInflater.from(mContext); while (mActionsGroup.getChildCount() < mActions.size()) { - final ImageButton actionView = (ImageButton) inflater.inflate( + final PipMenuActionView actionView = (PipMenuActionView) inflater.inflate( R.layout.pip_menu_action, mActionsGroup, false); mActionsGroup.addView(actionView); } @@ -424,7 +423,8 @@ public class PipMenuView extends FrameLayout { && (stackBounds.width() > stackBounds.height()); for (int i = 0; i < mActions.size(); i++) { final RemoteAction action = mActions.get(i); - final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i); + final PipMenuActionView actionView = + (PipMenuActionView) mActionsGroup.getChildAt(i); // TODO: Check if the action drawable has changed before we reload it action.getIcon().loadDrawableAsync(mContext, d -> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index afc7b5294a2f..5e23281b3438 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -170,6 +170,7 @@ public class PipTouchHandler { mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipBoundsState = pipBoundsState; mMenuController = menuController; + mPipUiEventLogger = pipUiEventLogger; mMenuController.addListener(new PipMenuListener()); mGesture = new DefaultPipTouchGesture(); mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer, @@ -186,6 +187,8 @@ public class PipTouchHandler { () -> { if (mPipBoundsState.isStashed()) { animateToUnStashedState(); + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_UNSTASHED); mPipBoundsState.setStashed(STASH_TYPE_NONE); } else { mMenuController.showMenuWithPossibleDelay(MENU_STATE_FULL, @@ -206,8 +209,6 @@ public class PipTouchHandler { mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(), this::onAccessibilityShowMenu, this::updateMovementBounds, mainExecutor); - mPipUiEventLogger = pipUiEventLogger; - mEnableStash = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, PIP_STASHING, @@ -867,6 +868,8 @@ public class PipTouchHandler { if (mEnableStash && shouldStash(vel, getPossiblyMotionBounds())) { mMotionHelper.stashToEdge(vel.x, vel.y, this::stashEndAction /* endAction */); } else { + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_UNSTASHED); mPipBoundsState.setStashed(STASH_TYPE_NONE); mMotionHelper.flingToSnapTarget(vel.x, vel.y, this::flingEndAction /* endAction */); @@ -897,6 +900,8 @@ public class PipTouchHandler { if (!mTouchState.isWaitingForDoubleTap()) { if (mPipBoundsState.isStashed()) { animateToUnStashedState(); + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_UNSTASHED); mPipBoundsState.setStashed(STASH_TYPE_NONE); } else { // User has stalled long enough for this not to be a drag or a double tap, @@ -921,9 +926,15 @@ public class PipTouchHandler { && mPipExclusionBoundsChangeListener.get() != null) { mPipExclusionBoundsChangeListener.get().accept(mPipBoundsState.getBounds()); } - if (mPipBoundsState.getBounds().left < 0) { + if (mPipBoundsState.getBounds().left < 0 + && mPipBoundsState.getStashedState() != STASH_TYPE_LEFT) { + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_LEFT); mPipBoundsState.setStashed(STASH_TYPE_LEFT); - } else { + } else if (mPipBoundsState.getBounds().left >= 0 + && mPipBoundsState.getStashedState() != STASH_TYPE_RIGHT) { + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_RIGHT); mPipBoundsState.setStashed(STASH_TYPE_RIGHT); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index 56f183fd7303..70980191f103 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -377,7 +377,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal private void registerWmShellPinnedStackListener(WindowManagerShellWrapper wmShell) { try { - wmShell.addPinnedStackListener(new PinnedStackListenerForwarder.PinnedStackListener() { + wmShell.addPinnedStackListener(new PinnedStackListenerForwarder.PinnedTaskListener() { @Override public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { if (DEBUG) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java new file mode 100644 index 000000000000..78af9df30e6a --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java @@ -0,0 +1,71 @@ +/* + * 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.wm.shell.sizecompatui; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.drawable.RippleDrawable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.Button; +import android.widget.FrameLayout; + +import androidx.annotation.Nullable; + +import com.android.wm.shell.R; + +/** Popup to show the hint about the {@link SizeCompatRestartButton}. */ +public class SizeCompatHintPopup extends FrameLayout implements View.OnClickListener { + + private SizeCompatUILayout mLayout; + + public SizeCompatHintPopup(Context context) { + super(context); + } + + public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public SizeCompatHintPopup(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + void inject(SizeCompatUILayout layout) { + mLayout = layout; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + final Button gotItButton = findViewById(R.id.got_it); + gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY), + null /* content */, null /* mask */)); + gotItButton.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + mLayout.dismissHint(); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java index 9094d7de8d63..08a840297df1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java @@ -22,19 +22,13 @@ import android.graphics.Color; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; -import android.view.LayoutInflater; import android.view.View; -import android.view.WindowManager; -import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.PopupWindow; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; /** Button to restart the size compat activity. */ @@ -42,10 +36,6 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick View.OnLongClickListener { private SizeCompatUILayout mLayout; - private ImageButton mRestartButton; - @VisibleForTesting - PopupWindow mShowingHint; - private WindowManager.LayoutParams mWinParams; public SizeCompatRestartButton(@NonNull Context context) { super(context); @@ -67,24 +57,19 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick void inject(SizeCompatUILayout layout) { mLayout = layout; - mWinParams = layout.getWindowLayoutParams(); - } - - void remove() { - dismissHint(); } @Override protected void onFinishInflate() { super.onFinishInflate(); - mRestartButton = findViewById(R.id.size_compat_restart_button); + final ImageButton restartButton = findViewById(R.id.size_compat_restart_button); final ColorStateList color = ColorStateList.valueOf(Color.LTGRAY); final GradientDrawable mask = new GradientDrawable(); mask.setShape(GradientDrawable.OVAL); mask.setColor(color); - mRestartButton.setBackground(new RippleDrawable(color, null /* content */, mask)); - mRestartButton.setOnClickListener(this); - mRestartButton.setOnLongClickListener(this); + restartButton.setBackground(new RippleDrawable(color, null /* content */, mask)); + restartButton.setOnClickListener(this); + restartButton.setOnLongClickListener(this); } @Override @@ -94,69 +79,7 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick @Override public boolean onLongClick(View v) { - showHint(); + mLayout.onRestartButtonLongClicked(); return true; } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (mLayout.mShouldShowHint) { - mLayout.mShouldShowHint = false; - showHint(); - } - } - - @Override - public void setVisibility(@Visibility int visibility) { - if (visibility == View.GONE && mShowingHint != null) { - // Also dismiss the popup. - dismissHint(); - } - super.setVisibility(visibility); - } - - @Override - public void setLayoutDirection(int layoutDirection) { - final int gravity = SizeCompatUILayout.getGravity(layoutDirection); - if (mWinParams.gravity != gravity) { - mWinParams.gravity = gravity; - getContext().getSystemService(WindowManager.class).updateViewLayout(this, - mWinParams); - } - super.setLayoutDirection(layoutDirection); - } - - void showHint() { - if (mShowingHint != null) { - return; - } - - // TODO: popup is not attached to the button surface. Need to handle this differently for - // non-fullscreen task. - final View popupView = LayoutInflater.from(getContext()).inflate( - R.layout.size_compat_mode_hint, null); - final PopupWindow popupWindow = new PopupWindow(popupView, - LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); - popupWindow.setWindowLayoutType(mWinParams.type); - popupWindow.setElevation(getResources().getDimension(R.dimen.bubble_elevation)); - popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod); - popupWindow.setClippingEnabled(false); - popupWindow.setOnDismissListener(() -> mShowingHint = null); - mShowingHint = popupWindow; - - final Button gotItButton = popupView.findViewById(R.id.got_it); - gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY), - null /* content */, null /* mask */)); - gotItButton.setOnClickListener(view -> dismissHint()); - popupWindow.showAtLocation(mRestartButton, mWinParams.gravity, mLayout.mPopupOffsetX, - mLayout.mPopupOffsetY); - } - - void dismissHint() { - if (mShowingHint != null) { - mShowingHint.dismiss(); - mShowingHint = null; - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java index a3880f497ff3..c981adee9b5c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java @@ -50,7 +50,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang /** Whether the IME is shown on display id. */ private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1); - /** The showing buttons by task id. */ + /** The showing UIs by task id. */ private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0); /** Avoid creating display context frequently for non-default display. */ @@ -77,12 +77,12 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang } /** - * Called when the Task info changed. Creates and updates the restart button if there is an - * activity in size compat, or removes the restart button if there is no size compat activity. + * Called when the Task info changed. Creates and updates the size compat UI if there is an + * activity in size compat, or removes the UI if there is no size compat activity. * * @param displayId display the task and activity are in. * @param taskId task the activity is in. - * @param taskConfig task config to place the restart button with. + * @param taskConfig task config to place the size compat UI with. * @param sizeCompatActivity the size compat activity in the task. Can be {@code null} if the * top activity in this Task is not in size compat. * @param taskListener listener to handle the Task Surface placement. @@ -94,10 +94,10 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang // Null token means the current foreground activity is not in size compatibility mode. removeLayout(taskId); } else if (mActiveLayouts.contains(taskId)) { - // Button already exists, update the button layout. + // UI already exists, update the UI layout. updateLayout(taskId, taskConfig, sizeCompatActivity, taskListener); } else { - // Create a new restart button. + // Create a new size compat UI. createLayout(displayId, taskId, taskConfig, sizeCompatActivity, taskListener); } } @@ -106,7 +106,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang public void onDisplayRemoved(int displayId) { mDisplayContextCache.remove(displayId); - // Remove all buttons on the removed display. + // Remove all size compat UIs on the removed display. final List<Integer> toRemoveTaskIds = new ArrayList<>(); forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId())); for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) { @@ -128,7 +128,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang mDisplaysWithIme.remove(displayId); } - // Hide the button when input method is showing. + // Hide the size compat UIs when input method is showing. forAllLayoutsOnDisplay(displayId, layout -> layout.updateImeVisibility(isShowing)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java index 5924b53f822c..32f3648be19a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java @@ -30,7 +30,6 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; -import android.view.Gravity; import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; @@ -43,7 +42,7 @@ import com.android.wm.shell.common.SyncTransactionQueue; /** * Records and handles layout of size compat UI on a task with size compat activity. Helps to - * calculate proper bounds when configuration or button position changes. + * calculate proper bounds when configuration or UI position changes. */ class SizeCompatUILayout { private static final String TAG = "SizeCompatUILayout"; @@ -56,12 +55,18 @@ class SizeCompatUILayout { private IBinder mActivityToken; private ShellTaskOrganizer.TaskListener mTaskListener; private DisplayLayout mDisplayLayout; - @VisibleForTesting - final SizeCompatUIWindowManager mWindowManager; @VisibleForTesting + final SizeCompatUIWindowManager mButtonWindowManager; + @VisibleForTesting + @Nullable + SizeCompatUIWindowManager mHintWindowManager; + @VisibleForTesting @Nullable SizeCompatRestartButton mButton; + @VisibleForTesting + @Nullable + SizeCompatHintPopup mHint; final int mButtonSize; final int mPopupOffsetX; final int mPopupOffsetY; @@ -79,7 +84,7 @@ class SizeCompatUILayout { mTaskListener = taskListener; mDisplayLayout = displayLayout; mShouldShowHint = !hasShownHint; - mWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this); + mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this); mButtonSize = mContext.getResources().getDimensionPixelSize(R.dimen.size_compat_button_size); @@ -87,21 +92,52 @@ class SizeCompatUILayout { mPopupOffsetY = mButtonSize; } - /** Creates the button window. */ + /** Creates the activity restart button window. */ void createSizeCompatButton(boolean isImeShowing) { if (isImeShowing || mButton != null) { // When ime is showing, wait until ime is dismiss to create UI. return; } - mButton = mWindowManager.createSizeCompatUI(); - updateSurfacePosition(); + mButton = mButtonWindowManager.createSizeCompatButton(); + updateButtonSurfacePosition(); + + if (mShouldShowHint) { + // Only show by default for the first time. + mShouldShowHint = false; + createSizeCompatHint(); + } + } + + /** Creates the restart button hint window. */ + private void createSizeCompatHint() { + if (mHint != null) { + // Hint already shown. + return; + } + mHintWindowManager = createHintWindowManager(); + mHint = mHintWindowManager.createSizeCompatHint(); + updateHintSurfacePosition(); } - /** Releases the button window. */ + @VisibleForTesting + SizeCompatUIWindowManager createHintWindowManager() { + return new SizeCompatUIWindowManager(mContext, mTaskConfig, this); + } + + /** Dismisses the hint window. */ + void dismissHint() { + mHint = null; + if (mHintWindowManager != null) { + mHintWindowManager.release(); + mHintWindowManager = null; + } + } + + /** Releases the UI windows. */ void release() { - mButton.remove(); + dismissHint(); mButton = null; - mWindowManager.release(); + mButtonWindowManager.release(); } /** Called when size compat info changed. */ @@ -115,7 +151,10 @@ class SizeCompatUILayout { // Update configuration. mContext = mContext.createConfigurationContext(taskConfig); - mWindowManager.setConfiguration(taskConfig); + mButtonWindowManager.setConfiguration(taskConfig); + if (mHintWindowManager != null) { + mHintWindowManager.setConfiguration(taskConfig); + } if (mButton == null || prevTaskListener != taskListener) { // TaskListener changed, recreate the button for new surface parent. @@ -126,14 +165,19 @@ class SizeCompatUILayout { if (!taskConfig.windowConfiguration.getBounds() .equals(prevTaskConfig.windowConfiguration.getBounds())) { - // Reposition the button surface. - updateSurfacePosition(); + // Reposition the UI surfaces. + updateButtonSurfacePosition(); + updateHintSurfacePosition(); } if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) { // Update layout for RTL. mButton.setLayoutDirection(taskConfig.getLayoutDirection()); - updateSurfacePosition(); + updateButtonSurfacePosition(); + if (mHint != null) { + mHint.setLayoutDirection(taskConfig.getLayoutDirection()); + updateHintSurfacePosition(); + } } } @@ -149,8 +193,9 @@ class SizeCompatUILayout { displayLayout.getStableBounds(curStableBounds); mDisplayLayout = displayLayout; if (!prevStableBounds.equals(curStableBounds)) { - // Stable bounds changed, update button surface position. - updateSurfacePosition(); + // Stable bounds changed, update UI surface positions. + updateButtonSurfacePosition(); + updateHintSurfacePosition(); } } @@ -162,27 +207,46 @@ class SizeCompatUILayout { return; } + // Hide size compat UIs when IME is showing. final int newVisibility = isImeShowing ? View.GONE : View.VISIBLE; if (mButton.getVisibility() != newVisibility) { mButton.setVisibility(newVisibility); } + if (mHint != null && mHint.getVisibility() != newVisibility) { + mHint.setVisibility(newVisibility); + } } /** Gets the layout params for restart button. */ - WindowManager.LayoutParams getWindowLayoutParams() { + WindowManager.LayoutParams getButtonWindowLayoutParams() { final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( + // Cannot be wrap_content as this determines the actual window size mButtonSize, mButtonSize, TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT); - winParams.gravity = getGravity(getLayoutDirection()); winParams.token = new Binder(); - winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + mContext.getDisplayId()); + winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId()); winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; return winParams; } - /** Called when it is ready to be placed button surface button. */ + /** Gets the layout params for hint popup. */ + WindowManager.LayoutParams getHintWindowLayoutParams(SizeCompatHintPopup hint) { + final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( + // Cannot be wrap_content as this determines the actual window size + hint.getMeasuredWidth(), hint.getMeasuredHeight(), + TYPE_APPLICATION_OVERLAY, + FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, + PixelFormat.TRANSLUCENT); + winParams.token = new Binder(); + winParams.setTitle(SizeCompatHintPopup.class.getSimpleName() + getTaskId()); + winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; + winParams.windowAnimations = android.R.style.Animation_InputMethod; + return winParams; + } + + /** Called when it is ready to be placed size compat UI surface. */ void attachToParentSurface(SurfaceControl.Builder b) { mTaskListener.attachChildSurfaceToTask(mTaskId, b); } @@ -192,13 +256,17 @@ class SizeCompatUILayout { ActivityClient.getInstance().restartActivityProcessIfVisible(mActivityToken); } + /** Called when the restart button is long clicked. */ + void onRestartButtonLongClicked() { + createSizeCompatHint(); + } + @VisibleForTesting - void updateSurfacePosition() { - if (mButton == null || mWindowManager.getSurfaceControl() == null) { + void updateButtonSurfacePosition() { + if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) { return; } - // The hint popup won't be at the correct position. - mButton.dismissHint(); + final SurfaceControl leash = mButtonWindowManager.getSurfaceControl(); // Use stable bounds to prevent the button from overlapping with system bars. final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); @@ -212,8 +280,30 @@ class SizeCompatUILayout { : stableBounds.right - taskBounds.left - mButtonSize; final int positionY = stableBounds.bottom - taskBounds.top - mButtonSize; - mSyncQueue.runInSync(t -> - t.setPosition(mWindowManager.getSurfaceControl(), positionX, positionY)); + mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY)); + } + + void updateHintSurfacePosition() { + if (mHint == null || mHintWindowManager == null + || mHintWindowManager.getSurfaceControl() == null) { + return; + } + final SurfaceControl leash = mHintWindowManager.getSurfaceControl(); + + // Use stable bounds to prevent the hint from overlapping with system bars. + final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); + final Rect stableBounds = new Rect(); + mDisplayLayout.getStableBounds(stableBounds); + stableBounds.intersect(taskBounds); + + // Position of the hint in the container coordinate. + final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL + ? stableBounds.left - taskBounds.left + mPopupOffsetX + : stableBounds.right - taskBounds.left - mPopupOffsetX - mHint.getMeasuredWidth(); + final int positionY = + stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight(); + + mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY)); } int getDisplayId() { @@ -227,9 +317,4 @@ class SizeCompatUILayout { private int getLayoutDirection() { return mContext.getResources().getConfiguration().getLayoutDirection(); } - - static int getGravity(int layoutDirection) { - return Gravity.BOTTOM - | (layoutDirection == View.LAYOUT_DIRECTION_RTL ? Gravity.START : Gravity.END); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java index a7ad982a4736..f634c4586e39 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java @@ -24,12 +24,14 @@ import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.SurfaceSession; +import android.view.View; import android.view.WindowlessWindowManager; import com.android.wm.shell.R; /** - * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton}. + * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton} or + * {@link SizeCompatHintPopup}. */ class SizeCompatUIWindowManager extends WindowlessWindowManager { @@ -67,18 +69,39 @@ class SizeCompatUIWindowManager extends WindowlessWindowManager { } /** Inflates {@link SizeCompatRestartButton} on to the root surface. */ - SizeCompatRestartButton createSizeCompatUI() { - if (mViewHost == null) { - mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + SizeCompatRestartButton createSizeCompatButton() { + if (mViewHost != null) { + throw new IllegalStateException( + "A UI has already been created with this window manager."); } + mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + final SizeCompatRestartButton button = (SizeCompatRestartButton) LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null); button.inject(mLayout); - mViewHost.setView(button, mLayout.getWindowLayoutParams()); + mViewHost.setView(button, mLayout.getButtonWindowLayoutParams()); return button; } + /** Inflates {@link SizeCompatHintPopup} on to the root surface. */ + SizeCompatHintPopup createSizeCompatHint() { + if (mViewHost != null) { + throw new IllegalStateException( + "A UI has already been created with this window manager."); + } + + mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + + final SizeCompatHintPopup hint = (SizeCompatHintPopup) + LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null); + // Measure how big the hint is. + hint.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + hint.inject(mLayout); + mViewHost.setView(hint, mLayout.getHintWindowLayoutParams(hint)); + return hint; + } + /** Releases the surface control and tears down the view hierarchy. */ void release() { if (mViewHost != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index b0167afa2e4e..11548adaf5d1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -42,6 +42,7 @@ import androidx.annotation.Nullable; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.draganddrop.DragAndDropPolicy; @@ -62,18 +63,20 @@ public class SplitScreenController implements DragAndDropPolicy.Starter { private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer; private final ShellExecutor mMainExecutor; private final SplitScreenImpl mImpl = new SplitScreenImpl(); + private final DisplayImeController mDisplayImeController; private StageCoordinator mStageCoordinator; public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Context context, RootTaskDisplayAreaOrganizer rootTDAOrganizer, - ShellExecutor mainExecutor) { + ShellExecutor mainExecutor, DisplayImeController displayImeController) { mTaskOrganizer = shellTaskOrganizer; mSyncQueue = syncQueue; mContext = context; mRootTDAOrganizer = rootTDAOrganizer; mMainExecutor = mainExecutor; + mDisplayImeController = displayImeController; } public SplitScreen asSplitScreen() { @@ -84,7 +87,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter { if (mStageCoordinator == null) { // TODO: Multi-display mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, - mRootTDAOrganizer, mTaskOrganizer); + mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController); } } 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 e44c820a656a..bbfbc4098d92 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 @@ -39,6 +39,7 @@ import androidx.annotation.VisibleForTesting; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitLayout; @@ -79,10 +80,12 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, private DisplayAreaInfo mDisplayAreaInfo; private final Context mContext; private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>(); + private final DisplayImeController mDisplayImeController; private boolean mExitSplitScreenOnHide = true; StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, - RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer) { + RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, + DisplayImeController displayImeController) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; @@ -90,13 +93,14 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, mTaskOrganizer = taskOrganizer; mMainStage = new MainStage(mTaskOrganizer, mDisplayId, mMainStageListener, mSyncQueue); mSideStage = new SideStage(mTaskOrganizer, mDisplayId, mSideStageListener, mSyncQueue); + mDisplayImeController = displayImeController; mRootTDAOrganizer.registerListener(displayId, this); } @VisibleForTesting StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, - MainStage mainStage, SideStage sideStage) { + MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; @@ -104,6 +108,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, mTaskOrganizer = taskOrganizer; mMainStage = mainStage; mSideStage = sideStage; + mDisplayImeController = displayImeController; mRootTDAOrganizer.registerListener(displayId, this); } @@ -357,8 +362,9 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, @Override public void onSnappedToDismiss(boolean bottomOrRight) { - final boolean mainStageToTop = bottomOrRight - && mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT; + final boolean mainStageToTop = + bottomOrRight ? mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT + : mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT; exitSplitScreen(mainStageToTop ? mMainStage : mSideStage); } @@ -420,7 +426,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, if (mSplitLayout == null) { mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mDisplayAreaInfo.configuration, this, - b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b)); + b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b), + mDisplayImeController); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index 45d551528940..76497706ce4f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -43,6 +43,7 @@ import java.util.List; /** * Util class to create the view for a splash screen content. + * * @hide */ public class SplashscreenContentDrawer { @@ -349,7 +350,7 @@ public class SplashscreenContentDrawer { // Calculate the difference between two colors based on the HSV dimensions. final float normalizeH = minAngle / 180f; - final double square = Math.pow(normalizeH, 2) + final double square = Math.pow(normalizeH, 2) + Math.pow(aHsv[1] - bHsv[1], 2) + Math.pow(aHsv[2] - bHsv[2], 2); final double mean = square / 3; @@ -433,8 +434,11 @@ public class SplashscreenContentDrawer { */ private interface ColorTester { float nonTransparentRatio(); + boolean isComplexColor(); + int getDominantColor(); + boolean isGrayscale(); } @@ -511,14 +515,17 @@ public class SplashscreenContentDrawer { // restore to original bounds drawable.setBounds(initialBounds); - final Palette.Builder builder = new Palette.Builder(bitmap) - .maximumColorCount(5).clearFilters(); + final Palette.Builder builder; // The Palette API will ignore Alpha, so it cannot handle transparent pixels, but // sometimes we will need this information to know if this Drawable object is // transparent. mFilterTransparent = filterTransparent; if (mFilterTransparent) { - builder.setQuantizer(TRANSPARENT_FILTER_QUANTIZER); + builder = new Palette.Builder(bitmap, TRANSPARENT_FILTER_QUANTIZER) + .maximumColorCount(5); + } else { + builder = new Palette.Builder(bitmap, null) + .maximumColorCount(5); } mPalette = builder.generate(); bitmap.recycle(); @@ -538,7 +545,7 @@ public class SplashscreenContentDrawer { public int getDominantColor() { final Palette.Swatch mainSwatch = mPalette.getDominantSwatch(); if (mainSwatch != null) { - return mainSwatch.getRgb(); + return mainSwatch.getInt(); } return Color.BLACK; } @@ -549,7 +556,7 @@ public class SplashscreenContentDrawer { if (swatches != null) { for (int i = swatches.size() - 1; i >= 0; i--) { Palette.Swatch swatch = swatches.get(i); - if (!isGrayscaleColor(swatch.getRgb())) { + if (!isGrayscaleColor(swatch.getInt())) { return false; } } @@ -561,9 +568,9 @@ public class SplashscreenContentDrawer { private static final int NON_TRANSPARENT = 0xFF000000; private final Quantizer mInnerQuantizer = new VariationalKMeansQuantizer(); private float mNonTransparentRatio; + @Override - public void quantize(final int[] pixels, final int maxColors, - final Palette.Filter[] filters) { + public void quantize(final int[] pixels, final int maxColors) { mNonTransparentRatio = 0; int realSize = 0; for (int i = pixels.length - 1; i > 0; i--) { @@ -575,7 +582,7 @@ public class SplashscreenContentDrawer { if (DEBUG) { Slog.d(TAG, "quantize: this is pure transparent image"); } - mInnerQuantizer.quantize(pixels, maxColors, filters); + mInnerQuantizer.quantize(pixels, maxColors); return; } mNonTransparentRatio = (float) realSize / pixels.length; @@ -587,7 +594,7 @@ public class SplashscreenContentDrawer { rowIndex++; } } - mInnerQuantizer.quantize(samplePixels, maxColors, filters); + mInnerQuantizer.quantize(samplePixels, maxColors); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 59f8c1df1213..2182ee5590e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -16,55 +16,78 @@ package com.android.wm.shell.transition; +import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; +import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Rect; import android.os.IBinder; import android.util.ArrayMap; +import android.view.Choreographer; import android.view.SurfaceControl; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.Transformation; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; +import com.android.internal.R; +import com.android.internal.policy.AttributeCache; +import com.android.internal.policy.TransitionAnimation; +import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.util.ArrayList; /** The default handler that handles anything not already handled. */ public class DefaultTransitionHandler implements Transitions.TransitionHandler { + private static final int MAX_ANIMATION_DURATION = 3000; + private final TransactionPool mTransactionPool; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; + private final TransitionAnimation mTransitionAnimation; /** Keeps track of the currently-running animations associated with each transition. */ private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>(); - DefaultTransitionHandler(@NonNull TransactionPool transactionPool, + private float mTransitionAnimationScaleSetting = 1.0f; + + DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context, @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { mTransactionPool = transactionPool; mMainExecutor = mainExecutor; mAnimExecutor = animExecutor; + mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG); + + AttributeCache.init(context); } @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "start default transition animation, info = %s", info); if (mAnimations.containsKey(transition)) { throw new IllegalStateException("Got a duplicate startAnimation call for " + transition); } final ArrayList<Animator> animations = new ArrayList<>(); mAnimations.put(transition, animations); - final boolean isOpening = Transitions.isOpeningType(info.getType()); final Runnable onAnimFinish = () -> { if (!animations.isEmpty()) return; @@ -77,19 +100,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { // Don't animate anything with an animating parent if (change.getParent() != null) continue; - final int mode = change.getMode(); - if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { - if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { - // This received a transferred starting window, so don't animate - continue; - } - // fade in - startExampleAnimation( - animations, change.getLeash(), true /* show */, onAnimFinish); - } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { - // fade out - startExampleAnimation( - animations, change.getLeash(), false /* show */, onAnimFinish); + Animation a = loadAnimation(info.getType(), change); + if (a != null) { + startAnimInternal(animations, a, change.getLeash(), onAnimFinish); } } t.apply(); @@ -105,32 +118,93 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { return null; } - // TODO(shell-transitions): real animations - private void startExampleAnimation(@NonNull ArrayList<Animator> animations, - @NonNull SurfaceControl leash, boolean show, @NonNull Runnable finishCallback) { - final float end = show ? 1.f : 0.f; - final float start = 1.f - end; + @Override + public void setAnimScaleSetting(float scale) { + mTransitionAnimationScaleSetting = scale; + } + + @Nullable + private Animation loadAnimation(int type, TransitionInfo.Change change) { + // TODO(b/178678389): It should handle more type animation here + Animation a = null; + + final boolean isOpening = Transitions.isOpeningType(type); + final int mode = change.getMode(); + final int flags = change.getFlags(); + + if (mode == TRANSIT_OPEN && isOpening) { + if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { + // This received a transferred starting window, so don't animate + return null; + } + + if (change.getTaskInfo() != null) { + a = mTransitionAnimation.loadDefaultAnimationAttr( + R.styleable.WindowAnimation_taskOpenEnterAnimation); + } else { + a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0 + ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter); + } + } else if (mode == TRANSIT_TO_FRONT && isOpening) { + if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { + // This received a transferred starting window, so don't animate + return null; + } + + a = mTransitionAnimation.loadDefaultAnimationAttr( + R.styleable.WindowAnimation_taskToFrontEnterAnimation); + } else if (mode == TRANSIT_CLOSE && !isOpening) { + if (change.getTaskInfo() != null) { + a = mTransitionAnimation.loadDefaultAnimationAttr( + R.styleable.WindowAnimation_taskCloseExitAnimation); + } else { + a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0 + ? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit); + } + } else if (mode == TRANSIT_TO_BACK && !isOpening) { + a = mTransitionAnimation.loadDefaultAnimationAttr( + R.styleable.WindowAnimation_taskToBackExitAnimation); + } else if (mode == TRANSIT_CHANGE) { + // In the absence of a specific adapter, we just want to keep everything stationary. + a = new AlphaAnimation(1.f, 1.f); + a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION); + } + + if (a != null) { + Rect start = change.getStartAbsBounds(); + Rect end = change.getEndAbsBounds(); + a.restrictDuration(MAX_ANIMATION_DURATION); + a.initialize(end.width(), end.height(), start.width(), start.height()); + a.scaleCurrentDuration(mTransitionAnimationScaleSetting); + } + return a; + } + + private void startAnimInternal(@NonNull ArrayList<Animator> animations, @NonNull Animation anim, + @NonNull SurfaceControl leash, @NonNull Runnable finishCallback) { final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - final ValueAnimator va = ValueAnimator.ofFloat(start, end); - va.setDuration(500); + final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); + final Transformation transformation = new Transformation(); + final float[] matrix = new float[9]; + // Animation length is already expected to be scaled. + va.overrideDurationScale(1.0f); + va.setDuration(anim.computeDurationHint()); va.addUpdateListener(animation -> { - float fraction = animation.getAnimatedFraction(); - transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); - transaction.apply(); + final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime()); + + applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix); }); + final Runnable finisher = () -> { - transaction.setAlpha(leash, end); - transaction.apply(); + applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix); + mTransactionPool.release(transaction); mMainExecutor.execute(() -> { animations.remove(va); finishCallback.run(); }); }; - va.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { } - + va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finisher.run(); @@ -140,11 +214,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { public void onAnimationCancel(Animator animation) { finisher.run(); } - - @Override - public void onAnimationRepeat(Animator animation) { } }); animations.add(va); mAnimExecutor.execute(va::start); } + + private static void applyTransformation(long time, SurfaceControl.Transaction t, + SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix) { + anim.getTransformation(time, transformation); + t.setMatrix(leash, transformation.getMatrix(), matrix); + t.setAlpha(leash, transformation.getAlpha()); + t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId()); + t.apply(); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 10344892e766..89eee67bf5af 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -25,9 +25,13 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemProperties; +import android.provider.Settings; import android.util.ArrayMap; import android.util.Log; import android.view.SurfaceControl; @@ -43,6 +47,7 @@ import android.window.WindowOrganizer; import androidx.annotation.BinderThread; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; @@ -63,6 +68,7 @@ public class Transitions { SystemProperties.getBoolean("persist.debug.shell_transit", false); private final WindowOrganizer mOrganizer; + private final Context mContext; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; private final TransitionPlayerImpl mPlayerImpl; @@ -72,6 +78,8 @@ public class Transitions { /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>(); + private float mTransitionAnimationScaleSetting = 1.0f; + private static final class ActiveTransition { TransitionHandler mFirstHandler = null; } @@ -84,26 +92,46 @@ public class Transitions { } public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, - @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { + @NonNull Context context, @NonNull ShellExecutor mainExecutor, + @NonNull ShellExecutor animExecutor) { mOrganizer = organizer; + mContext = context; mMainExecutor = mainExecutor; mAnimExecutor = animExecutor; mPlayerImpl = new TransitionPlayerImpl(); // The very last handler (0 in the list) should be the default one. - mHandlers.add(new DefaultTransitionHandler(pool, mainExecutor, animExecutor)); + mHandlers.add(new DefaultTransitionHandler(pool, context, mainExecutor, animExecutor)); // Next lowest priority is remote transitions. mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor); mHandlers.add(mRemoteTransitionHandler); + + ContentResolver resolver = context.getContentResolver(); + mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver, + Settings.Global.TRANSITION_ANIMATION_SCALE, + context.getResources().getFloat( + R.dimen.config_appTransitionAnimationDurationScaleDefault)); + dispatchAnimScaleSetting(mTransitionAnimationScaleSetting); + + resolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false, + new SettingsObserver()); } private Transitions() { mOrganizer = null; + mContext = null; mMainExecutor = null; mAnimExecutor = null; mPlayerImpl = null; mRemoteTransitionHandler = null; } + private void dispatchAnimScaleSetting(float scale) { + for (int i = mHandlers.size() - 1; i >= 0; --i) { + mHandlers.get(i).setAnimScaleSetting(scale); + } + } + /** Create an empty/non-registering transitions object for system-ui tests. */ @VisibleForTesting public static RemoteTransitions createEmptyForTesting() { @@ -368,6 +396,13 @@ public class Transitions { @Nullable WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request); + + /** + * Sets transition animation scale settings value to handler. + * + * @param scale The setting value of transition animation scale. + */ + default void setAnimScaleSetting(float scale) {} } @BinderThread @@ -404,4 +439,21 @@ public class Transitions { }); } } + + private class SettingsObserver extends ContentObserver { + + SettingsObserver() { + super(null); + } + + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + mTransitionAnimationScaleSetting = Settings.Global.getFloat( + mContext.getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE, + mTransitionAnimationScaleSetting); + + mMainExecutor.execute(() -> dispatchAnimScaleSetting(mTransitionAnimationScaleSetting)); + } + } } diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS index 2c6c7b358e3b..d80699de8a2d 100644 --- a/libs/WindowManager/Shell/tests/OWNERS +++ b/libs/WindowManager/Shell/tests/OWNERS @@ -1,2 +1,3 @@ +# Bug component: 909476 # includes OWNERS from parent directories natanieljr@google.com diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp index a57ac35583b2..9dd25fe0e6fe 100644 --- a/libs/WindowManager/Shell/tests/flicker/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "WMShellFlickerTests", srcs: ["src/**/*.java", "src/**/*.kt"], diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index 3282ece999ac..35bab7aaf22c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -18,241 +18,92 @@ package com.android.wm.shell.flicker import android.graphics.Region import android.view.Surface -import com.android.server.wm.flicker.dsl.LayersAssertionBuilder -import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy -import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER -import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER +import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.traces.layers.getVisibleBounds -@JvmOverloads -fun LayersAssertionBuilder.appPairsDividerIsVisible(bugId: Int = 0) { - end("appPairsDividerIsVisible", bugId) { +fun FlickerTestParameter.appPairsDividerIsVisible() { + assertLayersEnd { this.isVisible(APP_PAIR_SPLIT_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.appPairsDividerIsInvisible(bugId: Int = 0) { - end("appPairsDividerIsInVisible", bugId) { - this.notExists(APP_PAIR_SPLIT_DIVIDER) +fun FlickerTestParameter.appPairsDividerIsInvisible() { + assertLayersEnd { + this.notContains(APP_PAIR_SPLIT_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.appPairsDividerBecomesVisible(bugId: Int = 0) { - all("dividerLayerBecomesVisible", bugId) { - this.hidesLayer(DOCKED_STACK_DIVIDER) +fun FlickerTestParameter.appPairsDividerBecomesVisible() { + assertLayers { + this.isInvisible(DOCKED_STACK_DIVIDER) .then() - .showsLayer(DOCKED_STACK_DIVIDER) + .isVisible(DOCKED_STACK_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.dockedStackDividerIsVisible(bugId: Int = 0) { - end("dockedStackDividerIsVisible", bugId) { +fun FlickerTestParameter.dockedStackDividerIsVisible() { + assertLayersEnd { this.isVisible(DOCKED_STACK_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.dockedStackDividerBecomesVisible(bugId: Int = 0) { - all("dividerLayerBecomesVisible", bugId) { - this.hidesLayer(DOCKED_STACK_DIVIDER) +fun FlickerTestParameter.dockedStackDividerBecomesVisible() { + assertLayers { + this.isInvisible(DOCKED_STACK_DIVIDER) .then() - .showsLayer(DOCKED_STACK_DIVIDER) + .isVisible(DOCKED_STACK_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.dockedStackDividerBecomesInvisible(bugId: Int = 0) { - all("dividerLayerBecomesInvisible", bugId) { - this.showsLayer(DOCKED_STACK_DIVIDER) +fun FlickerTestParameter.dockedStackDividerBecomesInvisible() { + assertLayers { + this.isVisible(DOCKED_STACK_DIVIDER) .then() - .hidesLayer(DOCKED_STACK_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilder.dockedStackDividerIsInvisible(bugId: Int = 0) { - end("dockedStackDividerIsInvisible", bugId) { - this.notExists(DOCKED_STACK_DIVIDER) + .isInvisible(DOCKED_STACK_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.appPairsPrimaryBoundsIsVisible( - rotation: Int, - primaryLayerName: String, - bugId: Int = 0 -) { - end("PrimaryAppBounds", bugId) { - val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation)) +fun FlickerTestParameter.dockedStackDividerIsInvisible() { + assertLayersEnd { + this.notContains(DOCKED_STACK_DIVIDER) } } -@JvmOverloads -fun LayersAssertionBuilder.appPairsSecondaryBoundsIsVisible( - rotation: Int, - secondaryLayerName: String, - bugId: Int = 0 -) { - end("SecondaryAppBounds", bugId) { +fun FlickerTestParameter.appPairsPrimaryBoundsIsVisible(rotation: Int, primaryLayerName: String) { + assertLayersEnd { val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation)) + this.coversExactly(getPrimaryRegion(dividerRegion, rotation), primaryLayerName) } } -@JvmOverloads -fun LayersAssertionBuilder.dockedStackPrimaryBoundsIsVisible( +fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisible( rotation: Int, - primaryLayerName: String, - bugId: Int = 0 + primaryLayerName: String ) { - end("PrimaryAppBounds", bugId) { + assertLayersEnd { val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER) - this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation)) + this.coversExactly(getPrimaryRegion(dividerRegion, rotation), primaryLayerName) } } -@JvmOverloads -fun LayersAssertionBuilder.dockedStackSecondaryBoundsIsVisible( +fun FlickerTestParameter.appPairsSecondaryBoundsIsVisible( rotation: Int, - secondaryLayerName: String, - bugId: Int = 0 -) { - end("SecondaryAppBounds", bugId) { - val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER) - this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation)) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.appPairsDividerIsVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 + secondaryLayerName: String ) { - end("appPairsDividerIsVisible", bugId, enabled) { - this.isVisible(APP_PAIR_SPLIT_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.appPairsDividerIsInvisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - end("appPairsDividerIsInVisible", bugId, enabled) { - this.notExists(APP_PAIR_SPLIT_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.appPairsDividerBecomesVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("dividerLayerBecomesVisible", bugId, enabled) { - this.hidesLayer(DOCKED_STACK_DIVIDER) - .then() - .showsLayer(DOCKED_STACK_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.dockedStackDividerIsVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - end("dockedStackDividerIsVisible", bugId, enabled) { - this.isVisible(DOCKED_STACK_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.dockedStackDividerBecomesVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("dividerLayerBecomesVisible", bugId, enabled) { - this.hidesLayer(DOCKED_STACK_DIVIDER) - .then() - .showsLayer(DOCKED_STACK_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.dockedStackDividerBecomesInvisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("dividerLayerBecomesInvisible", bugId, enabled) { - this.showsLayer(DOCKED_STACK_DIVIDER) - .then() - .hidesLayer(DOCKED_STACK_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.dockedStackDividerIsInvisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - end("dockedStackDividerIsInvisible", bugId, enabled) { - this.notExists(DOCKED_STACK_DIVIDER) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.appPairsPrimaryBoundsIsVisible( - rotation: Int, - primaryLayerName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - end("PrimaryAppBounds", bugId, enabled) { - val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation)) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.appPairsSecondaryBoundsIsVisible( - rotation: Int, - secondaryLayerName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - end("SecondaryAppBounds", bugId, enabled) { + assertLayersEnd { val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation)) + this.coversExactly(getSecondaryRegion(dividerRegion, rotation), secondaryLayerName) } } -@JvmOverloads -fun LayersAssertionBuilderLegacy.dockedStackPrimaryBoundsIsVisible( +fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisible( rotation: Int, - primaryLayerName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 + secondaryLayerName: String ) { - end("PrimaryAppBounds", bugId, enabled) { + assertLayersEnd { val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER) - this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation)) - } -} - -@JvmOverloads -fun LayersAssertionBuilderLegacy.dockedStackSecondaryBoundsIsVisible( - rotation: Int, - secondaryLayerName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - end("SecondaryAppBounds", bugId, enabled) { - val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER) - this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation)) + this.coversExactly(getSecondaryRegion(dividerRegion, rotation), secondaryLayerName) } } @@ -260,10 +111,10 @@ fun getPrimaryRegion(dividerRegion: Region, rotation: Int): Region { val displayBounds = WindowUtils.getDisplayBounds(rotation) return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) { Region(0, 0, displayBounds.bounds.right, - dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset) + dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset) } else { Region(0, 0, dividerRegion.bounds.left, - dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset) + dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset) } } @@ -271,12 +122,12 @@ fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region { val displayBounds = WindowUtils.getDisplayBounds(rotation) return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) { Region(0, - dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset, - displayBounds.bounds.right, - displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset) + dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset, + displayBounds.bounds.right, + displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset) } else { Region(dividerRegion.bounds.right, 0, - displayBounds.bounds.right, - displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset) + displayBounds.bounds.right, + displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset) } -} +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt index d2cfb0fbb5f6..03b93c74233c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt @@ -18,3 +18,5 @@ package com.android.wm.shell.flicker const val IME_WINDOW_NAME = "InputMethod" const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui" +const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider" +const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt index 89bbdb0a2f99..9c50630095be 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt @@ -16,33 +16,33 @@ package com.android.wm.shell.flicker +import android.app.Instrumentation import android.content.pm.PackageManager import android.content.pm.PackageManager.FEATURE_LEANBACK import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY -import android.os.RemoteException -import android.os.SystemClock -import android.platform.helpers.IAppHelper import android.view.Surface import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice -import com.android.server.wm.flicker.Flicker import org.junit.Assume.assumeFalse import org.junit.Before +import org.junit.runners.Parameterized /** * Base class of all Flicker test that performs common functions for all flicker tests: * - * * - Caches transitions so that a transition is run once and the transition results are used by * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods * multiple times. * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed. * - Fails tests if results are not available for any test due to jank. */ -abstract class FlickerTestBase { - val instrumentation by lazy { InstrumentationRegistry.getInstrumentation() } - val uiDevice by lazy { UiDevice.getInstance(instrumentation) } - val packageManager: PackageManager by lazy { instrumentation.context.getPackageManager() } +abstract class FlickerTestBase( + protected val rotationName: String, + protected val rotation: Int +) { + val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + val uiDevice = UiDevice.getInstance(instrumentation) + val packageManager: PackageManager = instrumentation.context.packageManager protected val isTelevision: Boolean by lazy { packageManager.run { hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY) @@ -56,83 +56,12 @@ abstract class FlickerTestBase { @Before open fun televisionSetUp() = assumeFalse(isTelevision) - /** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param rotation Initial screen rotation - * - * @return test tag with pattern <NAME>__<APP>__<ROTATION> - </ROTATION></APP></NAME> */ - protected fun buildTestTag(testName: String, app: IAppHelper, rotation: Int): String { - return buildTestTag( - testName, app, rotation, rotation, app2 = null, extraInfo = "") - } - - /** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param beginRotation Initial screen rotation - * @param endRotation End screen rotation (if any, otherwise use same as initial) - * - * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION> - </END_ROTATION></BEGIN_ROTATION></APP></NAME> */ - protected fun buildTestTag( - testName: String, - app: IAppHelper, - beginRotation: Int, - endRotation: Int - ): String { - return buildTestTag( - testName, app, beginRotation, endRotation, app2 = null, extraInfo = "") - } - - /** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param app2 Second app being launched (if any) - * @param beginRotation Initial screen rotation - * @param endRotation End screen rotation (if any, otherwise use same as initial) - * @param extraInfo Additional information to append to the tag - * - * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>] - </EXTRA></NAME> */ - protected fun buildTestTag( - testName: String, - app: IAppHelper, - beginRotation: Int, - endRotation: Int, - app2: IAppHelper?, - extraInfo: String - ): String { - var testTag = "${testName}__${app.launcherName}" - if (app2 != null) { - testTag += "-${app2.launcherName}" - } - testTag += "__${Surface.rotationToString(beginRotation)}" - if (endRotation != beginRotation) { - testTag += "-${Surface.rotationToString(endRotation)}" - } - if (extraInfo.isNotEmpty()) { - testTag += "__$extraInfo" - } - return testTag - } - - protected fun Flicker.setRotation(rotation: Int) { - try { - when (rotation) { - Surface.ROTATION_270 -> device.setOrientationLeft() - Surface.ROTATION_90 -> device.setOrientationRight() - Surface.ROTATION_0 -> device.setOrientationNatural() - else -> device.setOrientationNatural() - } - // Wait for animation to complete - SystemClock.sleep(1000) - } catch (e: RemoteException) { - throw RuntimeException(e) + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NonRotationTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NonRotationTestBase.kt deleted file mode 100644 index 90334ae91e9d..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NonRotationTestBase.kt +++ /dev/null @@ -1,36 +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.wm.shell.flicker - -import android.view.Surface -import org.junit.runners.Parameterized - -abstract class NonRotationTestBase( - protected val rotationName: String, - protected val rotation: Int -) : FlickerTestBase() { - companion object { - const val SCREENSHOT_LAYER = "RotationLayer" - - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90) - return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt index c3fd66395366..90e71373b1fd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt @@ -16,17 +16,17 @@ package com.android.wm.shell.flicker.apppairs -import android.os.Bundle import android.os.SystemClock +import android.platform.test.annotations.Presubmit import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.appPairsDividerIsInvisible +import com.android.wm.shell.flicker.helpers.AppPairsHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -41,47 +41,47 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class AppPairsTestCannotPairNonResizeableApps( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : AppPairsTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag(configuration) - } - transitions { - nonResizeableApp?.launchViaIntent(wmHelper) - // TODO pair apps through normal UX flow - executeShellCommand( - composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - } - assertions { - presubmit { - layersTrace { - appPairsDividerIsInvisible() - } - windowManagerTrace { - val nonResizeableApp = nonResizeableApp - require(nonResizeableApp != null) { - "Non resizeable app not initialized" - } + testSpec: FlickerTestParameter +) : AppPairsTransition(testSpec) { - end("onlyResizeableAppWindowVisible") { - isVisible(nonResizeableApp.defaultWindowName) - isInvisible(primaryApp.defaultWindowName) - } - } - } - } + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + transitions { + nonResizeableApp?.launchViaIntent(wmHelper) + // TODO pair apps through normal UX flow + executeShellCommand( + composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) } + } + + @Presubmit + @Test + fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible() - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - transition, testSpec, repetitions = AppPairsHelper.TEST_REPETITIONS) + @Presubmit + @Test + fun onlyResizeableAppWindowVisible() { + val nonResizeableApp = nonResizeableApp + require(nonResizeableApp != null) { + "Non resizeable app not initialized" + } + testSpec.assertWmEnd { + isVisible(nonResizeableApp.defaultWindowName) + isInvisible(primaryApp.defaultWindowName) + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + repetitions = AppPairsHelper.TEST_REPETITIONS) } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt index 7a2a5e482d98..dc51b4fb5a9e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt @@ -16,19 +16,20 @@ package com.android.wm.shell.flicker.apppairs -import android.os.Bundle import android.os.SystemClock +import android.platform.test.annotations.Presubmit +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.traces.layers.getVisibleBounds +import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER import com.android.wm.shell.flicker.appPairsDividerIsVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -39,52 +40,53 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class AppPairsTestPairPrimaryAndSecondaryApps( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : AppPairsTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : AppPairsTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + transitions { + // TODO pair apps through normal UX flow + executeShellCommand( + composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + } + + @Presubmit + @Test + fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible() + + @Presubmit + @Test + fun bothAppWindowsVisible() { + testSpec.assertWmEnd { + isVisible(primaryApp.defaultWindowName) + isVisible(secondaryApp.defaultWindowName) + } + } + + @FlakyTest + @Test + fun appsEndingBounds() { + testSpec.assertLayersEnd { + val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) + this.coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion), + primaryApp.defaultWindowName) + .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion), + secondaryApp.defaultWindowName) + } + } + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag(configuration) - } - transitions { - // TODO pair apps through normal UX flow - executeShellCommand( - composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - } - assertions { - presubmit { - layersTrace { - appPairsDividerIsVisible() - } - windowManagerTrace { - end("bothAppWindowsVisible") { - isVisible(primaryApp.defaultWindowName) - isVisible(secondaryApp.defaultWindowName) - } - } - } - - flaky { - layersTrace { - end("appsEndingBounds") { - val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - this.hasVisibleRegion(primaryApp.defaultWindowName, - appPairsHelper.getPrimaryBounds(dividerRegion)) - .hasVisibleRegion(secondaryApp.defaultWindowName, - appPairsHelper.getSecondaryBounds(dividerRegion)) - } - } - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, transition, - testSpec, repetitions = AppPairsHelper.TEST_REPETITIONS) + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + repetitions = AppPairsHelper.TEST_REPETITIONS) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt index d8dc4c2b56f6..5bb9b2f8b8ca 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt @@ -16,19 +16,20 @@ package com.android.wm.shell.flicker.apppairs -import android.os.Bundle import android.os.SystemClock +import android.platform.test.annotations.Presubmit +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.traces.layers.getVisibleBounds +import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER import com.android.wm.shell.flicker.appPairsDividerIsInvisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -39,61 +40,67 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class AppPairsTestUnpairPrimaryAndSecondaryApps( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : AppPairsTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : AppPairsTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + setup { + executeShellCommand( + composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + transitions { + // TODO pair apps through normal UX flow + executeShellCommand( + composePairsCommand(primaryTaskId, secondaryTaskId, pair = false)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + } + + @Presubmit + @Test + fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible() + + @Presubmit + @Test + fun bothAppWindowsInvisible() { + testSpec.assertWmEnd { + isInvisible(primaryApp.defaultWindowName) + isInvisible(secondaryApp.defaultWindowName) + } + } + + @FlakyTest + @Test + fun appsStartingBounds() { + testSpec.assertLayersStart { + val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) + coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion), + primaryApp.defaultWindowName) + coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion), + secondaryApp.defaultWindowName) + } + } + + @FlakyTest + @Test + fun appsEndingBounds() { + testSpec.assertLayersEnd { + notContains(primaryApp.defaultWindowName) + notContains(secondaryApp.defaultWindowName) + } + } + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag(configuration) - } - setup { - executeShellCommand( - composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - } - transitions { - // TODO pair apps through normal UX flow - executeShellCommand( - composePairsCommand(primaryTaskId, secondaryTaskId, pair = false)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - } - assertions { - presubmit { - layersTrace { - appPairsDividerIsInvisible() - } - windowManagerTrace { - end("bothAppWindowsInvisible") { - isInvisible(primaryApp.defaultWindowName) - isInvisible(secondaryApp.defaultWindowName) - } - } - } - - flaky { - layersTrace { - start("appsStartingBounds") { - val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER) - this.hasVisibleRegion(primaryApp.defaultWindowName, - appPairsHelper.getPrimaryBounds(dividerRegion)) - .hasVisibleRegion(secondaryApp.defaultWindowName, - appPairsHelper.getSecondaryBounds(dividerRegion)) - } - end("appsEndingBounds") { - this.notExists(primaryApp.defaultWindowName) - .notExists(secondaryApp.defaultWindowName) - } - } - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, transition, - testSpec, repetitions = AppPairsHelper.TEST_REPETITIONS) + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + repetitions = AppPairsHelper.TEST_REPETITIONS) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt index 78a938aef69e..91e080f65550 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt @@ -17,41 +17,55 @@ package com.android.wm.shell.flicker.apppairs import android.app.Instrumentation -import android.os.Bundle +import android.platform.test.annotations.Presubmit import android.system.helpers.ActivityHelper import android.util.Log +import androidx.test.platform.app.InstrumentationRegistry import com.android.compatibility.common.util.SystemUtil +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.helpers.SplitScreenHelper import com.android.wm.shell.flicker.testapp.Components +import org.junit.Test import java.io.IOException -open class AppPairsTransition( - protected val instrumentation: Instrumentation -) { - internal val activityHelper = ActivityHelper.getInstance() - - internal val appPairsHelper = AppPairsHelper(instrumentation, +abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) { + protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + protected val isRotated = testSpec.config.startRotation.isRotated() + protected val activityHelper = ActivityHelper.getInstance() + protected val appPairsHelper = AppPairsHelper(instrumentation, Components.SplitScreenActivity.LABEL, Components.SplitScreenActivity.COMPONENT) - internal val primaryApp = SplitScreenHelper.getPrimary(instrumentation) - internal val secondaryApp = SplitScreenHelper.getSecondary(instrumentation) - internal open val nonResizeableApp: SplitScreenHelper? = + protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation) + protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation) + protected open val nonResizeableApp: SplitScreenHelper? = SplitScreenHelper.getNonResizeable(instrumentation) - internal var primaryTaskId = "" - internal var secondaryTaskId = "" - internal var nonResizeableTaskId = "" + protected var primaryTaskId = "" + protected var secondaryTaskId = "" + protected var nonResizeableTaskId = "" + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + transition(this, testSpec.config) + } + } - internal open val transition: FlickerBuilder.(Bundle) -> Unit + internal open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> setup { test { @@ -71,20 +85,9 @@ open class AppPairsTransition( primaryTaskId, secondaryTaskId, pair = false)) executeShellCommand(composePairsCommand( primaryTaskId, nonResizeableTaskId, pair = false)) - primaryApp.exit() - secondaryApp.exit() - nonResizeableApp?.exit() - } - } - - assertions { - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - } - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() + primaryApp.exit(wmHelper) + secondaryApp.exit(wmHelper) + nonResizeableApp?.exit(wmHelper) } } } @@ -128,4 +131,20 @@ open class AppPairsTransition( } append("$primaryApp $secondaryApp") } + + @Presubmit + @Test + open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt index 8aee005b7513..5f003ba62b2d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt @@ -16,27 +16,26 @@ package com.android.wm.shell.flicker.apppairs -import android.os.Bundle import android.os.SystemClock +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.appPairsDividerIsVisible import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -47,57 +46,65 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class RotateTwoLaunchedAppsInAppPairsMode( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : RotateTwoLaunchedAppsTransition( - InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag(configuration) - } - transitions { - executeShellCommand(composePairsCommand( - primaryTaskId, secondaryTaskId, true /* pair */)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - setRotation(configuration.endRotation) - } - assertions { - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - end("bothAppWindowsVisible") { - isVisible(primaryApp.defaultWindowName) - .isVisible(secondaryApp.defaultWindowName) - } - } - } - - flaky { - layersTrace { - appPairsDividerIsVisible() - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - appPairsPrimaryBoundsIsVisible(configuration.endRotation, - primaryApp.defaultWindowName, bugId = 172776659) - appPairsSecondaryBoundsIsVisible(configuration.endRotation, - secondaryApp.defaultWindowName, bugId = 172776659) - } - } - } + testSpec: FlickerTestParameter +) : RotateTwoLaunchedAppsTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + transitions { + executeShellCommand(composePairsCommand( + primaryTaskId, secondaryTaskId, true /* pair */)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + setRotation(testSpec.config.endRotation) } + } + + @Presubmit + @Test + fun bothAppWindowsVisible() { + testSpec.assertWmEnd { + isVisible(primaryApp.defaultWindowName) + .isVisible(secondaryApp.defaultWindowName) + } + } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - transition, testSpec, + @Presubmit + @Test + fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible() + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, + testSpec.config.endRotation) + + @Presubmit + @Test + fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, + testSpec.config.endRotation) + + @FlakyTest(bugId = 172776659) + @Test + fun appPairsPrimaryBoundsIsVisible() = + testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation, + primaryApp.defaultWindowName) + + @FlakyTest(bugId = 172776659) + @Test + fun appPairsSecondaryBoundsIsVisible() = + testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation, + secondaryApp.defaultWindowName) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270)) + supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270) + ) } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt index bc99c9430f13..d4792088ac31 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt @@ -16,22 +16,19 @@ package com.android.wm.shell.flicker.apppairs -import android.os.Bundle import android.os.SystemClock import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.appPairsDividerIsVisible @@ -39,7 +36,9 @@ import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -48,70 +47,75 @@ import org.junit.runners.Parameterized * Test open apps to app pairs and rotate. * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsRotateAndEnterAppPairsMode` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : RotateTwoLaunchedAppsTransition( - InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag(configuration) - } - transitions { - this.setRotation(configuration.endRotation) - executeShellCommand( - composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - } - assertions { - val isRotated = configuration.startRotation.isRotated() - presubmit { - layersTrace { - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - appPairsDividerIsVisible() - if (!isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - end("bothAppWindowsVisible") { - isVisible(primaryApp.defaultWindowName) - isVisible(secondaryApp.defaultWindowName) - } - } - } - flaky { - layersTrace { - appPairsPrimaryBoundsIsVisible(configuration.endRotation, - primaryApp.defaultWindowName, 172776659) - appPairsSecondaryBoundsIsVisible(configuration.endRotation, - secondaryApp.defaultWindowName, 172776659) - - if (isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - } - } + testSpec: FlickerTestParameter +) : RotateTwoLaunchedAppsTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + transitions { + this.setRotation(testSpec.config.endRotation) + executeShellCommand( + composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) } + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, + testSpec.config.endRotation) + + @Presubmit + @Test + fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible() + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales( + Surface.ROTATION_0, testSpec.config.endRotation) + + @Presubmit + @Test + override fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - transition, testSpec, + @Presubmit + @Test + override fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun bothAppWindowsVisible() { + testSpec.assertWmEnd { + isVisible(primaryApp.defaultWindowName) + isVisible(secondaryApp.defaultWindowName) + } + } + + @FlakyTest(bugId = 172776659) + @Test + fun appPairsPrimaryBoundsIsVisible() = + testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation, + primaryApp.defaultWindowName) + + @FlakyTest(bugId = 172776659) + @Test + fun appPairsSecondaryBoundsIsVisible() = + testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation, + secondaryApp.defaultWindowName) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270) ) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt index 8ea2544fcf61..83853e61ab5e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt @@ -16,21 +16,20 @@ package com.android.wm.shell.flicker.apppairs -import android.app.Instrumentation -import android.os.Bundle import android.view.Surface +import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.helpers.SplitScreenHelper -open class RotateTwoLaunchedAppsTransition( - instrumentation: Instrumentation -) : AppPairsTransition(instrumentation) { +abstract class RotateTwoLaunchedAppsTransition( + testSpec: FlickerTestParameter +) : AppPairsTransition(testSpec) { override val nonResizeableApp: SplitScreenHelper? get() = null - override val transition: FlickerBuilder.(Bundle) -> Unit + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { setup { test { @@ -45,8 +44,8 @@ open class RotateTwoLaunchedAppsTransition( eachRun { executeShellCommand(composePairsCommand( primaryTaskId, secondaryTaskId, pair = false)) - primaryApp.exit() - secondaryApp.exit() + primaryApp.exit(wmHelper) + secondaryApp.exit(wmHelper) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt index 9f2087fc91d6..901b7a393291 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt @@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.helpers import android.app.Instrumentation import android.content.ComponentName -import android.os.SystemClock import com.android.wm.shell.flicker.testapp.Components class SplitScreenHelper( @@ -27,17 +26,6 @@ class SplitScreenHelper( componentsInfo: ComponentName ) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) { - /** - * Reopens the first device window from the list of recent apps (overview) - */ - fun reopenAppFromOverview() { - val x = uiDevice.displayWidth / 2 - val y = uiDevice.displayHeight / 2 - uiDevice.click(x, y) - // Wait for animation to complete. - SystemClock.sleep(TIMEOUT_MS) - } - companion object { const val TEST_REPETITIONS = 1 const val TIMEOUT_MS = 3_000L diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt index 2c29220bf20e..17c51fb15b0c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt @@ -16,16 +16,15 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.WALLPAPER_TITLE import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.startRotation @@ -36,6 +35,7 @@ import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -47,56 +47,73 @@ import org.junit.runners.Parameterized @Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -// @FlakyTest(bugId = 179116910) class EnterSplitScreenDockActivity( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + super.transition(this, configuration) + transitions { + device.launchSplitScreen(wmHelper) + } + } + + @FlakyTest(bugId = 169271943) + @Test + fun dockedStackPrimaryBoundsIsVisible() = + testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation, + splitScreenApp.defaultWindowName) + + @Presubmit + @Test + fun dockedStackDividerBecomesVisible() = testSpec.dockedStackDividerBecomesVisible() + + @FlakyTest(bugId = 178531736) + @Test + // b/178531736 + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, + WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME, + splitScreenApp.defaultWindowName) + ) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @FlakyTest(bugId = 178531736) + @Test + // b/178531736 + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, + WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME, + splitScreenApp.defaultWindowName) + ) + + @Presubmit + @Test + fun appWindowIsVisible() { + testSpec.assertWmEnd { + isVisible(splitScreenApp.defaultWindowName) + } + } + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testLegacySplitScreenDockActivity", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - device.launchSplitScreen() - } - assertions { - layersTrace { - dockedStackPrimaryBoundsIsVisible( - configuration.startRotation, - splitScreenApp.defaultWindowName, bugId = 169271943) - dockedStackDividerBecomesVisible() - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, - WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME, - splitScreenApp.defaultWindowName), - bugId = 178531736 - ) - } - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, - WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME, - splitScreenApp.defaultWindowName), - bugId = 178531736 - ) - end("appWindowIsVisible") { - isVisible(splitScreenApp.defaultWindowName) - } - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, defaultTransitionSetup, testSpec, + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910 ) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt index 903971ea084f..a94fd463c624 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt @@ -16,17 +16,17 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesVisible import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible @@ -37,6 +37,7 @@ import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -45,61 +46,79 @@ import org.junit.runners.Parameterized * Test open activity to primary split screen and dock secondary activity to side * To run this test: `atest WMShellFlickerTests:EnterSplitScreenLaunchToSide` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EnterSplitScreenLaunchToSide( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + super.transition(this, configuration) + transitions { + device.launchSplitScreen(wmHelper) + device.reopenAppFromOverview(wmHelper) + } + } + + @FlakyTest(bugId = 169271943) + @Test + fun dockedStackPrimaryBoundsIsVisible() = + testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation, + splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 169271943) + @Test + fun dockedStackSecondaryBoundsIsVisible() = + testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation, + secondaryApp.defaultWindowName) + + @Presubmit + @Test + // b/169271943 + fun dockedStackDividerBecomesVisible() = testSpec.dockedStackDividerBecomesVisible() + + @FlakyTest(bugId = 178447631) + @Test + // TODO(b/178447631) Remove Splash Screen from white list when flicker lib + // add a wait for splash screen be gone + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME, + splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName) + ) + + @Presubmit + @Test + fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME, + splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName) + ) + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testLegacySplitScreenLaunchToSide", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - device.launchSplitScreen() - secondaryApp.reopenAppFromOverview() - } - assertions { - layersTrace { - dockedStackPrimaryBoundsIsVisible( - configuration.startRotation, - splitScreenApp.defaultWindowName, bugId = 169271943) - dockedStackSecondaryBoundsIsVisible( - configuration.startRotation, - secondaryApp.defaultWindowName, bugId = 169271943) - dockedStackDividerBecomesVisible() - // TODO(b/178447631) Remove Splash Screen from white list when flicker lib - // add a wait for splash screen be gone - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME, - splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName), - bugId = 178447631 - ) - } - windowManagerTrace { - appWindowBecomesVisible(secondaryApp.defaultWindowName) - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME, - splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName) - ) - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, defaultTransitionSetup, testSpec, + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, supportedRotations = listOf(Surface.ROTATION_0) // bugId = 175687842 ) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt index e3619235ee77..238059b484b5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt @@ -16,17 +16,14 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle -import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.WALLPAPER_TITLE import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.canSplitScreen import com.android.server.wm.flicker.helpers.openQuickstep import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry @@ -35,6 +32,7 @@ import com.android.wm.shell.flicker.dockedStackDividerIsInvisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.Assert import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -43,59 +41,68 @@ import org.junit.runners.Parameterized * Test open non-resizable activity will auto exit split screen mode * To run this test: `atest WMShellFlickerTests:EnterSplitScreenNonResizableNotDock` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FlakyTest(bugId = 173875043) class EnterSplitScreenNonResizableNotDock( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testLegacySplitScreenNonResizeableActivityNotDock", configuration) + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + super.transition(this, configuration) + teardown { + eachRun { + nonResizeableApp.exit(wmHelper) } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - nonResizeableApp.launchViaIntent(wmHelper) - device.openQuickstep() - if (device.canSplitScreen()) { - Assert.fail("Non-resizeable app should not enter split screen") - } - } - assertions { - layersTrace { - dockedStackDividerIsInvisible() - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, - SPLASH_SCREEN_NAME, - nonResizeableApp.defaultWindowName, - splitScreenApp.defaultWindowName), - bugId = 178447631 - ) - } - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(WALLPAPER_TITLE, - LAUNCHER_PACKAGE_NAME, - SPLASH_SCREEN_NAME, - nonResizeableApp.defaultWindowName, - splitScreenApp.defaultWindowName) - ) - end("appWindowIsVisible") { - isInvisible(nonResizeableApp.defaultWindowName) - } - } + } + transitions { + nonResizeableApp.launchViaIntent(wmHelper) + device.openQuickstep(wmHelper) + if (device.canSplitScreen(wmHelper)) { + Assert.fail("Non-resizeable app should not enter split screen") } } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, defaultTransitionSetup, testSpec, + } + + @Test + fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible() + + @FlakyTest(bugId = 178447631) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, + SPLASH_SCREEN_NAME, + nonResizeableApp.defaultWindowName, + splitScreenApp.defaultWindowName) + ) + + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(WALLPAPER_TITLE, + LAUNCHER_PACKAGE_NAME, + SPLASH_SCREEN_NAME, + nonResizeableApp.defaultWindowName, + splitScreenApp.defaultWindowName) + ) + + @Test + fun appWindowIsVisible() { + testSpec.assertWmEnd { + isInvisible(nonResizeableApp.defaultWindowName) + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt index 493366553623..acd570a3773e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt @@ -16,17 +16,15 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesInVisible import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.layerBecomesInvisible @@ -34,8 +32,10 @@ import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -44,51 +44,72 @@ import org.junit.runners.Parameterized * Test open resizeable activity split in primary, and drag divider to bottom exit split screen * To run this test: `atest WMShellFlickerTests:ExitLegacySplitScreenFromBottom` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class ExitLegacySplitScreenFromBottom( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testExitLegacySplitScreenFromBottom", configuration) + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + super.transition(this, configuration) + setup { + eachRun { + splitScreenApp.launchViaIntent(wmHelper) + device.launchSplitScreen(wmHelper) } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - device.launchSplitScreen() - device.exitSplitScreenFromBottom() - } - assertions { - layersTrace { - layerBecomesInvisible(DOCKED_STACK_DIVIDER) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName), - bugId = 178447631 - ) - } - windowManagerTrace { - appWindowBecomesInVisible(secondaryApp.defaultWindowName) - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName), - bugId = 178447631 - ) - } + } + teardown { + eachRun { + splitScreenApp.exit(wmHelper) } } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, defaultTransitionSetup, testSpec, + transitions { + device.exitSplitScreenFromBottom() + } + } + + @Presubmit + @Test + fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(DOCKED_STACK_DIVIDER) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName) + ) + + @Presubmit + @Test + fun appWindowBecomesInVisible() = + testSpec.appWindowBecomesInVisible(secondaryApp.defaultWindowName) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @FlakyTest(bugId = 178447631) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName) + ) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0) // bugId = 175687842 + supportedRotations = listOf(Surface.ROTATION_0) // b/175687842 ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt index ff3a979717f2..cef188695ce7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt @@ -16,16 +16,17 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesInVisible import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible @@ -34,6 +35,7 @@ import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEn import com.android.wm.shell.flicker.dockedStackDividerIsInvisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -45,49 +47,71 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class ExitPrimarySplitScreenShowSecondaryFullscreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testExitPrimarySplitScreenShowSecondaryFullscreen", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - device.launchSplitScreen() - secondaryApp.reopenAppFromOverview() - // TODO(b/175687842) Can not find Split screen divider, use exit() instead - splitScreenApp.exit() - } - assertions { - layersTrace { - dockedStackDividerIsInvisible(bugId = 175687842) - layerBecomesInvisible(splitScreenApp.defaultWindowName) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName), - bugId = 178447631 - ) - } - windowManagerTrace { - appWindowBecomesInVisible(splitScreenApp.defaultWindowName) - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, - secondaryApp.defaultWindowName), - bugId = 178447631 - ) - } + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + super.transition(this, configuration) + teardown { + eachRun { + secondaryApp.exit(wmHelper) } } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, defaultTransitionSetup, testSpec, + transitions { + splitScreenApp.launchViaIntent(wmHelper) + secondaryApp.launchViaIntent(wmHelper) + device.launchSplitScreen(wmHelper) + device.reopenAppFromOverview(wmHelper) + // TODO(b/175687842) Can not find Split screen divider, use exit() instead + splitScreenApp.exit(wmHelper) + } + } + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible() + + @Presubmit + @Test + fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName) + ) + + @Presubmit + @Test + fun appWindowBecomesInVisible() = + testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @FlakyTest(bugId = 178447631) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName, + secondaryApp.defaultWindowName) + ) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910 ) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt new file mode 100644 index 000000000000..1e89a25c06df --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt @@ -0,0 +1,47 @@ +/* + * 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.wm.shell.flicker.legacysplitscreen + +import android.view.Surface +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen + +abstract class LegacySplitScreenRotateTransition( + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + setup { + eachRun { + device.wakeUpAndGoToHomeScreen() + device.openQuickStepAndClearRecentAppsFromOverview(wmHelper) + secondaryApp.launchViaIntent(wmHelper) + splitScreenApp.launchViaIntent(wmHelper) + } + } + teardown { + eachRun { + splitScreenApp.exit(wmHelper) + secondaryApp.exit(wmHelper) + this.setRotation(Surface.ROTATION_0) + } + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt index 03b6edf0ff2a..7f69a66e6e82 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt @@ -19,15 +19,15 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit import android.support.test.launcherhelper.LauncherStrategyFactory import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.focusDoesNotChange -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.exitSplitScreen -import com.android.server.wm.flicker.helpers.isInSplitScreen import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview import com.android.server.wm.flicker.helpers.setRotation @@ -39,13 +39,13 @@ import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEnt import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible import com.android.wm.shell.flicker.helpers.SimpleAppHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -54,81 +54,100 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:LegacySplitScreenToLauncher` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class LegacySplitScreenToLauncher( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation) - .launcherStrategy.supportedLauncherPackage - val testApp = SimpleAppHelper(instrumentation) + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + private val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation) + .launcherStrategy.supportedLauncherPackage + private val testApp = SimpleAppHelper(instrumentation) - // b/161435597 causes the test not to work on 90 degrees - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - supportedRotations = listOf(Surface.ROTATION_0)) { configuration -> - withTestName { - buildTestTag("splitScreenToLauncher", configuration) + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + setup { + test { + device.wakeUpAndGoToHomeScreen() + device.openQuickStepAndClearRecentAppsFromOverview(wmHelper) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - device.openQuickStepAndClearRecentAppsFromOverview() - } - eachRun { - testApp.launchViaIntent(wmHelper) - this.setRotation(configuration.endRotation) - device.launchSplitScreen() - device.waitForIdle() - } + eachRun { + testApp.launchViaIntent(wmHelper) + this.setRotation(configuration.endRotation) + device.launchSplitScreen(wmHelper) + device.waitForIdle() } - teardown { - eachRun { - testApp.exit() - } - test { - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - } - } - transitions { - device.exitSplitScreen() - } - assertions { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - } - - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.endRotation) - navBarLayerRotatesAndScales(configuration.endRotation) - statusBarLayerRotatesScales(configuration.endRotation) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(launcherPackageName)) - - // b/161435597 causes the test not to work on 90 degrees - dockedStackDividerBecomesInvisible() - - layerBecomesInvisible(testApp.getPackage()) - } - - eventLog { - focusDoesNotChange(bugId = 151179149) - } + } + teardown { + eachRun { + testApp.exit(wmHelper) } } + transitions { + device.exitSplitScreen() + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.endRotation) + + @Presubmit + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation) + + @Presubmit + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(launcherPackageName)) + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun dockedStackDividerBecomesInvisible() = testSpec.dockedStackDividerBecomesInvisible() + + @Presubmit + @Test + fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(testApp.getPackage()) + + @FlakyTest(bugId = 151179149) + @Test + fun focusDoesNotChange() = testSpec.focusDoesNotChange() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + // b/161435597 causes the test not to work on 90 degrees + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt index 328ff88cd41b..91ea8716e4f0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt @@ -17,36 +17,35 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.app.Instrumentation -import android.os.Bundle import android.support.test.launcherhelper.LauncherStrategyFactory import android.view.Surface +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.wm.shell.flicker.helpers.SplitScreenHelper -abstract class LegacySplitScreenTransition( - protected val instrumentation: Instrumentation -) { - internal val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation) - internal val secondaryApp = SplitScreenHelper.getSecondary(instrumentation) - internal val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation) - internal val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation) +abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) { + protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + protected val isRotated = testSpec.config.startRotation.isRotated() + protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation) + protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation) + protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation) + protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation) .launcherStrategy.supportedLauncherPackage - internal val LIVE_WALLPAPER_PACKAGE_NAME = - "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2" - internal val LETTERBOX_NAME = "Letterbox" - internal val TOAST_NAME = "Toast" - internal val SPLASH_SCREEN_NAME = "Splash Screen" - internal open val defaultTransitionSetup: FlickerBuilder.(Bundle) -> Unit + protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> setup { eachRun { device.wakeUpAndGoToHomeScreen() - device.openQuickStepAndClearRecentAppsFromOverview() + device.openQuickStepAndClearRecentAppsFromOverview(wmHelper) secondaryApp.launchViaIntent(wmHelper) splitScreenApp.launchViaIntent(wmHelper) this.setRotation(configuration.startRotation) @@ -54,46 +53,46 @@ abstract class LegacySplitScreenTransition( } teardown { eachRun { - splitScreenApp.exit() - secondaryApp.exit() + secondaryApp.exit(wmHelper) + splitScreenApp.exit(wmHelper) this.setRotation(Surface.ROTATION_0) } } } - internal open val cleanSetup: FlickerBuilder.(Bundle) -> Unit - get() = { configuration -> - setup { - eachRun { - device.wakeUpAndGoToHomeScreen() - device.openQuickStepAndClearRecentAppsFromOverview() - this.setRotation(configuration.startRotation) - } - } - teardown { - eachRun { - nonResizeableApp.exit() - this.setRotation(Surface.ROTATION_0) - } - } + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + transition(this, testSpec.config) } + } - internal open val customRotateSetup: FlickerBuilder.(Bundle) -> Unit + internal open val cleanSetup: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { configuration -> setup { eachRun { device.wakeUpAndGoToHomeScreen() - device.openQuickStepAndClearRecentAppsFromOverview() - secondaryApp.launchViaIntent(wmHelper) - splitScreenApp.launchViaIntent(wmHelper) + device.openQuickStepAndClearRecentAppsFromOverview(wmHelper) + this.setRotation(configuration.startRotation) } } teardown { eachRun { - splitScreenApp.exit() - secondaryApp.exit() + nonResizeableApp.exit(wmHelper) + splitScreenApp.exit(wmHelper) + device.pressHome() this.setRotation(Surface.ROTATION_0) } } } + + companion object { + internal const val LIVE_WALLPAPER_PACKAGE_NAME = + "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2" + internal const val LETTERBOX_NAME = "Letterbox" + internal const val TOAST_NAME = "Toast" + internal const val SPLASH_SCREEN_NAME = "Splash Screen" + } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt index f2a7cda3b42d..caafa278d297 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt @@ -16,25 +16,26 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesInVisible import com.android.server.wm.flicker.appWindowBecomesVisible import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.layerBecomesVisible import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -44,58 +45,73 @@ import org.junit.runners.Parameterized * (Non resizable activity launch via recent overview) * To run this test: `atest WMShellFlickerTests:NonResizableDismissInLegacySplitScreen` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class NonResizableDismissInLegacySplitScreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testNonResizableDismissInLegacySplitScreen", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + cleanSetup(this, configuration) + setup { + eachRun { nonResizeableApp.launchViaIntent(wmHelper) splitScreenApp.launchViaIntent(wmHelper) - device.launchSplitScreen() - nonResizeableApp.reopenAppFromOverview() - wmHelper.waitForAppTransitionIdle() - } - assertions { - layersTrace { - layerBecomesVisible(nonResizeableApp.defaultWindowName) - layerBecomesInvisible(splitScreenApp.defaultWindowName) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, - LETTERBOX_NAME, TOAST_NAME, - splitScreenApp.defaultWindowName, - nonResizeableApp.defaultWindowName), - bugId = 178447631 - ) - } - windowManagerTrace { - appWindowBecomesVisible(nonResizeableApp.defaultWindowName) - appWindowBecomesInVisible(splitScreenApp.defaultWindowName) - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, - LETTERBOX_NAME, TOAST_NAME, - splitScreenApp.defaultWindowName, - nonResizeableApp.defaultWindowName), - bugId = 178447631 - ) - } + device.launchSplitScreen(wmHelper) } } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, cleanSetup, testSpec, + transitions { + device.reopenAppFromOverview(wmHelper) + } + } + + @Presubmit + @Test + fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, + LETTERBOX_NAME, TOAST_NAME, + splitScreenApp.defaultWindowName, + nonResizeableApp.defaultWindowName) + ) + + @Presubmit + @Test + fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName) + + @Presubmit + @Test + fun appWindowBecomesVisible() = + testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName) + + @Presubmit + @Test + fun appWindowBecomesInVisible() = + testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, + LETTERBOX_NAME, TOAST_NAME, + splitScreenApp.defaultWindowName, + nonResizeableApp.defaultWindowName) + ) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt index 421ecffc97d8..543484ac9759 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt @@ -16,25 +16,25 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesInVisible import com.android.server.wm.flicker.appWindowBecomesVisible import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.layerBecomesInvisible import com.android.server.wm.flicker.layerBecomesVisible import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -47,56 +47,73 @@ import org.junit.runners.Parameterized @Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class NonResizableLaunchInLegacySplitScreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testNonResizableLaunchInLegacySplitScreen", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + cleanSetup(this, configuration) + setup { + eachRun { splitScreenApp.launchViaIntent(wmHelper) - device.launchSplitScreen() - nonResizeableApp.launchViaIntent(wmHelper) - wmHelper.waitForAppTransitionIdle() - } - assertions { - layersTrace { - layerBecomesVisible(nonResizeableApp.defaultWindowName) - layerBecomesInvisible(splitScreenApp.defaultWindowName) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(DOCKED_STACK_DIVIDER, - LAUNCHER_PACKAGE_NAME, - LETTERBOX_NAME, - nonResizeableApp.defaultWindowName, - splitScreenApp.defaultWindowName), - bugId = 178447631 - ) - } - windowManagerTrace { - appWindowBecomesVisible(nonResizeableApp.defaultWindowName) - appWindowBecomesInVisible(splitScreenApp.defaultWindowName) - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(DOCKED_STACK_DIVIDER, - LAUNCHER_PACKAGE_NAME, - LETTERBOX_NAME, - nonResizeableApp.defaultWindowName, - splitScreenApp.defaultWindowName), - bugId = 178447631 - ) - } + device.launchSplitScreen(wmHelper) } } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, cleanSetup, testSpec, + transitions { + nonResizeableApp.launchViaIntent(wmHelper) + wmHelper.waitForAppTransitionIdle() + } + } + + @Presubmit + @Test + fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName) + + @Presubmit + @Test + fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(DOCKED_STACK_DIVIDER, + LAUNCHER_PACKAGE_NAME, + LETTERBOX_NAME, + nonResizeableApp.defaultWindowName, + splitScreenApp.defaultWindowName) + ) + + @Presubmit + @Test + fun appWindowBecomesVisible() = + testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName) + + @Presubmit + @Test + fun appWindowBecomesInVisible() = + testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(DOCKED_STACK_DIVIDER, + LAUNCHER_PACKAGE_NAME, + LETTERBOX_NAME, + nonResizeableApp.defaultWindowName, + splitScreenApp.defaultWindowName) + ) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt index 7edef9314941..d22833784bcf 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt @@ -16,17 +16,16 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesVisible import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.layerBecomesVisible import com.android.server.wm.flicker.noUncoveredRegions @@ -34,10 +33,10 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.appPairsDividerBecomesVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -46,54 +45,66 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreen` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class OpenAppToLegacySplitScreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val wmHelper = WindowManagerStateHelper() - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testOpenAppToLegacySplitScreen", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - device.launchSplitScreen() - wmHelper.waitForAppTransitionIdle() - } - assertions { - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName), - bugId = 178447631) - appWindowBecomesVisible(splitScreenApp.getPackage()) - } + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + super.transition(this, configuration) + transitions { + device.launchSplitScreen(wmHelper) + wmHelper.waitForAppTransitionIdle() + } + } - layersTrace { - noUncoveredRegions(configuration.startRotation, enabled = false) - statusBarLayerIsAlwaysVisible() - appPairsDividerBecomesVisible() - layerBecomesVisible(splitScreenApp.getPackage()) - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName), - bugId = 178447631) - } + @FlakyTest(bugId = 178447631) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName) + ) - eventLog { - focusChanges(splitScreenApp.`package`, - "recents_animation_input_consumer", "NexusLauncherActivity", - bugId = 151179149) - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, defaultTransitionSetup, testSpec, + @Presubmit + @Test + fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(splitScreenApp.getPackage()) + + @FlakyTest + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun appPairsDividerBecomesVisible() = testSpec.appPairsDividerBecomesVisible() + + @Presubmit + @Test + fun layerBecomesVisible() = testSpec.layerBecomesVisible(splitScreenApp.getPackage()) + + @FlakyTest(bugId = 178447631) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry( + listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName) + ) + + @FlakyTest(bugId = 151179149) + @Test + fun focusChanges() = testSpec.focusChanges(splitScreenApp.`package`, + "recents_animation_input_consumer", "NexusLauncherActivity") + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910 ) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt index 54a37d71868d..f5174bc3cef7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt @@ -16,24 +16,19 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.platform.test.annotations.Presubmit import android.graphics.Region import android.util.Rational import android.view.Surface import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By -import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.focusDoesNotChange import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.exitSplitScreen -import com.android.server.wm.flicker.helpers.isInSplitScreen import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.resizeSplitScreen import com.android.server.wm.flicker.helpers.setRotation @@ -42,7 +37,6 @@ import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales @@ -50,8 +44,10 @@ import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEn import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.traces.layers.getVisibleBounds +import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER import com.android.wm.shell.flicker.helpers.SimpleAppHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -62,14 +58,162 @@ import org.junit.runners.Parameterized * * Currently it runs only in 0 degrees because of b/156100803 */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @FlakyTest(bugId = 159096424) class ResizeLegacySplitScreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { + testSpec: FlickerTestParameter +) : LegacySplitScreenTransition(testSpec) { + private val testAppTop = SimpleAppHelper(instrumentation) + private val testAppBottom = ImeAppHelper(instrumentation) + + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + setup { + eachRun { + device.wakeUpAndGoToHomeScreen() + this.setRotation(configuration.startRotation) + this.launcherStrategy.clearRecentAppsFromOverview() + testAppBottom.launchViaIntent(wmHelper) + device.pressHome() + testAppTop.launchViaIntent(wmHelper) + device.waitForIdle() + device.launchSplitScreen(wmHelper) + val snapshot = + device.findObject(By.res(device.launcherPackageName, "snapshot")) + snapshot.click() + testAppBottom.openIME(device) + device.pressBack() + device.resizeSplitScreen(startRatio) + } + } + teardown { + eachRun { + testAppTop.exit(wmHelper) + testAppBottom.exit(wmHelper) + } + } + transitions { + device.resizeSplitScreen(stopRatio) + } + } + + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @FlakyTest(bugId = 156223549) + @Test + fun topAppWindowIsAlwaysVisible() { + testSpec.assertWm { + this.showsAppWindow(sSimpleActivity) + } + } + + @FlakyTest(bugId = 156223549) + @Test + fun bottomAppWindowIsAlwaysVisible() { + testSpec.assertWm { + this.showsAppWindow(sImeActivity) + } + } + + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation) + + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.endRotation) + + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation) + + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + + @Test + fun topAppLayerIsAlwaysVisible() { + testSpec.assertLayers { + this.isVisible(sSimpleActivity) + } + } + + @Test + fun bottomAppLayerIsAlwaysVisible() { + testSpec.assertLayers { + this.isVisible(sImeActivity) + } + } + + @Test + fun dividerLayerIsAlwaysVisible() { + testSpec.assertLayers { + this.isVisible(DOCKED_STACK_DIVIDER) + } + } + + @FlakyTest + @Test + fun appsStartingBounds() { + testSpec.assertLayersStart { + val displayBounds = WindowUtils.displayBounds + val dividerBounds = + entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds + + val topAppBounds = Region(0, 0, dividerBounds.right, + dividerBounds.top + WindowUtils.dockedStackDividerInset) + val bottomAppBounds = Region(0, + dividerBounds.bottom - WindowUtils.dockedStackDividerInset, + displayBounds.right, + displayBounds.bottom - WindowUtils.navigationBarHeight) + this.coversExactly(topAppBounds, "SimpleActivity") + .coversExactly(bottomAppBounds, "ImeActivity") + } + } + + @FlakyTest + @Test + fun appsEndingBounds() { + testSpec.assertLayersStart { + val displayBounds = WindowUtils.displayBounds + val dividerBounds = + entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds + + val topAppBounds = Region(0, 0, dividerBounds.right, + dividerBounds.top + WindowUtils.dockedStackDividerInset) + val bottomAppBounds = Region(0, + dividerBounds.bottom - WindowUtils.dockedStackDividerInset, + displayBounds.right, + displayBounds.bottom - WindowUtils.navigationBarHeight) + + this.coversExactly(topAppBounds, sSimpleActivity) + .coversExactly(bottomAppBounds, sImeActivity) + } + } + + @Test + fun focusDoesNotChange() { + testSpec.assertEventLog { + focusDoesNotChange() + } + } + companion object { private const val sSimpleActivity = "SimpleActivity" private const val sImeActivity = "ImeActivity" @@ -78,126 +222,14 @@ class ResizeLegacySplitScreen( @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testAppTop = SimpleAppHelper(instrumentation) - val testAppBottom = ImeAppHelper(instrumentation) - - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - supportedRotations = listOf(Surface.ROTATION_0)) { configuration -> - withTestName { - val description = (startRatio.toString().replace("/", "-") + "_to_" + - stopRatio.toString().replace("/", "-")) - buildTestTag("resizeSplitScreen", configuration, description) - } - repeat { configuration.repetitions } - setup { - eachRun { - device.wakeUpAndGoToHomeScreen() - this.setRotation(configuration.startRotation) - this.launcherStrategy.clearRecentAppsFromOverview() - testAppBottom.launchViaIntent(wmHelper) - device.pressHome() - testAppTop.launchViaIntent(wmHelper) - device.waitForIdle() - device.launchSplitScreen() - val snapshot = - device.findObject(By.res(device.launcherPackageName, "snapshot")) - snapshot.click() - testAppBottom.openIME(device) - device.pressBack() - device.resizeSplitScreen(startRatio) - } - } - teardown { - eachRun { - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - device.pressHome() - testAppTop.exit() - testAppBottom.exit() - } - test { - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - } - } - transitions { - device.resizeSplitScreen(stopRatio) - } - assertions { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - - all("topAppWindowIsAlwaysVisible", bugId = 156223549) { - this.showsAppWindow(sSimpleActivity) - } - - all("bottomAppWindowIsAlwaysVisible", bugId = 156223549) { - this.showsAppWindow(sImeActivity) - } - } - - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.endRotation) - navBarLayerRotatesAndScales(configuration.endRotation) - statusBarLayerRotatesScales(configuration.endRotation) - visibleLayersShownMoreThanOneConsecutiveEntry() - - all("topAppLayerIsAlwaysVisible") { - this.showsLayer(sSimpleActivity) - } - - all("bottomAppLayerIsAlwaysVisible") { - this.showsLayer(sImeActivity) - } - - all("dividerLayerIsAlwaysVisible") { - this.showsLayer(DOCKED_STACK_DIVIDER) - } - - start("appsStartingBounds", enabled = false) { - val displayBounds = WindowUtils.displayBounds - val dividerBounds = - entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds - - val topAppBounds = Region(0, 0, dividerBounds.right, - dividerBounds.top + WindowUtils.dockedStackDividerInset) - val bottomAppBounds = Region(0, - dividerBounds.bottom - WindowUtils.dockedStackDividerInset, - displayBounds.right, - displayBounds.bottom - WindowUtils.navigationBarHeight) - this.hasVisibleRegion("SimpleActivity", topAppBounds) - .hasVisibleRegion("ImeActivity", bottomAppBounds) - } - - end("appsEndingBounds", enabled = false) { - val displayBounds = WindowUtils.displayBounds - val dividerBounds = - entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds - - val topAppBounds = Region(0, 0, dividerBounds.right, - dividerBounds.top + WindowUtils.dockedStackDividerInset) - val bottomAppBounds = Region(0, - dividerBounds.bottom - WindowUtils.dockedStackDividerInset, - displayBounds.right, - displayBounds.bottom - WindowUtils.navigationBarHeight) - - this.hasVisibleRegion(sSimpleActivity, topAppBounds) - .hasVisibleRegion(sImeActivity, bottomAppBounds) - } - } - - eventLog { - focusDoesNotChange() - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) + .map { + val description = (startRatio.toString().replace("/", "-") + "_to_" + + stopRatio.toString().replace("/", "-")) + val newName = "${FlickerTestParameter.defaultName(it.config)}_$description" + FlickerTestParameter(it.config, name = newName) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt index 214269e13203..c914adae2b7c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt @@ -16,17 +16,16 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesVisible import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales @@ -38,6 +37,7 @@ import com.android.wm.shell.flicker.dockedStackDividerIsVisible import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -46,50 +46,64 @@ import org.junit.runners.Parameterized * Test dock activity to primary split screen and rotate * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppAndEnterSplitScreen` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class RotateOneLaunchedAppAndEnterSplitScreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : LegacySplitScreenRotateTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + super.transition(this, configuration) + transitions { + device.launchSplitScreen(wmHelper) + this.setRotation(testSpec.config.startRotation) + } + } + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible() + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackPrimaryBoundsIsVisible() = + testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation, + splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 169271943) + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @FlakyTest(bugId = 169271943) + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun appWindowBecomesVisible() = + testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName) + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testRotateOneLaunchedAppAndEnterSplitScreen", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - device.launchSplitScreen() - this.setRotation(configuration.startRotation) - } - assertions { - layersTrace { - dockedStackDividerIsVisible(bugId = 175687842) - dockedStackPrimaryBoundsIsVisible( - configuration.startRotation, - splitScreenApp.defaultWindowName, bugId = 175687842) - navBarLayerRotatesAndScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - statusBarLayerRotatesScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - } - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - appWindowBecomesVisible(splitScreenApp.defaultWindowName) - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, customRotateSetup, testSpec, - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = SplitScreenHelper.TEST_REPETITIONS, + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt index 4290c923b38d..ffb20a4bc99a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt @@ -16,17 +16,16 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesVisible import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales @@ -38,6 +37,7 @@ import com.android.wm.shell.flicker.dockedStackDividerIsVisible import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -46,50 +46,61 @@ import org.junit.runners.Parameterized * Rotate * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppInSplitScreenMode` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class RotateOneLaunchedAppInSplitScreenMode( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : LegacySplitScreenRotateTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + super.transition(this, configuration) + transitions { + this.setRotation(testSpec.config.startRotation) + device.launchSplitScreen(wmHelper) + } + } + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible() + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackPrimaryBoundsIsVisible() = testSpec.dockedStackPrimaryBoundsIsVisible( + testSpec.config.startRotation, splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 169271943) + @Test + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @FlakyTest(bugId = 169271943) + @Test + fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun appWindowBecomesVisible() = + testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName) + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testRotateOneLaunchedAppInSplitScreenMode", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - this.setRotation(configuration.startRotation) - device.launchSplitScreen() - } - assertions { - layersTrace { - dockedStackDividerIsVisible(bugId = 175687842) - dockedStackPrimaryBoundsIsVisible( - configuration.startRotation, - splitScreenApp.defaultWindowName, bugId = 175687842) - navBarLayerRotatesAndScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - statusBarLayerRotatesScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - } - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - appWindowBecomesVisible(splitScreenApp.defaultWindowName) - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, customRotateSetup, testSpec, + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt index 4095b9a2e61e..8cf1990d406f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt @@ -16,18 +16,18 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesVisible import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible @@ -39,6 +39,7 @@ import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -47,54 +48,69 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppAndEnterSplitScreen` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class RotateTwoLaunchedAppAndEnterSplitScreen( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { + testSpec: FlickerTestParameter +) : LegacySplitScreenRotateTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + super.transition(this, configuration) + transitions { + this.setRotation(testSpec.config.startRotation) + device.launchSplitScreen(wmHelper) + device.reopenAppFromOverview(wmHelper) + } + } + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible() + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackPrimaryBoundsIsVisible() = + testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation, + splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackSecondaryBoundsIsVisible() = + testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation, + secondaryApp.defaultWindowName) + + @FlakyTest(bugId = 169271943) + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @FlakyTest(bugId = 169271943) + @Test + fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @Presubmit + @Test + fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testRotateTwoLaunchedAppAndEnterSplitScreen", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - transitions { - this.setRotation(configuration.startRotation) - device.launchSplitScreen() - secondaryApp.reopenAppFromOverview() - } - assertions { - layersTrace { - dockedStackDividerIsVisible(bugId = 175687842) - dockedStackPrimaryBoundsIsVisible( - configuration.startRotation, - splitScreenApp.defaultWindowName, 175687842) - dockedStackSecondaryBoundsIsVisible( - configuration.startRotation, - secondaryApp.defaultWindowName, bugId = 175687842) - navBarLayerRotatesAndScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - statusBarLayerRotatesScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - } - windowManagerTrace { - appWindowBecomesVisible(secondaryApp.defaultWindowName) - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } - } - } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, customRotateSetup, testSpec, + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt index aebf6067615e..9c798d8ea661 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt @@ -16,18 +16,18 @@ package com.android.wm.shell.flicker.legacysplitscreen -import android.os.Bundle import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowBecomesVisible import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible @@ -39,6 +39,7 @@ import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -47,59 +48,76 @@ import org.junit.runners.Parameterized * Test open app to split screen. * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppInSplitScreenMode` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class RotateTwoLaunchedAppInSplitScreenMode( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : LegacySplitScreenTransition(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - buildTestTag("testRotateTwoLaunchedAppInSplitScreenMode", configuration) - } - repeat { SplitScreenHelper.TEST_REPETITIONS } - setup { - eachRun { - device.launchSplitScreen() - splitScreenApp.reopenAppFromOverview() - this.setRotation(configuration.startRotation) - } - } - transitions { - this.setRotation(configuration.startRotation) - } - assertions { - layersTrace { - dockedStackDividerIsVisible(bugId = 175687842) - dockedStackPrimaryBoundsIsVisible( - configuration.startRotation, - splitScreenApp.defaultWindowName, bugId = 175687842) - dockedStackSecondaryBoundsIsVisible( - configuration.startRotation, - secondaryApp.defaultWindowName, bugId = 175687842) - navBarLayerRotatesAndScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - statusBarLayerRotatesScales( - configuration.startRotation, - configuration.endRotation, bugId = 169271943) - } - windowManagerTrace { - appWindowBecomesVisible(secondaryApp.defaultWindowName) - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } + testSpec: FlickerTestParameter +) : LegacySplitScreenRotateTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + super.transition(this, configuration) + setup { + eachRun { + device.launchSplitScreen(wmHelper) + device.reopenAppFromOverview(wmHelper) + this.setRotation(testSpec.config.startRotation) } } - return FlickerTestRunnerFactory.getInstance().buildTest( - instrumentation, customRotateSetup, testSpec, + transitions { + this.setRotation(testSpec.config.startRotation) + } + } + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible() + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackPrimaryBoundsIsVisible() = + testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation, + splitScreenApp.defaultWindowName) + + @FlakyTest(bugId = 175687842) + @Test + fun dockedStackSecondaryBoundsIsVisible() = + testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation, + secondaryApp.defaultWindowName) + + @FlakyTest(bugId = 169271943) + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @FlakyTest(bugId = 169271943) + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @Presubmit + @Test + fun appWindowBecomesVisible() = + testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName) + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0 /* bugId = 178685668 */)) + supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt deleted file mode 100644 index bc42d5ed04ce..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AppTestBase.kt +++ /dev/null @@ -1,33 +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.wm.shell.flicker.pip - -import android.os.SystemClock -import com.android.wm.shell.flicker.NonRotationTestBase - -abstract class AppTestBase( - rotationName: String, - rotation: Int -) : NonRotationTestBase(rotationName, rotation) { - companion object { - fun waitForAnimationComplete() { - // TODO: UiDevice doesn't have reliable way to wait for the completion of animation. - // Consider to introduce WindowManagerStateHelper to access Activity state. - SystemClock.sleep(1000) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt index d56ed02972fb..75c33c671008 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt @@ -16,11 +16,13 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible @@ -29,6 +31,7 @@ import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -39,64 +42,96 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EnterExitPipTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<Array<Any>> { - val testApp = FixedAppHelper(instrumentation) - val testSpec = getTransition(eachRun = true) { configuration -> - setup { - eachRun { - testApp.launchViaIntent(wmHelper) - } - } - transitions { - // This will bring PipApp to fullscreen - pipApp.launchViaIntent(wmHelper) - } - assertions { - val displayBounds = WindowUtils.getDisplayBounds(configuration.startRotation) - presubmit { - windowManagerTrace { - all("pipApp must remain inside visible bounds") { - coversAtMostRegion(pipApp.defaultWindowName, displayBounds) - } - all("Initially shows both app windows then pipApp hides testApp") { - showsAppWindow(testApp.defaultWindowName) - .showsAppWindowOnTop(pipApp.defaultWindowName) - .then() - .hidesAppWindow(testApp.defaultWindowName) - } - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } - layersTrace { - all("Initially shows both app layers then pipApp hides testApp") { - showsLayer(testApp.defaultWindowName) - .showsLayer(pipApp.defaultWindowName) - .then() - .hidesLayer(testApp.defaultWindowName) - } - start("testApp covers the fullscreen, pipApp remains inside display") { - hasVisibleRegion(testApp.defaultWindowName, displayBounds) - coversAtMostRegion(displayBounds, pipApp.defaultWindowName) - } - end("pipApp covers the fullscreen") { - hasVisibleRegion(pipApp.defaultWindowName, displayBounds) - } - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - } - } + testSpec: FlickerTestParameter +) : PipTransition(testSpec) { + private val testApp = FixedAppHelper(instrumentation) + private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) + + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = buildTransition(eachRun = true) { + setup { + eachRun { + testApp.launchViaIntent(wmHelper) } } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - testSpec, supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 5) + transitions { + // This will bring PipApp to fullscreen + pipApp.launchViaIntent(wmHelper) + } + } + + @Presubmit + @Test + fun pipAppRemainInsideVisibleBounds() { + testSpec.assertWm { + coversAtMost(displayBounds, pipApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun showBothAppWindowsThenHidePip() { + testSpec.assertWm { + showsAppWindow(testApp.defaultWindowName) + .showsAppWindowOnTop(pipApp.defaultWindowName) + .then() + .hidesAppWindow(testApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun showBothAppLayersThenHidePip() { + testSpec.assertLayers { + isVisible(testApp.defaultWindowName) + .isVisible(pipApp.defaultWindowName) + .then() + .isInvisible(testApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun testAppCoversFullScreenWithPipOnDisplay() { + testSpec.assertLayersStart { + coversExactly(displayBounds, testApp.defaultWindowName) + coversAtMost(displayBounds, pipApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun pipAppCoversFullScreen() { + testSpec.assertLayersEnd { + coversExactly(displayBounds, pipApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index ff31ba7d2c01..83dca53b1542 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -16,11 +16,14 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible @@ -30,6 +33,7 @@ import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.startRotation import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -40,58 +44,71 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterPipTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<Array<Any>> { - val testSpec = getTransition(eachRun = true, - stringExtras = emptyMap()) { configuration -> - transitions { - pipApp.clickEnterPipButton() - pipApp.expandPipWindow(wmHelper) - } - assertions { - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() +class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = buildTransition(eachRun = true, stringExtras = emptyMap()) { + transitions { + pipApp.clickEnterPipButton() + pipApp.expandPipWindow(wmHelper) + } + } - all("pipWindowBecomesVisible") { - this.showsAppWindow(pipApp.defaultWindowName) - } - } + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - layersTrace { - statusBarLayerIsAlwaysVisible() - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - layersTrace { - all("pipLayerBecomesVisible") { - this.showsLayer(pipApp.launcherName) - } - } - } + @Presubmit + @Test + fun pipWindowBecomesVisible() { + testSpec.assertWm { + this.showsAppWindow(pipApp.defaultWindowName) + } + } - flaky { - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0) - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0, bugId = 140855415) - } - } - } - } + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - testSpec, supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 5) + @Presubmit + @Test + fun pipLayerBecomesVisible() { + testSpec.assertLayers { + this.isVisible(pipApp.launcherName) + } + } + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @FlakyTest(bugId = 140855415) + @Test + fun noUncoveredRegions() = + testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt index eaaa2f6390be..9011f1a9fb6a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt @@ -16,22 +16,25 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.wm.shell.flicker.helpers.FixedAppHelper +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE -import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT +import com.android.wm.shell.flicker.helpers.FixedAppHelper +import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE +import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -42,82 +45,108 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EnterPipToOtherOrientationTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - private val testApp = FixedAppHelper(instrumentation) + testSpec: FlickerTestParameter +) : PipTransition(testSpec) { + private val testApp = FixedAppHelper(instrumentation) + private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) + private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 5) { configuration -> - setupAndTeardown(this, configuration) + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + setupAndTeardown(this, configuration) - setup { - eachRun { - // Launch a portrait only app on the fullscreen stack - testApp.launchViaIntent(wmHelper, stringExtras = mapOf( - EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())) - // Launch the PiP activity fixed as landscape - pipApp.launchViaIntent(wmHelper, stringExtras = mapOf( - EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())) - } + setup { + eachRun { + // Launch a portrait only app on the fullscreen stack + testApp.launchViaIntent(wmHelper, stringExtras = mapOf( + EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())) + // Launch the PiP activity fixed as landscape + pipApp.launchViaIntent(wmHelper, stringExtras = mapOf( + EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())) } - teardown { - eachRun { - pipApp.exit() - testApp.exit() - } - } - transitions { - // Enter PiP, and assert that the PiP is within bounds now that the device is back - // in portrait - broadcastActionTrigger.doAction(ACTION_ENTER_PIP) - wmHelper.waitPipWindowShown() - wmHelper.waitForAppTransitionIdle() + } + teardown { + eachRun { + pipApp.exit(wmHelper) + testApp.exit(wmHelper) } - assertions { - val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) - val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) + } + transitions { + // Enter PiP, and assert that the PiP is within bounds now that the device is back + // in portrait + broadcastActionTrigger.doAction(ACTION_ENTER_PIP) + wmHelper.waitPipWindowShown() + wmHelper.waitForAppTransitionIdle() + } + } - presubmit { - windowManagerTrace { - all("pipApp window is always on top") { - showsAppWindowOnTop(pipApp.defaultWindowName) - } - start("pipApp window hides testApp") { - isInvisible(testApp.defaultWindowName) - } - end("testApp windows is shown") { - isVisible(testApp.defaultWindowName) - } - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } + @Presubmit + @Test + fun pipAppWindowIsAlwaysOnTop() { + testSpec.assertWm { + showsAppWindowOnTop(pipApp.defaultWindowName) + } + } - layersTrace { - start("pipApp layer hides testApp") { - hasVisibleRegion(pipApp.defaultWindowName, startingBounds) - isInvisible(testApp.defaultWindowName) - } - } - } + @Presubmit + @Test + fun pipAppHidesTestApp() { + testSpec.assertWmStart { + isInvisible(testApp.defaultWindowName) + } + } - flaky { - layersTrace { - end("testApp layer covers fullscreen") { - hasVisibleRegion(testApp.defaultWindowName, endingBounds) - } - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - } - } - } - } + @Presubmit + @Test + fun testAppWindowIsVisible() { + testSpec.assertWmEnd { + isVisible(testApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun pipAppLayerHidesTestApp() { + testSpec.assertLayersStart { + coversExactly(startingBounds, pipApp.defaultWindowName) + isInvisible(testApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun testAppLayerCoversFullScreen() { + testSpec.assertLayersEnd { + coversExactly(endingBounds, testApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt index 707d28d9c4c0..96eb66c3cc28 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt @@ -48,7 +48,7 @@ fun WindowManagerStateSubject.isInPipMode( activity: ComponentName ): WindowManagerStateSubject = apply { val windowName = activity.toWindowName() - hasWindow(windowName) + contains(windowName) val pinnedWindows = wmState.pinnedWindows .map { it.title } Truth.assertWithMessage("Window not in PIP mode") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt new file mode 100644 index 000000000000..3e331761f767 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt @@ -0,0 +1,117 @@ +/* + * 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.wm.shell.flicker.pip + +import android.platform.test.annotations.Presubmit +import android.view.Surface +import androidx.test.filters.FlakyTest +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.focusChanges +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.noUncoveredRegions +import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerRotatesScales +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import org.junit.Test +import org.junit.runners.Parameterized + +abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = buildTransition(eachRun = true) { configuration -> + setup { + eachRun { + this.setRotation(configuration.startRotation) + } + } + teardown { + eachRun { + this.setRotation(Surface.ROTATION_0) + } + } + } + + @Presubmit + @Test + open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + open fun pipWindowBecomesInvisible() { + testSpec.assertWm { + this.showsAppWindow(PIP_WINDOW_TITLE) + .then() + .hidesAppWindow(PIP_WINDOW_TITLE) + } + } + + @Presubmit + @Test + open fun pipLayerBecomesInvisible() { + testSpec.assertLayers { + this.isVisible(PIP_WINDOW_TITLE) + .then() + .isInvisible(PIP_WINDOW_TITLE) + } + } + + @Presubmit + @Test + open fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @Presubmit + @Test + open fun noUncoveredRegions() = + testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) + + @Presubmit + @Test + open fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @FlakyTest(bugId = 151179149) + @Test + open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity") + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt new file mode 100644 index 000000000000..0408421c72a5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt @@ -0,0 +1,44 @@ +/* + * 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.wm.shell.flicker.pip + +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.dsl.FlickerBuilder +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip launch. + * To run this test: `atest WMShellFlickerTests:PipCloseWithDismissButton` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class PipCloseWithDismissButtonTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + transitions { + pipApp.closePipWindow(wmHelper) + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt new file mode 100644 index 000000000000..afaf33a7c46f --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt @@ -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.wm.shell.flicker.pip + +import android.platform.test.annotations.Postsubmit +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.statusBarLayerRotatesScales +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip launch. + * To run this test: `atest WMShellFlickerTests:PipCloseWithSwipe` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + transitions { + val pipRegion = wmHelper.getWindowRegion(pipApp.component).bounds + val pipCenterX = pipRegion.centerX() + val pipCenterY = pipRegion.centerY() + val displayCenterX = device.displayWidth / 2 + device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 5) + } + } + + @Postsubmit + @Test + override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible() + + @Postsubmit + @Test + override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible() + + @Postsubmit + @Test + override fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @Postsubmit + @Test + override fun noUncoveredRegions() = super.noUncoveredRegions() + + @Postsubmit + @Test + override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt index f054e6412080..46339603f806 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt @@ -16,17 +16,20 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.startRotation import com.android.wm.shell.flicker.IME_WINDOW_NAME import com.android.wm.shell.flicker.helpers.ImeAppHelper import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -37,58 +40,67 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipKeyboardTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - private const val TAG_IME_VISIBLE = "imeIsVisible" +class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + private val imeApp = ImeAppHelper(instrumentation) - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val imeApp = ImeAppHelper(instrumentation) - val testSpec = getTransition(eachRun = false) { configuration -> - setup { - test { - imeApp.launchViaIntent(wmHelper) - setRotation(configuration.startRotation) - } + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = buildTransition(eachRun = false) { configuration -> + setup { + test { + imeApp.launchViaIntent(wmHelper) + setRotation(configuration.startRotation) } - teardown { - test { - imeApp.exit() - setRotation(Surface.ROTATION_0) - } + } + teardown { + test { + imeApp.exit(wmHelper) + setRotation(Surface.ROTATION_0) } - transitions { - // open the soft keyboard - imeApp.openIME(wmHelper) - createTag(TAG_IME_VISIBLE) + } + transitions { + // open the soft keyboard + imeApp.openIME(wmHelper) + createTag(TAG_IME_VISIBLE) - // then close it again - imeApp.closeIME(wmHelper) - } - assertions { - presubmit { - windowManagerTrace { - // Ensure the pip window remains visible throughout - // any keyboard interactions - all("pipInVisibleBounds") { - val displayBounds = WindowUtils.getDisplayBounds( - configuration.startRotation) - coversAtMostRegion(pipApp.defaultWindowName, displayBounds) - } - // Ensure that the pip window does not obscure the keyboard - tag(TAG_IME_VISIBLE) { - isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName) - } - } - } - } + // then close it again + imeApp.closeIME(wmHelper) } + } + + /** + * Ensure the pip window remains visible throughout any keyboard interactions + */ + @Presubmit + @Test + fun pipInVisibleBounds() { + testSpec.assertWm { + val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) + coversAtMost(displayBounds, pipApp.defaultWindowName) + } + } + + /** + * Ensure that the pip window does not obscure the keyboard + */ + @Presubmit + @Test + fun pipIsAboveAppWindow() { + testSpec.assertWmTag(TAG_IME_VISIBLE) { + isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName) + } + } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - testSpec, supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 5) + companion object { + private const val TAG_IME_VISIBLE = "imeIsVisible" + + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt index f10bd7f1e45a..97afc65b8b61 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt @@ -16,21 +16,24 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Postsubmit import android.view.Surface import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.dsl.runFlicker +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.exitSplitScreen -import com.android.server.wm.flicker.helpers.isInSplitScreen import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.helpers.ImeAppHelper import com.android.wm.shell.flicker.helpers.FixedAppHelper -import com.android.wm.shell.flicker.helpers.PipAppHelper -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.repetitions +import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.removeAllTasksButHome import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP @@ -46,83 +49,103 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @FlakyTest(bugId = 161435597) -class PipLegacySplitScreenTest( - rotationName: String, - rotation: Int -) : AppTestBase(rotationName, rotation) { - private val pipApp = PipAppHelper(instrumentation) +class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { private val imeApp = ImeAppHelper(instrumentation) private val testApp = FixedAppHelper(instrumentation) + private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) - @Test - fun testShowsPipLaunchingToSplitScreen() { - runFlicker(instrumentation) { - withTestName { "testShowsPipLaunchingToSplitScreen" } - repeat { TEST_REPETITIONS } + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } setup { test { removeAllTasksButHome() device.wakeUpAndGoToHomeScreen() - pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true")) - waitForAnimationComplete() + pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true"), + wmHelper = wmHelper) } } transitions { - testApp.launchViaIntent() - device.launchSplitScreen() - imeApp.launchViaIntent() - waitForAnimationComplete() + testApp.launchViaIntent(wmHelper) + device.launchSplitScreen(wmHelper) + imeApp.launchViaIntent(wmHelper) } teardown { eachRun { - imeApp.exit() - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - testApp.exit() + imeApp.exit(wmHelper) + testApp.exit(wmHelper) } test { removeAllTasksButHome() } } - assertions { - val displayBounds = WindowUtils.getDisplayBounds(rotation) - windowManagerTrace { - all("PIP window must remain inside visible bounds") { - coversAtMostRegion(pipApp.defaultWindowName, displayBounds) - } - end("Both app windows should be visible") { - isVisible(testApp.defaultWindowName) - isVisible(imeApp.defaultWindowName) - noWindowsOverlap(setOf(testApp.defaultWindowName, imeApp.defaultWindowName)) - } - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } - layersTrace { - all("PIP layer must remain inside visible bounds") { - coversAtMostRegion(displayBounds, pipApp.defaultWindowName) - } - end("Both app layers should be visible") { - coversAtMostRegion(displayBounds, testApp.defaultWindowName) - coversAtMostRegion(displayBounds, imeApp.defaultWindowName) - } - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - } - } + } + + @Postsubmit + @Test + fun pipWindowInsideDisplayBounds() { + testSpec.assertWm { + coversAtMost(displayBounds, pipApp.defaultWindowName) + } + } + + @Postsubmit + @Test + fun bothAppWindowsVisible() { + testSpec.assertWmEnd { + isVisible(testApp.defaultWindowName) + isVisible(imeApp.defaultWindowName) + noWindowsOverlap(testApp.defaultWindowName, imeApp.defaultWindowName) + } + } + + @Postsubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun pipLayerInsideDisplayBounds() { + testSpec.assertLayers { + coversAtMost(displayBounds, pipApp.defaultWindowName) + } + } + + @Postsubmit + @Test + fun bothAppLayersVisible() { + testSpec.assertLayersEnd { + coversAtMost(displayBounds, testApp.defaultWindowName) + coversAtMost(displayBounds, imeApp.defaultWindowName) } } + @Postsubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + companion object { const val TEST_REPETITIONS = 2 + @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val supportedRotations = intArrayOf(Surface.ROTATION_0) - return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + supportedRotations = listOf(Surface.ROTATION_0), + repetitions = TEST_REPETITIONS + ) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt new file mode 100644 index 000000000000..4c95da284d9e --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt @@ -0,0 +1,94 @@ +/* + * 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.wm.shell.flicker.pip + +import android.platform.test.annotations.Postsubmit +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.google.common.truth.Truth +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip launch. + * To run this test: `atest WMShellFlickerTests:PipMovesInAllApps` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + private val taplInstrumentation = LauncherInstrumentation() + + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = buildTransition(eachRun = false) { + teardown { + eachRun { + taplInstrumentation.pressHome() + } + } + transitions { + taplInstrumentation.pressHome().switchToAllApps() + wmHelper.waitForAppTransitionIdle() + } + } + + @Postsubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun pipAlwaysVisible() = testSpec.assertWm { this.showsAppWindow(pipApp.windowName) } + + @Postsubmit + @Test + fun pipWindowMovesUp() = testSpec.assertWmEnd { + val initialState = this.trace?.first()?.wmState + ?: error("Trace should not be empty") + val startPos = initialState.pinnedWindows.first().frame + val currPos = this.wmState.pinnedWindows.first().frame + val subject = Truth.assertWithMessage("Pip should have moved up") + subject.that(currPos.top).isGreaterThan(startPos.top) + subject.that(currPos.bottom).isGreaterThan(startPos.bottom) + subject.that(currPos.left).isEqualTo(startPos.left) + subject.that(currPos.right).isEqualTo(startPos.right) + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index ade65ac8aa63..df835d21e73f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -16,11 +16,14 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.setRotation @@ -34,6 +37,7 @@ import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.statusBarLayerRotatesScales import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -44,73 +48,91 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipRotationTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val fixedApp = FixedAppHelper(instrumentation) - val testSpec = getTransition(eachRun = false) { configuration -> - setup { - test { - fixedApp.launchViaIntent(wmHelper) - } - eachRun { - setRotation(configuration.startRotation) - } +class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + private val fixedApp = FixedAppHelper(instrumentation) + private val startingBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) + private val endingBounds = WindowUtils.getDisplayBounds(testSpec.config.endRotation) + + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = buildTransition(eachRun = false) { configuration -> + setup { + test { + fixedApp.launchViaIntent(wmHelper) } - transitions { - setRotation(configuration.endRotation) + eachRun { + setRotation(configuration.startRotation) } - teardown { - eachRun { - setRotation(Surface.ROTATION_0) - } + } + transitions { + setRotation(configuration.endRotation) + } + teardown { + eachRun { + setRotation(Surface.ROTATION_0) } - assertions { - val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation) - val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation) + } + } - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - layersTrace { - noUncoveredRegions(configuration.startRotation, - configuration.endRotation, allStates = false) - } - } + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - flaky { - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - navBarLayerRotatesAndScales(configuration.startRotation, - configuration.endRotation, bugId = 140855415) - statusBarLayerRotatesScales(configuration.startRotation, - configuration.endRotation, bugId = 140855415) + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + testSpec.config.endRotation, allStates = false) - start("appLayerRotates_StartingBounds", bugId = 140855415) { - hasVisibleRegion(fixedApp.defaultWindowName, startingBounds) - coversAtMostRegion(startingBounds, pipApp.defaultWindowName) - } - end("appLayerRotates_EndingBounds", bugId = 140855415) { - hasVisibleRegion(fixedApp.defaultWindowName, endingBounds) - coversAtMostRegion(endingBounds, pipApp.defaultWindowName) - } - } - } - } - } + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - return FlickerTestRunnerFactory.getInstance().buildRotationTest(instrumentation, - testSpec, supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90), + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @FlakyTest(bugId = 140855415) + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, + testSpec.config.endRotation) + + @FlakyTest(bugId = 140855415) + @Test + fun appLayerRotates_StartingBounds() { + testSpec.assertLayersStart { + coversExactly(startingBounds, fixedApp.defaultWindowName) + coversAtMost(startingBounds, pipApp.defaultWindowName) + } + } + + @FlakyTest(bugId = 140855415) + @Test + fun appLayerRotates_EndingBounds() { + testSpec.assertLayersEnd { + coversExactly(endingBounds, fixedApp.defaultWindowName) + coversAtMost(endingBounds, pipApp.defaultWindowName) + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigRotationTests( + supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90), repetitions = 5) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt index 96b6c912d152..7ba085d3cf1a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt @@ -16,13 +16,14 @@ package com.android.wm.shell.flicker.pip +import com.android.wm.shell.flicker.FlickerTestBase import com.android.wm.shell.flicker.helpers.PipAppHelper import org.junit.Before abstract class PipTestBase( rotationName: String, rotation: Int -) : AppTestBase(rotationName, rotation) { +) : FlickerTestBase(rotationName, rotation) { protected val testApp = PipAppHelper(instrumentation) @Before diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt index f2d58997d1f2..1bb1d2861f3f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt @@ -16,11 +16,14 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.focusChanges import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -32,6 +35,7 @@ import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -42,73 +46,89 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipToAppTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<Array<Any>> { - val testSpec = getTransition(eachRun = true) { configuration -> - setup { - eachRun { - this.setRotation(configuration.startRotation) - } - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } +class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = buildTransition(eachRun = true) { configuration -> + setup { + eachRun { + this.setRotation(configuration.startRotation) } - transitions { - pipApp.expandPipWindowToApp(wmHelper) + } + teardown { + eachRun { + this.setRotation(Surface.ROTATION_0) } - assertions { - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() + } + transitions { + pipApp.expandPipWindowToApp(wmHelper) + } + } - all("appReplacesPipWindow") { - this.showsAppWindow(PIP_WINDOW_TITLE) - .then() - .showsAppWindowOnTop(pipApp.launcherName) - } - } + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - layersTrace { - statusBarLayerIsAlwaysVisible() - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - all("appReplacesPipLayer") { - this.showsLayer(PIP_WINDOW_TITLE) - .then() - .showsLayer(pipApp.launcherName) - } - } - } + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - flaky { - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0) - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0, bugId = 140855415) - } + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - eventLog { - focusChanges( - "NexusLauncherActivity", pipApp.launcherName, - "NexusLauncherActivity", bugId = 151179149) - } - } - } - } + @Presubmit + @Test + fun appReplacesPipWindow() { + testSpec.assertWm { + this.showsAppWindow(PIP_WINDOW_TITLE) + .then() + .showsAppWindowOnTop(pipApp.launcherName) + } + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @Presubmit + @Test + fun appReplacesPipLayer() { + testSpec.assertLayers { + this.isVisible(PIP_WINDOW_TITLE) + .then() + .isVisible(pipApp.launcherName) + } + } - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5) + @Presubmit + @Test + fun noUncoveredRegions() = + testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @FlakyTest(bugId = 151179149) + @Test + fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", + pipApp.launcherName, "NexusLauncherActivity") + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 5) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt deleted file mode 100644 index 1b44377425db..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt +++ /dev/null @@ -1,115 +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.wm.shell.flicker.pip - -import android.view.Surface -import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test Pip launch. - * To run this test: `atest WMShellFlickerTests:PipToHomeTest` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipToHomeTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<Array<Any>> { - val testSpec = getTransition(eachRun = true) { configuration -> - setup { - eachRun { - this.setRotation(configuration.startRotation) - } - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } - } - transitions { - pipApp.closePipWindow(wmHelper) - } - assertions { - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - - all("pipWindowBecomesInvisible") { - this.showsAppWindow(PIP_WINDOW_TITLE) - .then() - .hidesAppWindow(PIP_WINDOW_TITLE) - } - } - - layersTrace { - statusBarLayerIsAlwaysVisible() - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - - all("pipLayerBecomesInvisible") { - this.showsLayer(PIP_WINDOW_TITLE) - .then() - .hidesLayer(PIP_WINDOW_TITLE) - } - } - } - - postsubmit { - layersTrace { - navBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0) - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - - flaky { - eventLog { - focusChanges(pipApp.launcherName, "NexusLauncherActivity", - bugId = 151179149) - } - } - } - } - - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - testSpec, supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5) - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index b1e404e4c8e6..b0a9afef9215 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransitionBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -18,18 +18,27 @@ package com.android.wm.shell.flicker.pip import android.app.Instrumentation import android.content.Intent -import android.os.Bundle import android.view.Surface +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.buildTestTag +import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.repetitions +import com.android.server.wm.flicker.startRotation import com.android.wm.shell.flicker.helpers.PipAppHelper import com.android.wm.shell.flicker.removeAllTasksButHome import com.android.wm.shell.flicker.testapp.Components -abstract class PipTransitionBase(protected val instrumentation: Instrumentation) { +abstract class PipTransition(protected val testSpec: FlickerTestParameter) { + protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + protected val isRotated = testSpec.config.startRotation.isRotated() + protected val pipApp = PipAppHelper(instrumentation) + protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation) + protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + // Helper class to process test actions by broadcast. protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) { private fun createIntentWithAction(broadcastAction: String): Intent { @@ -59,16 +68,20 @@ abstract class PipTransitionBase(protected val instrumentation: Instrumentation) } } - protected val pipApp = PipAppHelper(instrumentation) - protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation) + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + transition(this, testSpec.config) + } + } /** * Gets a configuration that handles basic setup and teardown of pip tests */ - protected val setupAndTeardown: FlickerBuilder.(Bundle) -> Unit - get() = { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } + protected val setupAndTeardown: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { setup { test { removeAllTasksButHome() @@ -81,7 +94,7 @@ abstract class PipTransitionBase(protected val instrumentation: Instrumentation) } test { removeAllTasksButHome() - pipApp.exit() + pipApp.exit(wmHelper) } } } @@ -95,11 +108,11 @@ abstract class PipTransitionBase(protected val instrumentation: Instrumentation) * @param extraSpec Addicional segment of flicker specification */ @JvmOverloads - open fun getTransition( + protected open fun buildTransition( eachRun: Boolean, stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true"), - extraSpec: FlickerBuilder.(Bundle) -> Unit = {} - ): FlickerBuilder.(Bundle) -> Unit { + extraSpec: FlickerBuilder.(Map<String, Any?>) -> Unit = {} + ): FlickerBuilder.(Map<String, Any?>) -> Unit { return { configuration -> setupAndTeardown(this, configuration) @@ -121,12 +134,12 @@ abstract class PipTransitionBase(protected val instrumentation: Instrumentation) teardown { eachRun { if (eachRun) { - pipApp.exit() + pipApp.exit(wmHelper) } } test { if (!eachRun) { - pipApp.exit() + pipApp.exit(wmHelper) } removeAllTasksButHome() } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt index c01bc94151e9..7916ce59af52 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt @@ -16,21 +16,25 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.wm.shell.flicker.pip.PipTransitionBase.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE +import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.Assert.assertEquals import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -41,75 +45,99 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class SetRequestedOrientationWhilePinnedTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - return FlickerTestRunnerFactory.getInstance().buildTest(instrumentation, - supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 1) { configuration -> - setupAndTeardown(this, configuration) + testSpec: FlickerTestParameter +) : PipTransition(testSpec) { + private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) + private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) - setup { - eachRun { - // Launch the PiP activity fixed as landscape - pipApp.launchViaIntent(wmHelper, stringExtras = mapOf( - EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(), - EXTRA_ENTER_PIP to "true")) - } - } - teardown { - eachRun { - pipApp.exit() - } - } - transitions { - // Request that the orientation is set to landscape - broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE) + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { configuration -> + setupAndTeardown(this, configuration) - // Launch the activity back into fullscreen and - // ensure that it is now in landscape - pipApp.launchViaIntent(wmHelper) - wmHelper.waitForFullScreenApp(pipApp.component) - wmHelper.waitForRotation(Surface.ROTATION_90) - assertEquals(Surface.ROTATION_90, device.displayRotation) + setup { + eachRun { + // Launch the PiP activity fixed as landscape + pipApp.launchViaIntent(wmHelper, stringExtras = mapOf( + EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(), + EXTRA_ENTER_PIP to "true")) } - assertions { - val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0) - val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90) - presubmit { - windowManagerTrace { - start("PIP window must remain inside display") { - coversAtMostRegion(pipApp.defaultWindowName, startingBounds) - } - end("pipApp shows on top") { - showsAppWindowOnTop(pipApp.defaultWindowName) - } - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - } - layersTrace { - start("PIP layer must remain inside display") { - coversAtMostRegion(startingBounds, pipApp.defaultWindowName) - } - end("pipApp layer covers fullscreen") { - hasVisibleRegion(pipApp.defaultWindowName, endingBounds) - } - } - } - - flaky { - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - } - } + } + teardown { + eachRun { + pipApp.exit(wmHelper) } } + transitions { + // Request that the orientation is set to landscape + broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE) + + // Launch the activity back into fullscreen and + // ensure that it is now in landscape + pipApp.launchViaIntent(wmHelper) + wmHelper.waitForFullScreenApp(pipApp.component) + wmHelper.waitForRotation(Surface.ROTATION_90) + assertEquals(Surface.ROTATION_90, device.displayRotation) + } + } + + @Presubmit + @Test + fun pipWindowInsideDisplay() { + testSpec.assertWmStart { + coversAtMost(startingBounds, pipApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun pipAppShowsOnTop() { + testSpec.assertWmEnd { + showsAppWindowOnTop(pipApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun pipLayerInsideDisplay() { + testSpec.assertLayersStart { + coversAtMost(startingBounds, pipApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun pipAppLayerCoversFullScreen() { + testSpec.assertLayersEnd { + coversExactly(endingBounds, pipApp.defaultWindowName) + } + } + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 1) } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp index 26627a47ee62..ea606df1536d 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "WMShellFlickerTestApp", srcs: ["**/*.java"], @@ -23,4 +32,4 @@ java_library { name: "wmshell-flicker-test-components", srcs: ["src/**/Components.java"], sdk_version: "test_current", -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp index c0ab20d9249f..fb53e5355c4a 100644 --- a/libs/WindowManager/Shell/tests/unittest/Android.bp +++ b/libs/WindowManager/Shell/tests/unittest/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "WMShellUnitTests", diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java index e094158e1144..27c626170a4b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java @@ -20,17 +20,17 @@ import static org.mockito.Mockito.mock; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; -import org.mockito.Mock; - public class TestAppPairsController extends AppPairsController { private TestAppPairsPool mPool; public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, DisplayController displayController) { - super(organizer, syncQueue, displayController, mock(ShellExecutor.class)); + super(organizer, syncQueue, displayController, mock(ShellExecutor.class), + mock(DisplayImeController.class)); mPool = new TestAppPairsPool(this); setPairsPool(mPool); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt index 416028088294..bdf75fcd8816 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubblePersistentRepositoryTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.bubbles.storage +import android.app.ActivityTaskManager.INVALID_TASK_ID import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase @@ -31,9 +32,10 @@ import org.junit.runner.RunWith class BubblePersistentRepositoryTest : ShellTestCase() { private val bubbles = listOf( - BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0), - BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428, "title"), - BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0) + BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0, null, 1), + BubbleEntity(10, "com.example.chat", "alice and bob", "key-2", 0, 16537428, "title", 2), + BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0, null, + INVALID_TASK_ID) ) private lateinit var repository: BubblePersistentRepository diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt index dd1a6a5a281e..05795fde7d6c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.bubbles.storage +import android.app.ActivityTaskManager.INVALID_TASK_ID import android.content.pm.LauncherApps import android.os.UserHandle import android.testing.AndroidTestingRunner @@ -37,10 +38,12 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { private val user0 = UserHandle.of(0) private val user10 = UserHandle.of(10) - private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0) + private val bubble1 = BubbleEntity(0, "com.example.messenger", "shortcut-1", "key-1", 120, 0, + null, 1) private val bubble2 = BubbleEntity(10, "com.example.chat", "alice and bob", - "key-2", 0, 16537428, "title") - private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0) + "key-2", 0, 16537428, "title", 2) + private val bubble3 = BubbleEntity(0, "com.example.messenger", "shortcut-2", "key-3", 120, 0, + null, INVALID_TASK_ID) private val bubbles = listOf(bubble1, bubble2, bubble3) @@ -105,13 +108,13 @@ class BubbleVolatileRepositoryTest : ShellTestCase() { @Test fun testAddBubbleMatchesByKey() { - val bubble = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0, "title") + val bubble = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0, "title", 1) repository.addBubbles(listOf(bubble)) assertEquals(bubble, repository.bubbles.get(0)) // Same key as first bubble but different entry val bubbleModified = BubbleEntity(0, "com.example.pkg", "shortcut-id", "key", 120, 0, - "different title") + "different title", 2) repository.addBubbles(listOf(bubbleModified)) assertEquals(bubbleModified, repository.bubbles.get(0)) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt index e0891a95c6a6..839b873d0c23 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleXmlHelperTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.bubbles.storage +import android.app.ActivityTaskManager.INVALID_TASK_ID import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase @@ -31,17 +32,18 @@ import java.io.ByteArrayOutputStream class BubbleXmlHelperTest : ShellTestCase() { private val bubbles = listOf( - BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0), - BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title"), - BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0) + BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0, null, 1), + BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title", 2), + BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0, null, + INVALID_TASK_ID) ) @Test fun testWriteXml() { val expectedEntries = """ -<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" /> -<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" /> -<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" /> +<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" tid="1" /> +<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" tid="2" /> +<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" tid="-1" /> """.trimIndent() ByteArrayOutputStream().use { writeXml(it, bubbles) @@ -56,9 +58,9 @@ class BubbleXmlHelperTest : ShellTestCase() { val src = """ <?xml version='1.0' encoding='utf-8' standalone='yes' ?> <bs v="1"> -<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" /> -<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" /> -<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" /> +<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" tid="1" /> +<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" tid="2" /> +<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" tid="-1" /> </bs> """.trimIndent() val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8))) @@ -79,4 +81,32 @@ class BubbleXmlHelperTest : ShellTestCase() { val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8))) assertEquals("failed parsing bubbles from xml\n$src", emptyList<BubbleEntity>(), actual) } + + /** + * In S we changed the XML to include a taskId, version didn't increase because we can set a + * reasonable default for taskId (INVALID_TASK_ID) if it wasn't in the XML previously, this + * tests that that works. + */ + @Test + fun testReadXMLWithoutTaskId() { + val expectedBubbles = listOf( + BubbleEntity(0, "com.example.messenger", "shortcut-1", "k1", 120, 0, null, + INVALID_TASK_ID), + BubbleEntity(10, "com.example.chat", "alice and bob", "k2", 0, 16537428, "title", + INVALID_TASK_ID), + BubbleEntity(0, "com.example.messenger", "shortcut-2", "k3", 120, 0, null, + INVALID_TASK_ID) + ) + + val src = """ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<bs v="1"> +<bb uid="0" pkg="com.example.messenger" sid="shortcut-1" key="k1" h="120" hid="0" /> +<bb uid="10" pkg="com.example.chat" sid="alice and bob" key="k2" h="0" hid="16537428" t="title" /> +<bb uid="0" pkg="com.example.messenger" sid="shortcut-2" key="k3" h="120" hid="0" /> +</bs> + """.trimIndent() + val actual = readXml(ByteArrayInputStream(src.toByteArray(Charsets.UTF_8))) + assertEquals("failed parsing bubbles from xml\n$src", expectedBubbles, actual) + } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java index 2b5b77e49e3a..88e754c58792 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java @@ -38,6 +38,12 @@ import com.android.internal.R; import org.junit.Test; +/** + * Tests for {@link DisplayLayout}. + * + * Build/Install/Run: + * atest WMShellUnitTests:DisplayLayoutTest + */ @SmallTest public class DisplayLayoutTest { @@ -70,18 +76,6 @@ public class DisplayLayoutTest { @Test public void testRotate() { // Basic rotate utility - Rect testParent = new Rect(0, 0, 1000, 600); - Rect testInner = new Rect(40, 20, 120, 80); - Rect testResult = new Rect(testInner); - DisplayLayout.rotateBounds(testResult, testParent, 1); - assertEquals(new Rect(20, 880, 80, 960), testResult); - testResult.set(testInner); - DisplayLayout.rotateBounds(testResult, testParent, 2); - assertEquals(new Rect(880, 20, 960, 80), testResult); - testResult.set(testInner); - DisplayLayout.rotateBounds(testResult, testParent, 3); - assertEquals(new Rect(520, 40, 580, 120), testResult); - Resources res = createResources(40, 50, false, 30, 40); DisplayInfo info = createDisplayInfo(1000, 1500, 60, ROTATION_0); DisplayLayout dl = new DisplayLayout(info, res, true, true); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java index 21bc32c6563c..d8aebc284bf1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java @@ -198,7 +198,7 @@ public class TaskStackListenerImplTest { @Test public void testOnActivityDismissingDockedStack() { - mImpl.onActivityDismissingDockedStack(); + mImpl.onActivityDismissingDockedTask(); verify(mCallback).onActivityDismissingDockedStack(); verify(mOtherCallback).onActivityDismissingDockedStack(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index 5821eed6f611..7b0e6b9a5ed7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.DisplayImeController; import org.junit.Before; import org.junit.Test; @@ -49,6 +50,7 @@ import org.mockito.MockitoAnnotations; public class SplitLayoutTests extends ShellTestCase { @Mock SplitLayout.LayoutChangeListener mLayoutChangeListener; @Mock SurfaceControl mRootLeash; + @Mock DisplayImeController mDisplayImeController; private SplitLayout mSplitLayout; @Before @@ -59,7 +61,8 @@ public class SplitLayoutTests extends ShellTestCase { mContext, getConfiguration(false), mLayoutChangeListener, - b -> b.setParent(mRootLeash)); + b -> b.setParent(mRootLeash), + mDisplayImeController); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java index 698315a77d8e..86d0d82222e4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java @@ -29,6 +29,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.DisplayImeController; import org.junit.Before; import org.junit.Test; @@ -42,6 +43,7 @@ import org.mockito.MockitoAnnotations; public class SplitWindowManagerTests extends ShellTestCase { @Mock SurfaceControl mSurfaceControl; @Mock SplitLayout mSplitLayout; + @Mock DisplayImeController mDisplayImeController; private SplitWindowManager mSplitWindowManager; @Before @@ -50,7 +52,7 @@ public class SplitWindowManagerTests extends ShellTestCase { final Configuration configuration = new Configuration(); configuration.setToDefaults(); mSplitWindowManager = new SplitWindowManager("TestSplitDivider", mContext, configuration, - b -> b.setParent(mSurfaceControl)); + b -> b.setParent(mSurfaceControl), mDisplayImeController); when(mSplitLayout.getDividerBounds()).thenReturn( new Rect(0, 0, configuration.windowConfiguration.getBounds().width(), configuration.windowConfiguration.getBounds().height())); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java index 8d5139b182f0..a8feb04f31d4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java @@ -47,7 +47,6 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase { private static final int TEST_BOUNDS_HEIGHT = 1000; OneHandedAnimationController mOneHandedAnimationController; - OneHandedTutorialHandler mTutorialHandler; @Mock private SurfaceControl mMockLeash; @@ -60,8 +59,6 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - - mTutorialHandler = new OneHandedTutorialHandler(mContext, mMainExecutor); mOneHandedAnimationController = new OneHandedAnimationController(mContext); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java index e9c4af12a0d6..b0f52cf656f8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java @@ -59,7 +59,7 @@ public class OneHandedBackgroundPanelOrganizerTest extends OneHandedTestCase { DisplayController mMockDisplayController; @Before - public void setUp() throws Exception { + public void setUp() { MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); mToken = new WindowContainerToken(mMockRealToken); @@ -82,15 +82,6 @@ public class OneHandedBackgroundPanelOrganizerTest extends OneHandedTestCase { } @Test - public void testUnregisterOrganizer() { - mBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash); - mTestableLooper.processAllMessages(); - mBackgroundPanelOrganizer.unregisterOrganizer(); - - assertThat(mBackgroundPanelOrganizer.getBackgroundSurface()).isNull(); - } - - @Test public void testShowBackgroundLayer() { mBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash); mBackgroundPanelOrganizer.showBackgroundPanelLayer(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index f141167178a1..1ad8fd3e7298 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -16,19 +16,24 @@ package com.android.wm.shell.onehanded; +import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED; + import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.om.IOverlayManager; import android.os.Handler; -import android.provider.Settings; import android.testing.AndroidTestingRunner; +import android.util.ArrayMap; import android.view.Display; +import android.view.SurfaceControl; import androidx.test.filters.SmallTest; @@ -37,19 +42,17 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerImpl; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) public class OneHandedControllerTest extends OneHandedTestCase { Display mDisplay; - OneHandedController mOneHandedController; - OneHandedTimeoutHandler mTimeoutHandler; + OneHandedController mSpiedOneHandedController; + OneHandedTimeoutHandler mSpiedTimeoutHandler; @Mock DisplayController mMockDisplayController; @@ -64,8 +67,6 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Mock OneHandedGestureHandler mMockGestureHandler; @Mock - OneHandedTimeoutHandler mMockTimeoutHandler; - @Mock OneHandedUiEventLogger mMockUiEventLogger; @Mock IOverlayManager mMockOverlayManager; @@ -74,14 +75,30 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Mock ShellExecutor mMockShellMainExecutor; @Mock + SurfaceControl mMockLeash; + @Mock Handler mMockShellMainHandler; + final boolean mDefaultEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( + getTestContext().getContentResolver()); + final boolean mDefaultSwipeToNotificationEnabled = + OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + getTestContext().getContentResolver()); + final boolean mDefaultTapAppToExitEnabled = OneHandedSettingsUtil.getSettingsTapsAppToExit( + getTestContext().getContentResolver()); + @Before - public void setUp() throws Exception { + public void setUp() { MockitoAnnotations.initMocks(this); mDisplay = mContext.getDisplay(); - mTimeoutHandler = Mockito.spy(new OneHandedTimeoutHandler(mMockShellMainExecutor)); - OneHandedController oneHandedController = new OneHandedController( + mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor)); + + when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); + when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); + when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash); + + mSpiedOneHandedController = spy(new OneHandedController( mContext, mMockDisplayController, mMockBackgroundOrganizer, @@ -89,16 +106,13 @@ public class OneHandedControllerTest extends OneHandedTestCase { mMockTouchHandler, mMockTutorialHandler, mMockGestureHandler, - mTimeoutHandler, + mSpiedTimeoutHandler, mMockUiEventLogger, mMockOverlayManager, mMockTaskStackListener, mMockShellMainExecutor, - mMockShellMainHandler); - mOneHandedController = Mockito.spy(oneHandedController); - - when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); - when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mMockShellMainHandler) + ); } @Test @@ -113,21 +127,36 @@ public class OneHandedControllerTest extends OneHandedTestCase { } @Test - public void testRegisterOrganizer() { - verify(mMockDisplayAreaOrganizer, atLeastOnce()).registerOrganizer(anyInt()); + public void testNoRegisterAndUnregisterInSameCall() { + if (mDefaultEnabled) { + verify(mMockDisplayAreaOrganizer, never()).unregisterOrganizer(); + } else { + verify(mMockDisplayAreaOrganizer, never()).registerOrganizer(FEATURE_ONE_HANDED); + } } @Test - public void testStartOneHanded() { - mOneHandedController.startOneHanded(); + public void testStartOneHandedShouldTriggerScheduleOffset() { + when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedOneHandedController.setOneHandedEnabled(true); + mSpiedOneHandedController.startOneHanded(); verify(mMockDisplayAreaOrganizer).scheduleOffset(anyInt(), anyInt()); } @Test + public void testStartOneHandedShouldNotTriggerScheduleOffset() { + mSpiedOneHandedController.setOneHandedEnabled(true); + when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(true); + mSpiedOneHandedController.startOneHanded(); + + verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); + } + + @Test public void testStopOneHanded() { when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); - mOneHandedController.stopOneHanded(); + mSpiedOneHandedController.stopOneHanded(); verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); } @@ -141,71 +170,121 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testRegisterTransitionCallback() { - OneHandedTransitionCallback callback = new OneHandedTransitionCallback() {}; - mOneHandedController.registerTransitionCallback(callback); + OneHandedTransitionCallback callback = new OneHandedTransitionCallback() { + }; + mSpiedOneHandedController.registerTransitionCallback(callback); verify(mMockDisplayAreaOrganizer).registerTransitionCallback(callback); } + @Test + public void testStopOneHandedShouldRemoveTimer() { + when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(true); + mSpiedOneHandedController.stopOneHanded(); + + verify(mSpiedTimeoutHandler, atLeastOnce()).removeTimer(); + } @Test - public void testStopOneHanded_shouldRemoveTimer() { - mOneHandedController.stopOneHanded(); + public void testUpdateEnabled() { + mSpiedOneHandedController.setOneHandedEnabled(true); - verify(mTimeoutHandler).removeTimer(); + verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(mDefaultEnabled); + verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled( + mDefaultEnabled || mDefaultSwipeToNotificationEnabled); } @Test - public void testUpdateIsEnabled() { - final boolean enabled = true; - mOneHandedController.setOneHandedEnabled(enabled); + public void testUpdateSwipeToNotification() { + mSpiedOneHandedController.setSwipeToNotificationEnabled(mDefaultSwipeToNotificationEnabled); - verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(enabled); + verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(mDefaultEnabled); + verify(mMockGestureHandler, atLeastOnce()).onOneHandedEnabled( + mDefaultEnabled || mDefaultSwipeToNotificationEnabled); } @Test - public void testUpdateSwipeToNotificationIsEnabled() { - final boolean enabled = true; - mOneHandedController.setSwipeToNotificationEnabled(enabled); + public void testSettingsObserverUpdateTapAppToExit() { + mSpiedOneHandedController.onTaskChangeExitSettingChanged(); + if (mDefaultTapAppToExitEnabled) { + verify(mMockTaskStackListener, atLeastOnce()).addListener(any()); + } else { + verify(mMockTaskStackListener, atLeastOnce()).removeListener(any()); + } + } - verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(enabled); + @Test + public void testSettingsObserverUpdateEnabled() { + mSpiedOneHandedController.onEnabledSettingChanged(); + + verify(mSpiedOneHandedController).setOneHandedEnabled(mDefaultEnabled); } - @Ignore("b/167943723, refactor it and fix it") @Test - public void tesSettingsObserver_updateTapAppToExit() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.TAPS_APP_TO_EXIT, 1); + public void testSettingsObserverUpdateTimeout() { + mSpiedOneHandedController.onTimeoutSettingChanged(); - verify(mOneHandedController).setTaskChangeToExit(true); + verify(mSpiedTimeoutHandler, atLeastOnce()).setTimeout(anyInt()); } - @Ignore("b/167943723, refactor it and fix it") @Test - public void tesSettingsObserver_updateEnabled() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.ONE_HANDED_MODE_ENABLED, 1); + public void testSettingsObserverUpdateSwipeToNotification() { + mSpiedOneHandedController.onSwipeToNotificationEnabledSettingChanged(); + + // Swipe to notification function is opposite with one handed mode function + if (mDefaultSwipeToNotificationEnabled) { + verify(mSpiedOneHandedController).setSwipeToNotificationEnabled( + mDefaultSwipeToNotificationEnabled); + } else { + verify(mSpiedOneHandedController, never()).setSwipeToNotificationEnabled( + mDefaultSwipeToNotificationEnabled); + } + } + + @Test + public void testLockedOneHandedDisabled() { + // Default mLockDisabled is false + assertThat(mSpiedOneHandedController.isLockedDisabled()).isFalse(); + + mSpiedOneHandedController.setOneHandedEnabled(true); + mSpiedOneHandedController.setLockedDisabled(false /* locked */, true /* enabled */); + + // If mOneHandedEnabled == enabled, then keep unlocked + assertThat(mSpiedOneHandedController.isLockedDisabled()).isFalse(); - verify(mOneHandedController).setOneHandedEnabled(true); + // If prefer locked enabled state and 'mOneHandedEnabled == enabled', then unlocked + mSpiedOneHandedController.setLockedDisabled(true /* locked */, true /* enabled */); + + assertThat(mSpiedOneHandedController.isLockedDisabled()).isFalse(); + + // If prefer locked disabled state and 'mOneHandedEnabled != enabled', then locked disabled + mSpiedOneHandedController.setLockedDisabled(true /* locked */, false /* enabled */); + + assertThat(mSpiedOneHandedController.isLockedDisabled()).isTrue(); + + // If prefer unlock disabled state and 'mOneHandedEnabled != enabled', then unlocked + mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */); + + assertThat(mSpiedOneHandedController.isLockedDisabled()).isFalse(); } - @Ignore("b/167943723, refactor it and fix it") @Test - public void tesSettingsObserver_updateTimeout() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.ONE_HANDED_MODE_TIMEOUT, - OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); + public void testKeyguardShowingLockOneHandedDisabled() { + when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedOneHandedController.setOneHandedEnabled(true); + mSpiedOneHandedController.setLockedDisabled(true /* locked */, false /* enabled */); + mSpiedOneHandedController.startOneHanded(); - verify(mMockTimeoutHandler).setTimeout( - OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); + verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); } - @Ignore("b/167943723, refactor it and fix it") @Test - public void tesSettingsObserver_updateSwipeToNotification() { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1); + public void testResetKeyguardShowingLockOneHandedDisabled() { + when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); + mSpiedOneHandedController.setOneHandedEnabled(true); + mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */); + mSpiedOneHandedController.startOneHanded(); - verify(mOneHandedController).setSwipeToNotificationEnabled(true); + verify(mMockDisplayAreaOrganizer).scheduleOffset(anyInt(), anyInt()); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java index 01162b5c0b83..7a826c1be4d3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -37,6 +38,7 @@ import android.testing.TestableLooper; import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; +import android.window.DisplayAreaAppearedInfo; import android.window.DisplayAreaInfo; import android.window.IWindowContainerToken; import android.window.WindowContainerToken; @@ -53,15 +55,19 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; + @SmallTest @RunWith(AndroidTestingRunner.class) public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { + static final int DISPLAYAREA_INFO_COUNT = 3; static final int DISPLAY_WIDTH = 1000; static final int DISPLAY_HEIGHT = 1000; DisplayAreaInfo mDisplayAreaInfo; Display mDisplay; - OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer; + OneHandedDisplayAreaOrganizer mSpiedDisplayAreaOrganizer; OneHandedTutorialHandler mTutorialHandler; OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator; WindowContainerToken mToken; @@ -86,6 +92,8 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { @Mock ShellExecutor mMockShellMainExecutor; + List<DisplayAreaAppearedInfo> mDisplayAreaAppearedInfoList = new ArrayList<>(); + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -112,170 +120,195 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH); when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT); - mDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext, + mSpiedDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext, mMockDisplayController, mMockAnimationController, mTutorialHandler, mMockBackgroundOrganizer, mMockShellMainExecutor)); + + for (int i = 0; i < DISPLAYAREA_INFO_COUNT; i++) { + mDisplayAreaAppearedInfoList.add(getDummyDisplayAreaInfo()); + } + doReturn(mDisplayAreaAppearedInfoList).when(mSpiedDisplayAreaOrganizer).registerOrganizer( + FEATURE_ONE_HANDED); + } + + private DisplayAreaAppearedInfo getDummyDisplayAreaInfo() { + return new DisplayAreaAppearedInfo(mDisplayAreaInfo, mMockLeash); + } + + @Test + public void testRegisterDisplayAreaOrganizer() { + assertThat(mSpiedDisplayAreaOrganizer.registerOrganizer(FEATURE_ONE_HANDED)).isNotNull(); } @Test public void testOnDisplayAreaAppeared() { - mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash); + mDisplayAreaAppearedInfoList.forEach( + (info) -> mSpiedDisplayAreaOrganizer.onDisplayAreaAppeared( + info.getDisplayAreaInfo(), + info.getLeash())); verify(mMockAnimationController, never()).getAnimator(any(), any(), any(), any()); } @Test public void testOnDisplayAreaVanished() { - mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash); - mDisplayAreaOrganizer.onDisplayAreaVanished(mDisplayAreaInfo); + mDisplayAreaAppearedInfoList.forEach( + (info) -> mSpiedDisplayAreaOrganizer.onDisplayAreaAppeared( + info.getDisplayAreaInfo(), + info.getLeash())); + + mDisplayAreaAppearedInfoList.forEach( + (info) -> mSpiedDisplayAreaOrganizer.onDisplayAreaVanished( + info.getDisplayAreaInfo())); - assertThat(mDisplayAreaOrganizer.mDisplayAreaTokenMap).isEmpty(); + verify(mSpiedDisplayAreaOrganizer, times(DISPLAYAREA_INFO_COUNT)).onDisplayAreaVanished( + any()); } @Test public void testRotation_portrait_0_to_landscape_90() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 0 -> 90 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_portrait_0_to_seascape_270() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 0 -> 270 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_portrait_180_to_landscape_90() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 180 -> 90 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_portrait_180_to_seascape_270() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 180 -> 270 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_landscape_90_to_portrait_0() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 90 -> 0 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_landscape_90_to_portrait_180() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 90 -> 180 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_Seascape_270_to_portrait_0() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 270 -> 0 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_seascape_90_to_portrait_180() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 270 -> 180 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_portrait_0_to_portrait_0() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 0 -> 0 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_portrait_0_to_portrait_180() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 0 -> 180 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_portrait_180_to_portrait_180() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 180 -> 180 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_portrait_180_to_portrait_0() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 180 -> 0 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_landscape_90_to_landscape_90() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 90 -> 90 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_landscape_90_to_seascape_270() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 90 -> 270 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_seascape_270_to_seascape_270() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 270 -> 270 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } @Test public void testRotation_seascape_90_to_landscape_90() { when(mMockLeash.isValid()).thenReturn(false); // Rotate 270 -> 90 - mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90, + mSpiedDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90, mMockWindowContainerTransaction); - verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); + verify(mSpiedDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt()); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java index e5f2ff717e37..b275b701f87a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedGestureHandlerTest.java @@ -18,10 +18,8 @@ package com.android.wm.shell.onehanded; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; - import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; +import android.view.ViewConfiguration; import androidx.test.filters.SmallTest; @@ -29,7 +27,6 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -38,7 +35,6 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) public class OneHandedGestureHandlerTest extends OneHandedTestCase { - OneHandedTutorialHandler mTutorialHandler; OneHandedGestureHandler mGestureHandler; @Mock DisplayController mMockDisplayController; @@ -46,11 +42,10 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { ShellExecutor mMockShellMainExecutor; @Before - public void setUp() throws Exception { + public void setUp() { MockitoAnnotations.initMocks(this); - mTutorialHandler = new OneHandedTutorialHandler(mContext, mMockShellMainExecutor); mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController, - mMockShellMainExecutor); + ViewConfiguration.get(mTestContext), mMockShellMainExecutor); } @Test @@ -68,16 +63,6 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { assertThat(mGestureHandler.mGestureEventCallback).isEqualTo(callback); } - @Ignore("b/167943723, refactor it and fix it") - @Test - public void testReceiveNewConfig_whenThreeButtonModeEnabled() { - mGestureHandler.onOneHandedEnabled(true); - mGestureHandler.onThreeButtonModeEnabled(true); - - assertThat(mGestureHandler.mInputMonitor).isNotNull(); - assertThat(mGestureHandler.mInputEventReceiver).isNotNull(); - } - @Test public void testOneHandedDisabled_shouldDisposeInputChannel() { mGestureHandler.onOneHandedEnabled(false); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java index f8c9d535ba94..61643d86c8d9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedSettingsUtilTest.java @@ -28,7 +28,6 @@ import android.database.ContentObserver; import android.net.Uri; import android.provider.Settings; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import androidx.test.filters.SmallTest; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java index 73a95345e1c9..32a188d02cf0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java @@ -19,88 +19,47 @@ package com.android.wm.shell.onehanded; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.wm.shell.onehanded.OneHandedController.SUPPORT_ONE_HANDED_MODE; -import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS; import static org.junit.Assume.assumeTrue; import android.content.Context; import android.hardware.display.DisplayManager; import android.os.SystemProperties; -import android.provider.Settings; +import android.testing.TestableContext; import androidx.test.platform.app.InstrumentationRegistry; -import org.junit.After; import org.junit.Before; +import org.junit.Rule; +import org.mockito.Answers; +import org.mockito.Mock; /** * Base class that does One Handed specific setup. */ public abstract class OneHandedTestCase { - static boolean sOrigEnabled; - static boolean sOrigTapsAppToExitEnabled; - static int sOrigTimeout; - static boolean sOrigSwipeToNotification; - + @Mock(answer = Answers.RETURNS_DEEP_STUBS) protected Context mContext; + @Rule + public TestableContext mTestContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + @Before - public void setupSettings() { + public void setUpContext() { assumeTrue(SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)); - final Context testContext = - InstrumentationRegistry.getInstrumentation().getTargetContext(); - final DisplayManager dm = testContext.getSystemService(DisplayManager.class); - mContext = testContext.createDisplayContext(dm.getDisplay(DEFAULT_DISPLAY)); - - InstrumentationRegistry - .getInstrumentation() - .getUiAutomation() - .adoptShellPermissionIdentity(); - - sOrigEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( - getContext().getContentResolver()); - sOrigTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout( - getContext().getContentResolver()); - sOrigTapsAppToExitEnabled = OneHandedSettingsUtil.getSettingsTapsAppToExit( - getContext().getContentResolver()); - sOrigSwipeToNotification = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( - getContext().getContentResolver()); - Settings.Secure.putInt(getContext().getContentResolver(), - Settings.Secure.ONE_HANDED_MODE_ENABLED, 1); - Settings.Secure.putInt(getContext().getContentResolver(), - Settings.Secure.ONE_HANDED_MODE_TIMEOUT, ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); - Settings.Secure.putInt(getContext().getContentResolver(), - Settings.Secure.TAPS_APP_TO_EXIT, 1); - Settings.Secure.putInt(getContext().getContentResolver(), - Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1); + final DisplayManager dm = getTestContext().getSystemService(DisplayManager.class); + mContext = getTestContext().createDisplayContext(dm.getDisplay(DEFAULT_DISPLAY)); } - @After - public void restoreSettings() { - if (mContext == null) { - // Return early if one-handed mode is not supported - return; - } - - Settings.Secure.putInt(getContext().getContentResolver(), - Settings.Secure.ONE_HANDED_MODE_ENABLED, sOrigEnabled ? 1 : 0); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.ONE_HANDED_MODE_TIMEOUT, sOrigTimeout); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.TAPS_APP_TO_EXIT, sOrigTapsAppToExitEnabled ? 1 : 0); - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, - sOrigSwipeToNotification ? 1 : 0); - - InstrumentationRegistry - .getInstrumentation() - .getUiAutomation() - .dropShellPermissionIdentity(); + /** return testable context */ + protected TestableContext getTestContext() { + return mTestContext; } + /** return display context */ protected Context getContext() { return mContext; } } - diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java index bbe8891817d6..98f240a209a6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java @@ -27,24 +27,18 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.verify; -import android.os.Looper; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.wm.shell.TestShellExecutor; -import com.android.wm.shell.common.ShellExecutor; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.util.ArrayList; - @SmallTest @RunWith(AndroidTestingRunner.class) public class OneHandedTimeoutHandlerTest extends OneHandedTestCase { @@ -52,7 +46,7 @@ public class OneHandedTimeoutHandlerTest extends OneHandedTestCase { private TestShellExecutor mMainExecutor; @Before - public void setUp() throws Exception { + public void setUp() { MockitoAnnotations.initMocks(this); mMainExecutor = new TestShellExecutor(); mTimeoutHandler = Mockito.spy(new OneHandedTimeoutHandler(mMainExecutor)); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java index d3b02caf8b65..8660e0e9f7f6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTouchHandlerTest.java @@ -18,8 +18,9 @@ package com.android.wm.shell.onehanded; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.spy; + import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import androidx.test.filters.SmallTest; @@ -35,18 +36,20 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) public class OneHandedTouchHandlerTest extends OneHandedTestCase { - private OneHandedTouchHandler mTouchHandler; - - @Mock - private OneHandedTimeoutHandler mTimeoutHandler; + boolean mIsEventCallback = false; + private OneHandedTouchHandler mTouchHandler; + private OneHandedTimeoutHandler mSpiedTimeoutHandler; + private OneHandedTouchHandler.OneHandedTouchEventCallback mTouchEventCallback = + () -> mIsEventCallback = true; @Mock - private ShellExecutor mMainExecutor; + private ShellExecutor mMockShellMainExecutor; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mTouchHandler = new OneHandedTouchHandler(mTimeoutHandler, mMainExecutor); + mSpiedTimeoutHandler = spy(new OneHandedTimeoutHandler(mMockShellMainExecutor)); + mTouchHandler = new OneHandedTouchHandler(mSpiedTimeoutHandler, mMockShellMainExecutor); } @Test @@ -55,7 +58,7 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { }; mTouchHandler.registerTouchEventListener(callback); - assertThat(mTouchHandler.mTouchEventCallback).isEqualTo(callback); + assertThat(mIsEventCallback).isFalse(); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java index c3e6bf376bda..024cf7ffc1f3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java @@ -17,10 +17,12 @@ package com.android.wm.shell.onehanded; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.om.IOverlayManager; import android.os.Handler; import android.testing.AndroidTestingRunner; +import android.util.ArrayMap; import androidx.test.filters.SmallTest; @@ -37,12 +39,15 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) public class OneHandedTutorialHandlerTest extends OneHandedTestCase { - @Mock - OneHandedTouchHandler mTouchHandler; - OneHandedTutorialHandler mTutorialHandler; - OneHandedGestureHandler mGestureHandler; OneHandedTimeoutHandler mTimeoutHandler; OneHandedController mOneHandedController; + + @Mock + OneHandedGestureHandler mMockGestureHandler; + @Mock + OneHandedTouchHandler mMockTouchHandler; + @Mock + OneHandedTutorialHandler mMockTutorialHandler; @Mock DisplayController mMockDisplayController; @Mock @@ -64,18 +69,17 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mTutorialHandler = new OneHandedTutorialHandler(mContext, mMockShellMainExecutor); mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor); - mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController, - mMockShellMainExecutor); + + when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); mOneHandedController = new OneHandedController( getContext(), mMockDisplayController, mMockBackgroundOrganizer, mMockDisplayAreaOrganizer, - mTouchHandler, - mTutorialHandler, - mGestureHandler, + mMockTouchHandler, + mMockTutorialHandler, + mMockGestureHandler, mTimeoutHandler, mMockUiEventLogger, mMockOverlayManager, @@ -86,6 +90,6 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { @Test public void testRegisterForDisplayAreaOrganizer() { - verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mTutorialHandler); + verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockTutorialHandler); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java index 3147dab1a0f8..63b94139dd9c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java @@ -16,6 +16,7 @@ package com.android.wm.shell.pip; +import static android.util.RotationUtils.rotateBounds; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_90; @@ -37,7 +38,6 @@ import android.view.SurfaceControl; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.common.DisplayLayout; import org.junit.Before; import org.junit.Test; @@ -141,7 +141,7 @@ public class PipAnimationControllerTest extends ShellTestCase { // Apply fraction 1 to compute the end value. animator.applySurfaceControlTransaction(mLeash, new DummySurfaceControlTx(), 1); final Rect rotatedEndBounds = new Rect(endBounds); - DisplayLayout.rotateBounds(rotatedEndBounds, endBounds, ROTATION_90); + rotateBounds(rotatedEndBounds, endBounds, ROTATION_90); assertEquals("Expect 90 degree rotated bounds", rotatedEndBounds, animator.mCurrentValue); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index cfe84639d24e..f2b4e9761226 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -124,7 +124,7 @@ public class PipControllerTest extends ShellTestCase { final ComponentName component1 = new ComponentName(mContext, "component1"); when(mMockPipBoundsState.getLastPipComponentName()).thenReturn(component1); - mPipController.mPinnedStackListener.onActivityHidden(component1); + mPipController.mPinnedTaskListener.onActivityHidden(component1); verify(mMockPipBoundsState).setLastPipComponentName(null); } @@ -135,7 +135,7 @@ public class PipControllerTest extends ShellTestCase { final ComponentName component2 = new ComponentName(mContext, "component2"); when(mMockPipBoundsState.getLastPipComponentName()).thenReturn(component1); - mPipController.mPinnedStackListener.onActivityHidden(component2); + mPipController.mPinnedTaskListener.onActivityHidden(component2); verify(mMockPipBoundsState, never()).setLastPipComponentName(null); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java new file mode 100644 index 000000000000..9845d4650d20 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java @@ -0,0 +1,85 @@ +/* + * 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.wm.shell.sizecompatui; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; + +import android.content.res.Configuration; +import android.os.IBinder; +import android.testing.AndroidTestingRunner; +import android.view.LayoutInflater; +import android.widget.Button; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.R; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.SyncTransactionQueue; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link SizeCompatHintPopup}. + * + * Build/Install/Run: + * atest WMShellUnitTests:SizeCompatHintPopupTest + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class SizeCompatHintPopupTest extends ShellTestCase { + + @Mock private SyncTransactionQueue mSyncTransactionQueue; + @Mock private IBinder mActivityToken; + @Mock private ShellTaskOrganizer.TaskListener mTaskListener; + @Mock private DisplayLayout mDisplayLayout; + + private SizeCompatUILayout mLayout; + private SizeCompatHintPopup mHint; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + final int taskId = 1; + mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mContext, new Configuration(), + taskId, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/); + mHint = (SizeCompatHintPopup) + LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null); + mHint.inject(mLayout); + + spyOn(mLayout); + } + + @Test + public void testOnClick() { + doNothing().when(mLayout).dismissHint(); + + final Button button = mHint.findViewById(R.id.got_it); + button.performClick(); + + verify(mLayout).dismissHint(); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java index d9086a6ccdc1..5a43925a5677 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java @@ -19,13 +19,13 @@ package com.android.wm.shell.sizecompatui; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.content.res.Configuration; import android.os.IBinder; import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; +import android.widget.ImageButton; import androidx.test.filters.SmallTest; @@ -71,45 +71,25 @@ public class SizeCompatRestartButtonTest extends ShellTestCase { mButton.inject(mLayout); spyOn(mLayout); - spyOn(mButton); - doNothing().when(mButton).showHint(); } @Test public void testOnClick() { doNothing().when(mLayout).onRestartButtonClicked(); - mButton.onClick(mButton); + final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button); + button.performClick(); verify(mLayout).onRestartButtonClicked(); } @Test public void testOnLongClick() { - verify(mButton, never()).showHint(); + doNothing().when(mLayout).onRestartButtonLongClicked(); - mButton.onLongClick(mButton); + final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button); + button.performLongClick(); - verify(mButton).showHint(); - } - - @Test - public void testOnAttachedToWindow_showHint() { - mLayout.mShouldShowHint = false; - mButton.onAttachedToWindow(); - - verify(mButton, never()).showHint(); - - mLayout.mShouldShowHint = true; - mButton.onAttachedToWindow(); - - verify(mButton).showHint(); - } - - @Test - public void testRemove() { - mButton.remove(); - - verify(mButton).dismissHint(); + verify(mLayout).onRestartButtonLongClicked(); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java index 236db44bdce0..f33cfe86224f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java @@ -18,6 +18,7 @@ package com.android.wm.shell.sizecompatui; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; @@ -27,6 +28,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.ActivityClient; @@ -68,6 +70,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase { @Mock private ShellTaskOrganizer.TaskListener mTaskListener; @Mock private DisplayLayout mDisplayLayout; @Mock private SizeCompatRestartButton mButton; + @Mock private SizeCompatHintPopup mHint; private Configuration mTaskConfig; private SizeCompatUILayout mLayout; @@ -81,8 +84,13 @@ public class SizeCompatUILayoutTest extends ShellTestCase { TASK_ID, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/); spyOn(mLayout); - spyOn(mLayout.mWindowManager); - doReturn(mButton).when(mLayout.mWindowManager).createSizeCompatUI(); + spyOn(mLayout.mButtonWindowManager); + doReturn(mButton).when(mLayout.mButtonWindowManager).createSizeCompatButton(); + + final SizeCompatUIWindowManager hintWindowManager = mLayout.createHintWindowManager(); + spyOn(hintWindowManager); + doReturn(mHint).when(hintWindowManager).createSizeCompatHint(); + doReturn(hintWindowManager).when(mLayout).createHintWindowManager(); } @Test @@ -90,24 +98,45 @@ public class SizeCompatUILayoutTest extends ShellTestCase { // Not create button if IME is showing. mLayout.createSizeCompatButton(true /* isImeShowing */); - verify(mLayout.mWindowManager, never()).createSizeCompatUI(); + verify(mLayout.mButtonWindowManager, never()).createSizeCompatButton(); assertNull(mLayout.mButton); + assertNull(mLayout.mHintWindowManager); + assertNull(mLayout.mHint); + // Not create hint popup. + mLayout.mShouldShowHint = false; mLayout.createSizeCompatButton(false /* isImeShowing */); - verify(mLayout.mWindowManager).createSizeCompatUI(); + verify(mLayout.mButtonWindowManager).createSizeCompatButton(); assertNotNull(mLayout.mButton); + assertNull(mLayout.mHintWindowManager); + assertNull(mLayout.mHint); + + // Create hint popup. + mLayout.release(); + mLayout.mShouldShowHint = true; + mLayout.createSizeCompatButton(false /* isImeShowing */); + + verify(mLayout.mButtonWindowManager, times(2)).createSizeCompatButton(); + assertNotNull(mLayout.mButton); + assertNotNull(mLayout.mHintWindowManager); + verify(mLayout.mHintWindowManager).createSizeCompatHint(); + assertNotNull(mLayout.mHint); + assertFalse(mLayout.mShouldShowHint); } @Test public void testRelease() { mLayout.createSizeCompatButton(false /* isImeShowing */); + final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager; mLayout.release(); assertNull(mLayout.mButton); - verify(mButton).remove(); - verify(mLayout.mWindowManager).release(); + assertNull(mLayout.mHint); + verify(hintWindowManager).release(); + assertNull(mLayout.mHintWindowManager); + verify(mLayout.mButtonWindowManager).release(); } @Test @@ -119,7 +148,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase { mLayout.updateSizeCompatInfo(mTaskConfig, mActivityToken, mTaskListener, false /* isImeShowing */); - verify(mLayout, never()).updateSurfacePosition(); + verify(mLayout, never()).updateButtonSurfacePosition(); verify(mLayout, never()).release(); verify(mLayout, never()).createSizeCompatButton(anyBoolean()); @@ -140,7 +169,8 @@ public class SizeCompatUILayoutTest extends ShellTestCase { mLayout.updateSizeCompatInfo(newTaskConfiguration, mActivityToken, newTaskListener, false /* isImeShowing */); - verify(mLayout).updateSurfacePosition(); + verify(mLayout).updateButtonSurfacePosition(); + verify(mLayout).updateHintSurfacePosition(); } @Test @@ -152,14 +182,16 @@ public class SizeCompatUILayoutTest extends ShellTestCase { mContext.getResources(), false, false); mLayout.updateDisplayLayout(displayLayout1); - verify(mLayout).updateSurfacePosition(); + verify(mLayout).updateButtonSurfacePosition(); + verify(mLayout).updateHintSurfacePosition(); // No update if the display bounds is the same. clearInvocations(mLayout); final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo, mContext.getResources(), false, false); mLayout.updateDisplayLayout(displayLayout2); - verify(mLayout, never()).updateSurfacePosition(); + verify(mLayout, never()).updateButtonSurfacePosition(); + verify(mLayout, never()).updateHintSurfacePosition(); } @Test @@ -203,4 +235,29 @@ public class SizeCompatUILayoutTest extends ShellTestCase { verify(ActivityClient.getInstance()).restartActivityProcessIfVisible(mActivityToken); } + + @Test + public void testOnRestartButtonLongClicked_showHint() { + mLayout.dismissHint(); + + assertNull(mLayout.mHint); + + mLayout.onRestartButtonLongClicked(); + + assertNotNull(mLayout.mHint); + } + + @Test + public void testDismissHint() { + mLayout.onRestartButtonLongClicked(); + final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager; + assertNotNull(mLayout.mHint); + assertNotNull(hintWindowManager); + + mLayout.dismissHint(); + + assertNull(mLayout.mHint); + assertNull(mLayout.mHintWindowManager); + verify(hintWindowManager).release(); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index d2d18129d071..74753aac4a24 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -41,6 +41,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; +import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.SyncTransactionQueue; import org.junit.Before; @@ -58,13 +59,14 @@ public class StageCoordinatorTests extends ShellTestCase { @Mock private RootTaskDisplayAreaOrganizer mRootTDAOrganizer; @Mock private MainStage mMainStage; @Mock private SideStage mSideStage; + @Mock private DisplayImeController mDisplayImeController; private StageCoordinator mStageCoordinator; @Before public void setup() { MockitoAnnotations.initMocks(this); mStageCoordinator = new TestStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, - mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage); + mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, mDisplayImeController); } @Test @@ -94,9 +96,9 @@ public class StageCoordinatorTests extends ShellTestCase { TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, - MainStage mainStage, SideStage sideStage) { + MainStage mainStage, SideStage sideStage, DisplayImeController imeController) { super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage, - sideStage); + sideStage, imeController); // Prepare default TaskDisplayArea for testing. mDisplayAreaInfo = new DisplayAreaInfo( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index 5eca3e75a7b8..926108c41e5e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -41,6 +41,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.ActivityManager.RunningTaskInfo; +import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -58,6 +59,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; @@ -78,6 +80,8 @@ public class ShellTransitionTests { private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class); private final TransactionPool mTransactionPool = mock(TransactionPool.class); + private final Context mContext = + InstrumentationRegistry.getInstrumentation().getTargetContext(); private final TestShellExecutor mMainExecutor = new TestShellExecutor(); private final ShellExecutor mAnimExecutor = new TestShellExecutor(); private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler(); @@ -90,8 +94,8 @@ public class ShellTransitionTests { @Test public void testBasicTransitionFlow() { - Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, - mAnimExecutor); + Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, + mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); IBinder transitToken = new Binder(); @@ -109,8 +113,8 @@ public class ShellTransitionTests { @Test public void testNonDefaultHandler() { - Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, - mAnimExecutor); + Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, + mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); @@ -188,8 +192,8 @@ public class ShellTransitionTests { @Test public void testRequestRemoteTransition() { - Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, - mAnimExecutor); + Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, + mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); final boolean[] remoteCalled = new boolean[]{false}; @@ -255,8 +259,8 @@ public class ShellTransitionTests { @Test public void testRegisteredRemoteTransition() { - Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, - mAnimExecutor); + Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, + mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); final boolean[] remoteCalled = new boolean[]{false}; diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 4b4284a0c745..aba0f1b47673 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -14,6 +14,23 @@ // libandroidfw is partially built for the host (used by obbtool, aapt, and others) +package { + default_applicable_licenses: ["frameworks_base_libs_androidfw_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_libs_androidfw_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_defaults { name: "libandroidfw_defaults", cflags: [ diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING index 777aa0b429e5..766714cd7694 100644 --- a/libs/androidfw/TEST_MAPPING +++ b/libs/androidfw/TEST_MAPPING @@ -1,10 +1,6 @@ { "presubmit": [ { - "name": "libandroidfw_tests", - "host": true - }, - { "name": "CtsResourcesLoaderTests" } ] diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp index b36ff0968ba3..3035a79f668d 100644 --- a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp +++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp @@ -1,3 +1,12 @@ +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_libs_androidfw_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_libs_androidfw_license"], +} + cc_fuzz { name: "cursorwindow_fuzzer", srcs: [ diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp index 77ef8dfb9725..b511244c4a30 100644 --- a/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp +++ b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp @@ -12,6 +12,15 @@ // 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_libs_androidfw_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_libs_androidfw_license"], +} + cc_fuzz { name: "resourcefile_fuzzer", srcs: [ diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp index 3a99d41448f2..f166fdeafa41 100644 --- a/libs/hostgraphics/Android.bp +++ b/libs/hostgraphics/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_library_host_static { name: "libhostgraphics", diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 678b0ad3924d..77ceda91898e 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -1,3 +1,33 @@ +package { + default_applicable_licenses: ["frameworks_base_libs_hwui_license"], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// See: http://go/android-license-faq +license { + name: "frameworks_base_libs_hwui_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "SPDX-license-identifier-BSD", + ], + license_text: [ + "NOTICE", + ], +} + cc_defaults { name: "hwui_defaults", defaults: [ diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index b39f4f20dc0d..0bf948006ea0 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -249,5 +249,20 @@ void DamageAccumulator::finish(SkRect* totalDirty) { mHead->pendingDirty.setEmpty(); } +const StretchEffect* DamageAccumulator::findNearestStretchEffect() const { + DirtyStack* frame = mHead; + while (frame->prev != frame) { + frame = frame->prev; + if (frame->type == TransformRenderNode) { + const auto& effect = + frame->renderNode->properties().layerProperties().getStretchEffect(); + if (!effect.isEmpty()) { + return &effect; + } + } + } + return nullptr; +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h index 2faa9d012d66..89ee0e34055b 100644 --- a/libs/hwui/DamageAccumulator.h +++ b/libs/hwui/DamageAccumulator.h @@ -35,6 +35,7 @@ namespace uirenderer { struct DirtyStack; class RenderNode; class Matrix4; +class StretchEffect; class DamageAccumulator { PREVENT_COPY_AND_ASSIGN(DamageAccumulator); @@ -62,6 +63,8 @@ public: void finish(SkRect* totalDirty); + const StretchEffect* findNearestStretchEffect() const; + private: void pushCommon(); void applyMatrix4Transform(DirtyStack* frame); diff --git a/libs/hwui/FrameInfo.cpp b/libs/hwui/FrameInfo.cpp index ce9b2889cd9e..2448cc904104 100644 --- a/libs/hwui/FrameInfo.cpp +++ b/libs/hwui/FrameInfo.cpp @@ -20,13 +20,12 @@ namespace android { namespace uirenderer { -const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames = { +const std::array FrameInfoNames{ "Flags", "FrameTimelineVsyncId", "IntendedVsync", "Vsync", - "OldestInputEvent", - "NewestInputEvent", + "InputEventId", "HandleInputStart", "AnimationStart", "PerformTraversalsStart", @@ -40,6 +39,8 @@ const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> Fram "DequeueBufferDuration", "QueueBufferDuration", "GpuCompleted", + "SwapBuffersCompleted", + "DisplayPresentTime", }; static_assert(static_cast<int>(FrameInfoIndex::NumIndexes) == 20, diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index 45a367f525da..912d04c5d87d 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -28,15 +28,14 @@ namespace android { namespace uirenderer { -#define UI_THREAD_FRAME_INFO_SIZE 11 +static constexpr size_t UI_THREAD_FRAME_INFO_SIZE = 10; enum class FrameInfoIndex { Flags = 0, FrameTimelineVsyncId, IntendedVsync, Vsync, - OldestInputEvent, - NewestInputEvent, + InputEventId, HandleInputStart, AnimationStart, PerformTraversalsStart, @@ -56,13 +55,14 @@ enum class FrameInfoIndex { GpuCompleted, SwapBuffersCompleted, + DisplayPresentTime, // Must be the last value! // Also must be kept in sync with FrameMetrics.java#FRAME_STATS_COUNT NumIndexes }; -extern const std::array<std::string, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames; +extern const std::array<const char*, static_cast<int>(FrameInfoIndex::NumIndexes)> FrameInfoNames; namespace FrameInfoFlags { enum { diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 4a2e30dd38f2..4eefe921fbe9 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -112,7 +112,7 @@ void JankTracker::finishFrame(const FrameInfo& frame) { std::lock_guard lock(mDataMutex); // Fast-path for jank-free frames - int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted); + int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::SwapBuffersCompleted); if (mDequeueTimeForgiveness && frame[FrameInfoIndex::DequeueBufferDuration] > 500_us) { nsecs_t expectedDequeueDuration = mDequeueTimeForgiveness + frame[FrameInfoIndex::Vsync] - frame[FrameInfoIndex::IssueDrawCommandsStart]; @@ -219,7 +219,7 @@ void JankTracker::dumpData(int fd, const ProfileDataDescription* description, void JankTracker::dumpFrames(int fd) { dprintf(fd, "\n\n---PROFILEDATA---\n"); for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) { - dprintf(fd, "%s", FrameInfoNames[i].c_str()); + dprintf(fd, "%s", FrameInfoNames[i]); dprintf(fd, ","); } for (size_t i = 0; i < mFrames.size(); i++) { @@ -236,7 +236,6 @@ void JankTracker::dumpFrames(int fd) { } void JankTracker::reset() { - std::lock_guard lock(mDataMutex); mFrames.clear(); mData->reset(); (*mGlobalData)->reset(); diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 64b8b711f0a8..170f73120414 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -384,10 +384,11 @@ struct DrawImageLattice final : Op { struct DrawTextBlob final : Op { static const auto kType = Type::DrawTextBlob; DrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) - : blob(sk_ref_sp(blob)), x(x), y(y), paint(paint) {} + : blob(sk_ref_sp(blob)), x(x), y(y), paint(paint), drawTextBlobMode(gDrawTextBlobMode) {} sk_sp<const SkTextBlob> blob; SkScalar x, y; SkPaint paint; + DrawTextBlobMode drawTextBlobMode; void draw(SkCanvas* c, const SkMatrix&) const { c->drawTextBlob(blob.get(), x, y, paint); } }; @@ -832,6 +833,24 @@ constexpr color_transform_fn colorTransformForOp() { } } +template<> +constexpr color_transform_fn colorTransformForOp<DrawTextBlob>() { + return [](const void *opRaw, ColorTransform transform) { + const DrawTextBlob *op = reinterpret_cast<const DrawTextBlob*>(opRaw); + switch (op->drawTextBlobMode) { + case DrawTextBlobMode::HctOutline: + const_cast<SkPaint&>(op->paint).setColor(SK_ColorBLACK); + break; + case DrawTextBlobMode::HctInner: + const_cast<SkPaint&>(op->paint).setColor(SK_ColorWHITE); + break; + default: + transformPaint(transform, const_cast<SkPaint*>(&(op->paint))); + break; + } + }; +} + #define X(T) colorTransformForOp<T>(), static const color_transform_fn color_transform_fns[] = { #include "DisplayListOps.in" diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 44f54eef458f..f5b2675c7fe6 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -226,6 +226,9 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu if (!mProperties.getAllowForceDark()) { info.disableForceDark++; } + if (!mProperties.layerProperties().getStretchEffect().isEmpty()) { + info.stretchEffectCount++; + } uint32_t animatorDirtyMask = 0; if (CC_LIKELY(info.runAnimations)) { @@ -267,6 +270,9 @@ void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool fu if (!mProperties.getAllowForceDark()) { info.disableForceDark--; } + if (!mProperties.layerProperties().getStretchEffect().isEmpty()) { + info.stretchEffectCount--; + } info.damageAccumulator->popTransform(); } diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 8fba9cf21df1..0589f136b666 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -70,6 +70,7 @@ LayerProperties& LayerProperties::operator=(const LayerProperties& other) { setXferMode(other.xferMode()); setColorFilter(other.getColorFilter()); setImageFilter(other.getImageFilter()); + mStretchEffect = other.mStretchEffect; return *this; } diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 1fddac4cd05d..28d2b4cec0e1 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -188,7 +188,7 @@ void SkiaCanvas::restoreUnclippedLayer(int restoreCount, const SkPaint& paint) { } if (mCanvas->getSaveCount() == restoreCount + 1) { - SkCanvasPriv::DrawBehind(mCanvas, *filterPaint(paint)); + SkCanvasPriv::DrawBehind(mCanvas, filterPaint(paint)); this->restore(); } } @@ -431,15 +431,14 @@ void SkiaCanvas::drawColor(int color, SkBlendMode mode) { mCanvas->drawColor(color, mode); } -SkiaCanvas::PaintCoW&& SkiaCanvas::filterPaint(PaintCoW&& paint) const { +void SkiaCanvas::onFilterPaint(SkPaint& paint) { if (mPaintFilter) { - mPaintFilter->filter(&paint.writeable()); + mPaintFilter->filter(&paint); } - return std::move(paint); } void SkiaCanvas::drawPaint(const SkPaint& paint) { - mCanvas->drawPaint(*filterPaint(paint)); + mCanvas->drawPaint(filterPaint(paint)); } // ---------------------------------------------------------------------------- @@ -457,13 +456,11 @@ void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint, points += 2; } - apply_looper(&paint, [&](const SkPaint& p) { - mCanvas->drawPoints(mode, count, pts.get(), p); - }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPoints(mode, count, pts.get(), p); }); } void SkiaCanvas::drawPoint(float x, float y, const Paint& paint) { - apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawPoint(x, y, p); }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPoint(x, y, p); }); } void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint) { @@ -472,9 +469,8 @@ void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint) void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY, const Paint& paint) { - apply_looper(&paint, [&](const SkPaint& p) { - mCanvas->drawLine(startX, startY, stopX, stopY, p); - }); + applyLooper(&paint, + [&](const SkPaint& p) { mCanvas->drawLine(startX, startY, stopX, stopY, p); }); } void SkiaCanvas::drawLines(const float* points, int count, const Paint& paint) { @@ -484,46 +480,44 @@ void SkiaCanvas::drawLines(const float* points, int count, const Paint& paint) { void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - apply_looper(&paint, [&](const SkPaint& p) { + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawRect({left, top, right, bottom}, p); }); } void SkiaCanvas::drawRegion(const SkRegion& region, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawRegion(region, p); }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawRegion(region, p); }); } void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); - apply_looper(&paint, [&](const SkPaint& p) { - mCanvas->drawRoundRect(rect, rx, ry, p); - }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawRoundRect(rect, rx, ry, p); }); } void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner, const Paint& paint) { - apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawDRRect(outer, inner, p); }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawDRRect(outer, inner, p); }); } void SkiaCanvas::drawCircle(float x, float y, float radius, const Paint& paint) { if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return; - apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawCircle(x, y, radius, p); }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawCircle(x, y, radius, p); }); } void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); - apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawOval(oval, p); }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawOval(oval, p); }); } void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, bool useCenter, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect arc = SkRect::MakeLTRB(left, top, right, bottom); - apply_looper(&paint, [&](const SkPaint& p) { + applyLooper(&paint, [&](const SkPaint& p) { if (fabs(sweepAngle) >= 360.0f) { mCanvas->drawOval(arc, p); } else { @@ -537,13 +531,11 @@ void SkiaCanvas::drawPath(const SkPath& path, const Paint& paint) { if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) { return; } - apply_looper(&paint, [&](const SkPaint& p) { mCanvas->drawPath(path, p); }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawPath(path, p); }); } void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const Paint& paint) { - apply_looper(&paint, [&](const SkPaint& p) { - mCanvas->drawVertices(vertices, mode, p); - }); + applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawVertices(vertices, mode, p); }); } // ---------------------------------------------------------------------------- @@ -552,7 +544,7 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) { auto image = bitmap.makeImage(); - apply_looper(paint, [&](const SkPaint& p) { + applyLooper(paint, [&](const SkPaint& p) { auto sampling = SkSamplingOptions(p.getFilterQuality()); mCanvas->drawImage(image, left, top, sampling, &p); }); @@ -562,7 +554,7 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* auto image = bitmap.makeImage(); SkAutoCanvasRestore acr(mCanvas, true); mCanvas->concat(matrix); - apply_looper(paint, [&](const SkPaint& p) { + applyLooper(paint, [&](const SkPaint& p) { auto sampling = SkSamplingOptions(p.getFilterQuality()); mCanvas->drawImage(image, 0, 0, sampling, &p); }); @@ -575,7 +567,7 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float s SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - apply_looper(paint, [&](const SkPaint& p) { + applyLooper(paint, [&](const SkPaint& p) { auto sampling = SkSamplingOptions(p.getFilterQuality()); mCanvas->drawImageRect(image, srcRect, dstRect, sampling, &p, SkCanvas::kFast_SrcRectConstraint); @@ -672,11 +664,11 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, pnt.setShader(image->makeShader(sampling)); auto v = builder.detach(); - apply_looper(&pnt, [&](const SkPaint& p) { + applyLooper(&pnt, [&](const SkPaint& p) { SkPaint copy(p); auto s = SkSamplingOptions(p.getFilterQuality()); if (s != sampling) { - // apply_looper changed the quality? + // applyLooper changed the quality? copy.setShader(image->makeShader(s)); } mCanvas->drawVertices(v, SkBlendMode::kModulate, copy); @@ -707,7 +699,7 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa lattice.fBounds = nullptr; SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); auto image = bitmap.makeImage(); - apply_looper(paint, [&](const SkPaint& p) { + applyLooper(paint, [&](const SkPaint& p) { auto filter = SkSamplingOptions(p.getFilterQuality()).filter; mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p); }); @@ -746,9 +738,7 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& pai sk_sp<SkTextBlob> textBlob(builder.make()); - apply_looper(&paintCopy, [&](const SkPaint& p) { - mCanvas->drawTextBlob(textBlob, 0, 0, p); - }); + applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); }); drawTextDecorations(x, y, totalAdvance, paintCopy); } @@ -788,9 +778,7 @@ void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, sk_sp<SkTextBlob> textBlob(builder.make()); - apply_looper(&paintCopy, [&](const SkPaint& p) { - mCanvas->drawTextBlob(textBlob, 0, 0, p); - }); + applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); }); } // ---------------------------------------------------------------------------- diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index eac3f2217bd8..9ab2b106dbfa 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -169,53 +169,24 @@ protected: const Paint& paint, const SkPath& path, size_t start, size_t end) override; - /** This class acts as a copy on write SkPaint. - * - * Initially this will be the SkPaint passed to the contructor. - * The first time writable() is called this will become a copy of the - * initial SkPaint (or a default SkPaint if nullptr). - */ - struct PaintCoW { - PaintCoW(const SkPaint& that) : mPtr(&that) {} - PaintCoW(const SkPaint* ptr) : mPtr(ptr) {} - PaintCoW(const PaintCoW&) = delete; - PaintCoW(PaintCoW&&) = delete; - PaintCoW& operator=(const PaintCoW&) = delete; - PaintCoW& operator=(PaintCoW&&) = delete; - SkPaint& writeable() { - if (!mStorage) { - if (!mPtr) { - mStorage.emplace(); - } else { - mStorage.emplace(*mPtr); - } - mPtr = &*mStorage; - } - return *mStorage; - } - operator const SkPaint*() const { return mPtr; } - const SkPaint* operator->() const { assert(mPtr); return mPtr; } - explicit operator bool() { return mPtr != nullptr; } - private: - const SkPaint* mPtr; - std::optional<SkPaint> mStorage; - }; + void onFilterPaint(SkPaint& paint); - /** Filters the paint using the current paint filter. - * - * @param paint the paint to filter. Will be initialized with the default - * SkPaint before filtering if filtering is required. - */ - PaintCoW&& filterPaint(PaintCoW&& paint) const; + SkPaint filterPaint(const SkPaint& src) { + SkPaint dst(src); + this->onFilterPaint(dst); + return dst; + } // proc(const SkPaint& modifiedPaint) - template <typename Proc> void apply_looper(const Paint* paint, Proc proc) { - SkPaint skp; - BlurDrawLooper* looper = nullptr; - if (paint) { - skp = *filterPaint(paint); - looper = paint->getLooper(); + template <typename Proc> + void applyLooper(const Paint* paint, Proc proc, void (*preFilter)(SkPaint&) = nullptr) { + BlurDrawLooper* looper = paint ? paint->getLooper() : nullptr; + const SkPaint* skpPtr = paint; + SkPaint skp = skpPtr ? *skpPtr : SkPaint(); + if (preFilter) { + preFilter(skp); } + this->onFilterPaint(skp); if (looper) { looper->apply(skp, [&](SkPoint offset, const SkPaint& modifiedPaint) { mCanvas->save(); @@ -228,7 +199,6 @@ protected: } } - private: struct SaveRec { int saveCount; diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index f2481f83767d..cc9094c8afe9 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -98,6 +98,8 @@ public: const SkISize screenSize; + int stretchEffectCount = 0; + struct Out { bool hasFunctors = false; // This is only updated if evaluateAnimations is true diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index 146bf283c58a..b046f45d9c57 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -110,16 +110,19 @@ public: bool darken = channelSum < (128 * 3); // outline + gDrawTextBlobMode = DrawTextBlobMode::HctOutline; Paint outlinePaint(paint); simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance); // inner + gDrawTextBlobMode = DrawTextBlobMode::HctInner; Paint innerPaint(paint); simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); innerPaint.setStyle(SkPaint::kFill_Style); canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance); + gDrawTextBlobMode = DrawTextBlobMode::Normal; } else { // standard draw path canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance); diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index fdfa2883c33f..d1bdb712e911 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -61,6 +61,14 @@ class Bitmap; class Paint; struct Typeface; +enum class DrawTextBlobMode { + Normal, + HctOutline, + HctInner, +}; + +inline DrawTextBlobMode gDrawTextBlobMode = DrawTextBlobMode::Normal; + class ANDROID_API Canvas { public: virtual ~Canvas(){}; diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index df66981853bb..f24ba5c1c878 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -240,8 +240,8 @@ static void android_view_ThreadedRenderer_setSdrWhitePoint(JNIEnv* env, jobject static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) { LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE, - "Mismatched size expectations, given %d expected %d", - frameInfoSize, UI_THREAD_FRAME_INFO_SIZE); + "Mismatched size expectations, given %d expected %zu", frameInfoSize, + UI_THREAD_FRAME_INFO_SIZE); RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo()); return proxy->syncAndDrawFrame(); diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp index 80239687a7fb..5f60437b5cc7 100644 --- a/libs/hwui/jni/android_graphics_RenderNode.cpp +++ b/libs/hwui/jni/android_graphics_RenderNode.cpp @@ -25,6 +25,7 @@ #include <renderthread/CanvasContext.h> #endif #include <TreeInfo.h> +#include <effects/StretchEffect.h> #include <hwui/Paint.h> #include <utils/TraceUtils.h> @@ -549,6 +550,7 @@ static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz, // ---------------------------------------------------------------------------- jmethodID gPositionListener_PositionChangedMethod; +jmethodID gPositionListener_ApplyStretchMethod; jmethodID gPositionListener_PositionLostMethod; static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, @@ -571,6 +573,11 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, Matrix4 transform; info.damageAccumulator->computeCurrentTransform(&transform); const RenderProperties& props = node.properties(); + + if (info.stretchEffectCount) { + handleStretchEffect(info, transform); + } + uirenderer::Rect bounds(props.getWidth(), props.getHeight()); transform.mapRect(bounds); @@ -613,7 +620,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, JNIEnv* env = jnienv(); jobject localref = env->NewLocalRef(mWeakRef); if (CC_UNLIKELY(!localref)) { - jnienv()->DeleteWeakGlobalRef(mWeakRef); + env->DeleteWeakGlobalRef(mWeakRef); mWeakRef = nullptr; return; } @@ -634,6 +641,32 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, return env; } + void handleStretchEffect(const TreeInfo& info, const Matrix4& transform) { + // Search up to find the nearest stretcheffect parent + const StretchEffect* effect = info.damageAccumulator->findNearestStretchEffect(); + if (!effect) { + return; + } + + uirenderer::Rect area = effect->stretchArea; + transform.mapRect(area); + JNIEnv* env = jnienv(); + + jobject localref = env->NewLocalRef(mWeakRef); + if (CC_UNLIKELY(!localref)) { + env->DeleteWeakGlobalRef(mWeakRef); + mWeakRef = nullptr; + return; + } +#ifdef __ANDROID__ // Layoutlib does not support CanvasContext + env->CallVoidMethod(localref, gPositionListener_ApplyStretchMethod, + info.canvasContext.getFrameNumber(), area.left, area.top, + area.right, area.bottom, effect->stretchDirection.fX, + effect->stretchDirection.fY, effect->maxStretchAmount); +#endif + env->DeleteLocalRef(localref); + } + void doUpdatePositionAsync(jlong frameNumber, jint left, jint top, jint right, jint bottom) { ATRACE_NAME("Update SurfaceView position"); @@ -775,6 +808,8 @@ int register_android_view_RenderNode(JNIEnv* env) { jclass clazz = FindClassOrDie(env, "android/graphics/RenderNode$PositionUpdateListener"); gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz, "positionChanged", "(JIIII)V"); + gPositionListener_ApplyStretchMethod = + GetMethodIDOrDie(env, clazz, "applyStretch", "(JFFFFFFF)V"); gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz, "positionLost", "(J)V"); return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 389fe7eed7c7..50eea31f984f 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -105,7 +105,6 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con LightingInfo::updateLighting(lightGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, SkMatrix::I()); - layerUpdateQueue->clear(); // Draw visual debugging features if (CC_UNLIKELY(Properties::showDirtyRegions || @@ -113,9 +112,14 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con SkCanvas* profileCanvas = surface->getCanvas(); SkiaProfileRenderer profileRenderer(profileCanvas); profiler->draw(profileRenderer); - profileCanvas->flush(); } + { + ATRACE_NAME("flush commands"); + surface->flushAndSubmit(); + } + layerUpdateQueue->clear(); + // Log memory statistics if (CC_UNLIKELY(Properties::debugLevel != kDebugDisabled)) { dumpResourceCacheUsage(); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 6456e36a847a..1f73ac919a47 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -454,9 +454,6 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform); } - ATRACE_NAME("flush commands"); - surface->flushAndSubmit(); - Properties::skpCaptureEnabled = previousSkpEnabled; } diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index b2884023a83d..af7271e96cb9 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -170,55 +170,23 @@ void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { // Recording Canvas draw operations: Bitmaps // ---------------------------------------------------------------------------- -SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint) { - bool fixBlending = false; - bool fixAA = false; - if (paint) { - // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and - // older. - fixBlending = sApiLevel <= 27 && paint->getBlendMode() == SkBlendMode::kClear; - fixAA = paint->isAntiAlias(); +void SkiaRecordingCanvas::FilterForImage(SkPaint& paint) { + // kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and + // older. + if (sApiLevel <= 27 && paint.getBlendMode() == SkBlendMode::kClear) { + paint.setBlendMode(SkBlendMode::kDstOut); } - if (fixBlending || fixAA) { - SkPaint& tmpPaint = paint.writeable(); - - if (fixBlending) { - tmpPaint.setBlendMode(SkBlendMode::kDstOut); - } - - // disabling AA on bitmap draws matches legacy HWUI behavior - tmpPaint.setAntiAlias(false); - } - - return filterPaint(std::move(paint)); -} - -static BlurDrawLooper* get_looper(const Paint* paint) { - return paint ? paint->getLooper() : nullptr; -} - -template <typename Proc> -void applyLooper(BlurDrawLooper* looper, const SkPaint* paint, Proc proc) { - if (looper) { - SkPaint p; - if (paint) { - p = *paint; - } - looper->apply(p, [&](SkPoint offset, const SkPaint& modifiedPaint) { - proc(offset.fX, offset.fY, &modifiedPaint); - }); - } else { - proc(0, 0, paint); - } + // disabling AA on bitmap draws matches legacy HWUI behavior + paint.setAntiAlias(false); } -static SkFilterMode Paint_to_filter(const SkPaint* paint) { - return paint && paint->getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear - : SkFilterMode::kNearest; +static SkFilterMode Paint_to_filter(const SkPaint& paint) { + return paint.getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear + : SkFilterMode::kNearest; } -static SkSamplingOptions Paint_to_sampling(const SkPaint* paint) { +static SkSamplingOptions Paint_to_sampling(const SkPaint& paint) { // Android only has 1-bit for "filter", so we don't try to cons-up mipmaps or cubics return SkSamplingOptions(Paint_to_filter(paint), SkMipmapMode::kNone); } @@ -226,10 +194,12 @@ static SkSamplingOptions Paint_to_sampling(const SkPaint* paint) { void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) { sk_sp<SkImage> image = bitmap.makeImage(); - applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y, - const SkPaint* p) { - mRecorder.drawImage(image, left + x, top + y, Paint_to_sampling(p), p, bitmap.palette()); - }); + applyLooper( + paint, + [&](const SkPaint& p) { + mRecorder.drawImage(image, left, top, Paint_to_sampling(p), &p, bitmap.palette()); + }, + FilterForImage); // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means // it is not safe to store a raw SkImage pointer, because the image object will be destroyed @@ -245,10 +215,12 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, con sk_sp<SkImage> image = bitmap.makeImage(); - applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y, - const SkPaint* p) { - mRecorder.drawImage(image, x, y, Paint_to_sampling(p), p, bitmap.palette()); - }); + applyLooper( + paint, + [&](const SkPaint& p) { + mRecorder.drawImage(image, 0, 0, Paint_to_sampling(p), &p, bitmap.palette()); + }, + FilterForImage); if (!bitmap.isImmutable() && image.get() && !image->unique()) { mDisplayList->mMutableImages.push_back(image.get()); @@ -263,11 +235,13 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop sk_sp<SkImage> image = bitmap.makeImage(); - applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y, - const SkPaint* p) { - mRecorder.drawImageRect(image, srcRect, dstRect.makeOffset(x, y), Paint_to_sampling(p), - p, SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); - }); + applyLooper( + paint, + [&](const SkPaint& p) { + mRecorder.drawImageRect(image, srcRect, dstRect, Paint_to_sampling(p), &p, + SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); + }, + FilterForImage); if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() && !dstRect.isEmpty()) { @@ -303,11 +277,12 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch // HWUI always draws 9-patches with linear filtering, regardless of the Paint. const SkFilterMode filter = SkFilterMode::kLinear; - applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y, - const SkPaint* p) { - mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), filter, p, - bitmap.palette()); - }); + applyLooper( + paint, + [&](const SkPaint& p) { + mRecorder.drawImageLattice(image, lattice, dst, filter, &p, bitmap.palette()); + }, + FilterForImage); if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index 8d7a21a732dd..ff03e0c5f6d6 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -87,6 +87,8 @@ private: std::unique_ptr<SkiaDisplayList> mDisplayList; StartReorderBarrierDrawable* mCurrentBarrier; + static void FilterForImage(SkPaint&); + /** * A new SkiaDisplayList is created or recycled if available. * @@ -96,7 +98,7 @@ private: */ void initDisplayList(uirenderer::RenderNode* renderNode, int width, int height); - PaintCoW&& filterBitmap(PaintCoW&& paint); + using INHERITED = SkiaCanvas; }; } // namespace skiapipeline diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index ad6363b4452d..1bd943f4c21d 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -17,14 +17,15 @@ #include "SkiaVulkanPipeline.h" #include "DeferredLayerUpdater.h" +#include "LightingInfo.h" #include "Readback.h" #include "ShaderCache.h" -#include "LightingInfo.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" #include "VkInteropFunctorDrawable.h" #include "renderstate/RenderState.h" #include "renderthread/Frame.h" +#include "utils/TraceUtils.h" #include <SkSurface.h> #include <SkTypes.h> @@ -73,8 +74,6 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con LightingInfo::updateLighting(lightGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer, mVkSurface->getCurrentPreTransform()); - ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext()); - layerUpdateQueue->clear(); // Draw visual debugging features if (CC_UNLIKELY(Properties::showDirtyRegions || @@ -82,9 +81,14 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con SkCanvas* profileCanvas = backBuffer->getCanvas(); SkiaProfileRenderer profileRenderer(profileCanvas); profiler->draw(profileRenderer); - profileCanvas->flush(); } + { + ATRACE_NAME("flush commands"); + mVkManager.finishFrame(backBuffer.get()); + } + layerUpdateQueue->clear(); + // Log memory statistics if (CC_UNLIKELY(Properties::debugLevel != kDebugDisabled)) { dumpResourceCacheUsage(); diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index b760db287bcb..f69ddacf7ca1 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -484,7 +484,8 @@ void CanvasContext::draw() { // TODO(b/165985262): measure performance impact const auto vsyncId = mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId); if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) { - const auto inputEventId = mCurrentFrameInfo->get(FrameInfoIndex::NewestInputEvent); + const auto inputEventId = + static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId)); native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), vsyncId, inputEventId); } @@ -591,7 +592,6 @@ void CanvasContext::draw() { } void CanvasContext::finishFrame(FrameInfo* frameInfo) { - // TODO (b/169858044): Consolidate this into a single call. mJankTracker.finishFrame(*frameInfo); mJankTracker.finishGpuDraw(*frameInfo); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index b9568fcf8e66..423cc08189ca 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -230,7 +230,10 @@ void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) { } void RenderProxy::resetProfileInfo() { - mRenderThread.queue().runSync([=]() { mContext->resetFrameStats(); }); + mRenderThread.queue().runSync([=]() { + std::lock_guard lock(mRenderThread.getJankDataMutex()); + mContext->resetFrameStats(); + }); } uint32_t RenderProxy::frameTimePercentile(int percentile) { diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 3e7ce368f55d..e93824dfbd30 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -31,6 +31,7 @@ #include "Properties.h" #include "RenderThread.h" +#include "pipeline/skia/ShaderCache.h" #include "renderstate/RenderState.h" #include "utils/TraceUtils.h" @@ -476,17 +477,10 @@ static void destroy_semaphore(void* context) { } } -void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) { - if (CC_UNLIKELY(Properties::waitForGpuCompletion)) { - ATRACE_NAME("Finishing GPU work"); - mDeviceWaitIdle(mDevice); - } - - VulkanSurface::NativeBufferInfo* bufferInfo = surface->getCurrentBufferInfo(); - if (!bufferInfo) { - // If VulkanSurface::dequeueNativeBuffer failed earlier, then swapBuffers is a no-op. - return; - } +void VulkanManager::finishFrame(SkSurface* surface) { + ATRACE_NAME("Vulkan finish frame"); + ALOGE_IF(mSwapSemaphore != VK_NULL_HANDLE || mDestroySemaphoreContext != nullptr, + "finishFrame already has an outstanding semaphore"); VkExportSemaphoreCreateInfo exportInfo; exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; @@ -499,32 +493,52 @@ void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) semaphoreInfo.flags = 0; VkSemaphore semaphore; VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore); - ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to create semaphore"); + ALOGE_IF(VK_SUCCESS != err, "VulkanManager::makeSwapSemaphore(): Failed to create semaphore"); GrBackendSemaphore backendSemaphore; backendSemaphore.initVulkan(semaphore); - int fenceFd = -1; - DestroySemaphoreInfo* destroyInfo = - new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore); GrFlushInfo flushInfo; - flushInfo.fNumSemaphores = 1; - flushInfo.fSignalSemaphores = &backendSemaphore; - flushInfo.fFinishedProc = destroy_semaphore; - flushInfo.fFinishedContext = destroyInfo; - GrSemaphoresSubmitted submitted = bufferInfo->skSurface->flush( - SkSurface::BackendSurfaceAccess::kPresent, flushInfo); - GrDirectContext* context = GrAsDirectContext(bufferInfo->skSurface->recordingContext()); + if (err == VK_SUCCESS) { + mDestroySemaphoreContext = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore); + flushInfo.fNumSemaphores = 1; + flushInfo.fSignalSemaphores = &backendSemaphore; + flushInfo.fFinishedProc = destroy_semaphore; + flushInfo.fFinishedContext = mDestroySemaphoreContext; + } else { + semaphore = VK_NULL_HANDLE; + } + GrSemaphoresSubmitted submitted = + surface->flush(SkSurface::BackendSurfaceAccess::kPresent, flushInfo); + GrDirectContext* context = GrAsDirectContext(surface->recordingContext()); ALOGE_IF(!context, "Surface is not backed by gpu"); context->submit(); - if (submitted == GrSemaphoresSubmitted::kYes) { + if (semaphore != VK_NULL_HANDLE) { + if (submitted == GrSemaphoresSubmitted::kYes) { + mSwapSemaphore = semaphore; + } else { + destroy_semaphore(mDestroySemaphoreContext); + mDestroySemaphoreContext = nullptr; + } + } + skiapipeline::ShaderCache::get().onVkFrameFlushed(context); +} + +void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) { + if (CC_UNLIKELY(Properties::waitForGpuCompletion)) { + ATRACE_NAME("Finishing GPU work"); + mDeviceWaitIdle(mDevice); + } + + int fenceFd = -1; + if (mSwapSemaphore != VK_NULL_HANDLE) { VkSemaphoreGetFdInfoKHR getFdInfo; getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; getFdInfo.pNext = nullptr; - getFdInfo.semaphore = semaphore; + getFdInfo.semaphore = mSwapSemaphore; getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; - err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd); + VkResult err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd); ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd"); } else { ALOGE("VulkanManager::swapBuffers(): Semaphore submission failed"); @@ -532,9 +546,11 @@ void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) std::lock_guard<std::mutex> lock(mGraphicsQueueMutex); mQueueWaitIdle(mGraphicsQueue); } - destroy_semaphore(destroyInfo); + destroy_semaphore(mDestroySemaphoreContext); surface->presentCurrentBuffer(dirtyRect, fenceFd); + mSwapSemaphore = VK_NULL_HANDLE; + mDestroySemaphoreContext = nullptr; } void VulkanManager::destroySurface(VulkanSurface* surface) { diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 121afc90cfe5..0912369b611d 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -69,6 +69,7 @@ public: void destroySurface(VulkanSurface* surface); Frame dequeueNextBuffer(VulkanSurface* surface); + void finishFrame(SkSurface* surface); void swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect); // Inserts a wait on fence command into the Vulkan command buffer. @@ -200,6 +201,9 @@ private: SwapBehavior mSwapBehavior = SwapBehavior::Discard; GrVkExtensions mExtensions; uint32_t mDriverVersion = 0; + + VkSemaphore mSwapSemaphore = VK_NULL_HANDLE; + void* mDestroySemaphoreContext = nullptr; }; } /* namespace renderthread */ diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp index 6a9a98d6743b..898c64bd4159 100644 --- a/libs/hwui/tests/common/TestContext.cpp +++ b/libs/hwui/tests/common/TestContext.cpp @@ -22,16 +22,16 @@ namespace android { namespace uirenderer { namespace test { -const DisplayInfo& getDisplayInfo() { - static DisplayInfo info = [] { - DisplayInfo info; +const ui::StaticDisplayInfo& getDisplayInfo() { + static ui::StaticDisplayInfo info = [] { + ui::StaticDisplayInfo info; #if HWUI_NULL_GPU info.density = 2.f; #else const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__); - const status_t status = SurfaceComposerClient::getDisplayInfo(token, &info); + const status_t status = SurfaceComposerClient::getStaticDisplayInfo(token, &info); LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get display info", __FUNCTION__); #endif return info; diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h index 7d2f6d8ea731..9d00366daffe 100644 --- a/libs/hwui/tests/common/TestContext.h +++ b/libs/hwui/tests/common/TestContext.h @@ -23,8 +23,8 @@ #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> -#include <ui/DisplayInfo.h> #include <ui/DisplayMode.h> +#include <ui/StaticDisplayInfo.h> #include <utils/Looper.h> #include <atomic> @@ -36,7 +36,7 @@ namespace android { namespace uirenderer { namespace test { -const DisplayInfo& getDisplayInfo(); +const ui::StaticDisplayInfo& getDisplayInfo(); const ui::DisplayMode& getActiveDisplayMode(); inline const ui::Size& getActiveDisplayResolution() { diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp index ab23448ab93f..1a3dbe7faaf6 100644 --- a/libs/hwui/tests/unit/TypefaceTests.cpp +++ b/libs/hwui/tests/unit/TypefaceTests.cpp @@ -33,7 +33,7 @@ namespace { constexpr char kRobotoVariable[] = "/system/fonts/Roboto-Regular.ttf"; -constexpr char kRegularFont[] = "/system/fonts/NotoSerif-Regular.ttf"; +constexpr char kRegularFont[] = "/system/fonts/NotoSerif.ttf"; constexpr char kBoldFont[] = "/system/fonts/NotoSerif-Bold.ttf"; constexpr char kItalicFont[] = "/system/fonts/NotoSerif-Italic.ttf"; constexpr char kBoldItalicFont[] = "/system/fonts/NotoSerif-BoldItalic.ttf"; diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp index 438a92ef8fb5..f322bff8e6ee 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -13,6 +13,15 @@ // 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"], +} + cc_defaults { name: "libincidentpriv_defaults", @@ -125,6 +134,3 @@ cc_test { "libgmock", ], } - - - diff --git a/libs/input/Android.bp b/libs/input/Android.bp index dca35012cbdd..55f932dffff1 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + cc_library_shared { name: "libinputservice", srcs: [ diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h index e6dfc4c6f99a..c0ab58bd2e7e 100644 --- a/libs/input/MouseCursorController.h +++ b/libs/input/MouseCursorController.h @@ -20,7 +20,6 @@ #include <gui/DisplayEventReceiver.h> #include <input/DisplayViewport.h> #include <input/Input.h> -#include <ui/DisplayInfo.h> #include <utils/BitSet.h> #include <utils/Looper.h> #include <utils/RefBase.h> diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 827fcf1e1bc1..97567bab202b 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -21,7 +21,6 @@ #include <gui/DisplayEventReceiver.h> #include <input/DisplayViewport.h> #include <input/Input.h> -#include <ui/DisplayInfo.h> #include <utils/BitSet.h> #include <utils/Looper.h> #include <utils/RefBase.h> diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h index 98073fea323e..26a65a47471d 100644 --- a/libs/input/PointerControllerContext.h +++ b/libs/input/PointerControllerContext.h @@ -21,7 +21,6 @@ #include <gui/DisplayEventReceiver.h> #include <input/DisplayViewport.h> #include <input/Input.h> -#include <ui/DisplayInfo.h> #include <utils/BitSet.h> #include <utils/Looper.h> #include <utils/RefBase.h> diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 213b3adfb2a8..4eabfb238f4a 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + cc_test { name: "libinputservice_test", srcs: [ diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp index d2b7d5c75d58..132d71eb6db8 100644 --- a/libs/protoutil/Android.bp +++ b/libs/protoutil/Android.bp @@ -12,6 +12,15 @@ // WITHOUT WARRANTIES 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"], +} + cc_defaults { name: "libprotoutil_defaults", diff --git a/libs/services/Android.bp b/libs/services/Android.bp index 1e621079b532..bf2e764aae6a 100644 --- a/libs/services/Android.bp +++ b/libs/services/Android.bp @@ -14,6 +14,15 @@ // Provides C++ wrappers for system services. +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"], +} + cc_library_shared { name: "libservices", srcs: [ diff --git a/libs/storage/Android.bp b/libs/storage/Android.bp index c19933e39c96..e8c6c071bc06 100644 --- a/libs/storage/Android.bp +++ b/libs/storage/Android.bp @@ -1,3 +1,20 @@ +package { + default_applicable_licenses: ["frameworks_base_libs_storage_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_libs_storage_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_library_static { name: "libstorage", diff --git a/libs/tracingproxy/Android.bp b/libs/tracingproxy/Android.bp index 67f407ff7599..7126bfac773d 100644 --- a/libs/tracingproxy/Android.bp +++ b/libs/tracingproxy/Android.bp @@ -14,6 +14,17 @@ // Provides C++ wrappers for system services. +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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], +} + cc_library_shared { name: "libtracingproxy", diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp index e752b55f5ef7..edc847487dcb 100644 --- a/libs/usb/Android.bp +++ b/libs/usb/Android.bp @@ -14,6 +14,16 @@ // 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 + // SPDX-license-identifier-GPL-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + java_sdk_library { name: "com.android.future.usb.accessory", srcs: ["src/**/*.java"], diff --git a/libs/usb/tests/AccessoryChat/Android.bp b/libs/usb/tests/AccessoryChat/Android.bp index 19ed3d3ef52e..72090fb692e0 100644 --- a/libs/usb/tests/AccessoryChat/Android.bp +++ b/libs/usb/tests/AccessoryChat/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "AccessoryChat", diff --git a/libs/usb/tests/AccessoryChat/accessorychat/Android.bp b/libs/usb/tests/AccessoryChat/accessorychat/Android.bp index 5613745e966b..108649aa92ae 100644 --- a/libs/usb/tests/AccessoryChat/accessorychat/Android.bp +++ b/libs/usb/tests/AccessoryChat/accessorychat/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_binary { name: "accessorychat", host_supported: true, diff --git a/libs/usb/tests/accessorytest/Android.bp b/libs/usb/tests/accessorytest/Android.bp index c6340e32e60c..69761aeb46fe 100644 --- a/libs/usb/tests/accessorytest/Android.bp +++ b/libs/usb/tests/accessorytest/Android.bp @@ -1,3 +1,13 @@ +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 + // SPDX-license-identifier-GPL-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + cc_binary_host { name: "accessorytest", diff --git a/location/java/android/location/GnssAntennaInfo.java b/location/java/android/location/GnssAntennaInfo.java index f1eb8fccdd3c..6633d24ebc35 100644 --- a/location/java/android/location/GnssAntennaInfo.java +++ b/location/java/android/location/GnssAntennaInfo.java @@ -39,11 +39,7 @@ public final class GnssAntennaInfo implements Parcelable { /** * Used for receiving GNSS antenna info from the GNSS engine. - * - * @deprecated Prefer to use a broadcast receiver for - * {@link LocationManager#ACTION_GNSS_ANTENNA_INFOS_CHANGED}. */ - @Deprecated public interface Listener { /** * Invoked on a change to GNSS antenna info. diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java index 3dcaffbd2712..242d9a3627db 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -1096,13 +1096,7 @@ public final class GnssMeasurement implements Parcelable { * Gets the carrier frequency of the tracked signal. * * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz, - * L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary - * common use central frequency, e.g. L1 = 1575.45 MHz for GPS. - * - * <p> For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two raw - * measurement objects will be reported for this same satellite, in one of the measurement - * objects, all the values related to L1 will be filled, and in the other all of the values - * related to L5 will be filled. + * L5 = 1176.45 MHz, varying GLO channels, etc. * * <p>The value is only available if {@link #hasCarrierFrequencyHz()} is {@code true}. * @@ -1382,7 +1376,8 @@ public final class GnssMeasurement implements Parcelable { * <p> AGC acts as a variable gain amplifier adjusting the power of the incoming signal. The AGC * level may be used to indicate potential interference. Higher gain (and/or lower input power) * shall be output as a positive number. Hence in cases of strong jamming, in the band of this - * signal, this value will go more negative. + * signal, this value will go more negative. This value must be consistent given the same level + * of the incoming signal power. * * <p> Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW * components) may also affect the typical output of of this value on any given hardware design diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java index b46e8ce2f605..23390fce1a5f 100644 --- a/location/java/android/location/GnssStatus.java +++ b/location/java/android/location/GnssStatus.java @@ -284,12 +284,7 @@ public final class GnssStatus implements Parcelable { * Gets the carrier frequency of the signal tracked. * * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 - * MHz, L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary - * common use central frequency, e.g. L1 = 1575.45 MHz for GPS. - * - * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two measurements - * will be reported for this same satellite, in one all the values related to L1 will be - * filled, and in the other all of the values related to L5 will be filled. + * MHz, L5 = 1176.45 MHz, varying GLO channels, etc. * * <p>The value is only available if {@link #hasCarrierFrequencyHz(int satelliteIndex)} is * {@code true}. diff --git a/location/java/android/location/IGnssAntennaInfoListener.aidl b/location/java/android/location/IGnssAntennaInfoListener.aidl new file mode 100644 index 000000000000..3cceea3e6d26 --- /dev/null +++ b/location/java/android/location/IGnssAntennaInfoListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.location; + +import android.location.GnssAntennaInfo; + +/** + * {@hide} + */ +oneway interface IGnssAntennaInfoListener { + void onGnssAntennaInfoChanged(in List<GnssAntennaInfo> antennaInfos); +} diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index adf58da6a072..6fa6536997c4 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -26,6 +26,7 @@ import android.location.GnssCapabilities; import android.location.GnssMeasurementCorrections; import android.location.GnssMeasurementRequest; import android.location.IGeocodeListener; +import android.location.IGnssAntennaInfoListener; import android.location.IGnssMeasurementsListener; import android.location.IGnssStatusListener; import android.location.IGnssNavigationMessageListener; @@ -48,13 +49,13 @@ import android.os.ICancellationSignal; */ interface ILocationManager { - @nullable Location getLastLocation(String provider, in LastLocationRequest request, String packageName, String attributionTag); - @nullable ICancellationSignal getCurrentLocation(String provider, in LocationRequest request, in ILocationCallback callback, String packageName, String attributionTag, String listenerId); + @nullable Location getLastLocation(String provider, in LastLocationRequest request, String packageName, @nullable String attributionTag); + @nullable ICancellationSignal getCurrentLocation(String provider, in LocationRequest request, in ILocationCallback callback, String packageName, @nullable String attributionTag, String listenerId); - void registerLocationListener(String provider, in LocationRequest request, in ILocationListener listener, String packageName, String attributionTag, String listenerId); + void registerLocationListener(String provider, in LocationRequest request, in ILocationListener listener, String packageName, @nullable String attributionTag, String listenerId); void unregisterLocationListener(in ILocationListener listener); - void registerLocationPendingIntent(String provider, in LocationRequest request, in PendingIntent pendingIntent, String packageName, String attributionTag); + void registerLocationPendingIntent(String provider, in LocationRequest request, in PendingIntent pendingIntent, String packageName, @nullable String attributionTag); void unregisterLocationPendingIntent(in PendingIntent pendingIntent); void injectLocation(in Location location); @@ -79,24 +80,27 @@ interface ILocationManager @nullable List<GnssAntennaInfo> getGnssAntennaInfos(); - void registerGnssStatusCallback(in IGnssStatusListener callback, String packageName, @nullable String attributionTag); + void registerGnssStatusCallback(in IGnssStatusListener callback, String packageName, @nullable String attributionTag, String listenerId); void unregisterGnssStatusCallback(in IGnssStatusListener callback); - void registerGnssNmeaCallback(in IGnssNmeaListener callback, String packageName, @nullable String attributionTag); + void registerGnssNmeaCallback(in IGnssNmeaListener callback, String packageName, @nullable String attributionTag, String listenerId); void unregisterGnssNmeaCallback(in IGnssNmeaListener callback); - void addGnssMeasurementsListener(in GnssMeasurementRequest request, in IGnssMeasurementsListener listener, String packageName, @nullable String attributionTag); + void addGnssMeasurementsListener(in GnssMeasurementRequest request, in IGnssMeasurementsListener listener, String packageName, @nullable String attributionTag, String listenerId); void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener); void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections); - void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag); + void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag, String listenerId); void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener); + void addGnssAntennaInfoListener(in IGnssAntennaInfoListener listener, String packageName, @nullable String attributionTag, String listenerId); + void removeGnssAntennaInfoListener(in IGnssAntennaInfoListener listener); + void addProviderRequestListener(in IProviderRequestListener listener); void removeProviderRequestListener(in IProviderRequestListener listener); int getGnssBatchSize(); - void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, @nullable String attributionTag, @nullable String listenerId); + void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, @nullable String attributionTag, String listenerId); void flushGnssBatch(); void stopGnssBatch(); @@ -117,7 +121,8 @@ interface ILocationManager boolean isLocationEnabledForUser(int userId); void setLocationEnabledForUser(boolean enabled, int userId); - void addTestProvider(String name, in ProviderProperties properties, String packageName, @nullable String attributionTag); + void addTestProvider(String name, in ProviderProperties properties, + in List<String> locationTags, String packageName, @nullable String attributionTag); void removeTestProvider(String provider, String packageName, @nullable String attributionTag); void setTestProviderLocation(String provider, in Location location, String packageName, @nullable String attributionTag); void setTestProviderEnabled(String provider, boolean enabled, String packageName, @nullable String attributionTag); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 088b789ea690..e73e915bf88b 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -20,7 +20,6 @@ import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.Manifest.permission.LOCATION_HARDWARE; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; -import static android.location.GpsStatus.GPS_EVENT_STARTED; import static android.location.LocationRequest.createFromDeprecatedCriteria; import static android.location.LocationRequest.createFromDeprecatedProvider; @@ -44,15 +43,13 @@ import android.app.PropertyInvalidatedCache; import android.compat.Compatibility; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageManager; import android.location.provider.IProviderRequestListener; import android.location.provider.ProviderProperties; import android.location.provider.ProviderRequest; -import android.location.provider.ProviderRequest.Listener; +import android.location.provider.ProviderRequest.ChangedListener; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; @@ -77,6 +74,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -349,28 +347,6 @@ public class LocationManager { public static final String EXTRA_GNSS_CAPABILITIES = "android.location.extra.GNSS_CAPABILITIES"; /** - * Broadcast intent action when GNSS antenna infos change. Includes an intent extra, - * {@link #EXTRA_GNSS_ANTENNA_INFOS}, with an ArrayList of the new {@link GnssAntennaInfo}. This - * may be read via {@link android.content.Intent#getParcelableArrayListExtra(String)}. - * - * @see #EXTRA_GNSS_ANTENNA_INFOS - * @see #getGnssAntennaInfos() - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_GNSS_ANTENNA_INFOS_CHANGED = - "android.location.action.GNSS_ANTENNA_INFOS_CHANGED"; - - /** - * Intent extra included with {@link #ACTION_GNSS_ANTENNA_INFOS_CHANGED} broadcasts, containing - * the new ArrayList of {@link GnssAntennaInfo}. This may be read via - * {@link android.content.Intent#getParcelableArrayListExtra(String)}. - * - * @see #ACTION_GNSS_ANTENNA_INFOS_CHANGED - */ - public static final String EXTRA_GNSS_ANTENNA_INFOS = - "android.location.extra.GNSS_ANTENNA_INFOS"; - - /** * Broadcast intent action for Settings app to inject a footer at the bottom of location * settings. This is for use only by apps that are included in the system image. * @@ -412,7 +388,7 @@ public class LocationManager { private static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY = "cache_key.location_enabled"; - private static ILocationManager getService() throws RemoteException { + static ILocationManager getService() throws RemoteException { try { return ILocationManager.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.LOCATION_SERVICE)); @@ -439,11 +415,13 @@ public class LocationManager { new GnssNavigationTransportManager(); } - private static final ProviderRequestTransportManager sProviderRequestListeners = - new ProviderRequestTransportManager(); + private static class ProviderRequestLazyLoader { + static final ProviderRequestTransportManager sProviderRequestListeners = + new ProviderRequestTransportManager(); + } - private final Context mContext; - private final ILocationManager mService; + final Context mContext; + final ILocationManager mService; private volatile PropertyInvalidatedCache<Integer, Boolean> mLocationEnabledCache = new PropertyInvalidatedCache<Integer, Boolean>( @@ -1956,12 +1934,33 @@ public class LocationManager { * allowed} for your app. */ public void addTestProvider(@NonNull String provider, @NonNull ProviderProperties properties) { + addTestProvider(provider, properties, Collections.emptySet()); + } + + /** + * Creates a test location provider and adds it to the set of active providers. This provider + * will replace any provider with the same name that exists prior to this call. + * + * @param provider the provider name + * @param properties the provider properties + * @param extraAttributionTags additional attribution tags associated with this provider + * + * @throws IllegalArgumentException if provider is null + * @throws IllegalArgumentException if properties is null + * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION + * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED + * allowed} for your app. + */ + public void addTestProvider(@NonNull String provider, @NonNull ProviderProperties properties, + @NonNull Set<String> extraAttributionTags) { Preconditions.checkArgument(provider != null, "invalid null provider"); Preconditions.checkArgument(properties != null, "invalid null properties"); + Preconditions.checkArgument(extraAttributionTags != null, + "invalid null extra attribution tags"); try { - mService.addTestProvider(provider, properties, mContext.getOpPackageName(), - mContext.getFeatureId()); + mService.addTestProvider(provider, properties, new ArrayList<>(extraAttributionTags), + mContext.getOpPackageName(), mContext.getFeatureId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2637,10 +2636,11 @@ public class LocationManager { } /** - * Registers a GNSS antenna info listener. GNSS antenna info updates will only be received while - * the {@link #GPS_PROVIDER} is enabled, and while the client app is in the foreground. + * Registers a GNSS antenna info listener that will receive all changes to antenna info. Use + * {@link #getGnssAntennaInfos()} to get current antenna info. * - * <p>Not all GNSS chipsets support antenna info updates, see {@link #getGnssCapabilities()}. + * <p>Not all GNSS chipsets support antenna info updates, see {@link #getGnssCapabilities()}. If + * unsupported, the listener will never be invoked. * * <p>Prior to Android S, this requires the {@link Manifest.permission#ACCESS_FINE_LOCATION} * permission. @@ -2651,10 +2651,7 @@ public class LocationManager { * * @throws IllegalArgumentException if executor is null * @throws IllegalArgumentException if listener is null - * - * @deprecated Prefer to use a receiver for {@link #ACTION_GNSS_ANTENNA_INFOS_CHANGED}. */ - @Deprecated public boolean registerAntennaInfoListener( @NonNull @CallbackExecutor Executor executor, @NonNull GnssAntennaInfo.Listener listener) { @@ -2664,13 +2661,10 @@ public class LocationManager { } /** - * Unregisters a GNSS Antenna Info listener. + * Unregisters a GNSS antenna info listener. * * @param listener a {@link GnssAntennaInfo.Listener} object to remove - * - * @deprecated Prefer to use a receiver for {@link #ACTION_GNSS_ANTENNA_INFOS_CHANGED}. */ - @Deprecated public void unregisterAntennaInfoListener(@NonNull GnssAntennaInfo.Listener listener) { GnssLazyLoader.sGnssAntennaInfoListeners.removeListener(listener); } @@ -2778,34 +2772,33 @@ public class LocationManager { } /** - * Registers a {@link ProviderRequest.Listener} to all providers. + * Adds a {@link ProviderRequest.ChangedListener} for listening to all providers' + * {@link ProviderRequest} changed events. * * @param executor the executor that the callback runs on * @param listener the listener to register - * @return {@code true} always * @hide */ @SystemApi @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) - public boolean registerProviderRequestListener( + public void addProviderRequestChangedListener( @NonNull @CallbackExecutor Executor executor, - @NonNull Listener listener) { - sProviderRequestListeners.addListener(listener, + @NonNull ChangedListener listener) { + ProviderRequestLazyLoader.sProviderRequestListeners.addListener(listener, new ProviderRequestTransport(executor, listener)); - return true; } /** - * Unregisters a {@link ProviderRequest.Listener}. + * Removes a {@link ProviderRequest.ChangedListener} that has been added. * * @param listener the listener to remove. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) - public void unregisterProviderRequestListener( - @NonNull Listener listener) { - sProviderRequestListeners.removeListener(listener); + public void removeProviderRequestChangedListener( + @NonNull ProviderRequest.ChangedListener listener) { + ProviderRequestLazyLoader.sProviderRequestListeners.removeListener(listener); } /** @@ -2917,11 +2910,16 @@ public class LocationManager { private static class GnssStatusTransportManager extends ListenerTransportManager<GnssStatusTransport> { + GnssStatusTransportManager() { + super(false); + } + @Override protected void registerTransport(GnssStatusTransport transport) throws RemoteException { getService().registerGnssStatusCallback(transport, transport.getPackage(), - transport.getAttributionTag()); + transport.getAttributionTag(), + AppOpsManager.toReceiverId(transport.getListener())); } @Override @@ -2934,11 +2932,16 @@ public class LocationManager { private static class GnssNmeaTransportManager extends ListenerTransportManager<GnssNmeaTransport> { + GnssNmeaTransportManager() { + super(false); + } + @Override protected void registerTransport(GnssNmeaTransport transport) throws RemoteException { getService().registerGnssNmeaCallback(transport, transport.getPackage(), - transport.getAttributionTag()); + transport.getAttributionTag(), + AppOpsManager.toReceiverId(transport.getListener())); } @Override @@ -2951,11 +2954,16 @@ public class LocationManager { private static class GnssMeasurementsTransportManager extends ListenerTransportManager<GnssMeasurementsTransport> { + GnssMeasurementsTransportManager() { + super(false); + } + @Override protected void registerTransport(GnssMeasurementsTransport transport) throws RemoteException { getService().addGnssMeasurementsListener(transport.getRequest(), transport, - transport.getPackage(), transport.getAttributionTag()); + transport.getPackage(), transport.getAttributionTag(), + AppOpsManager.toReceiverId(transport.getListener())); } @Override @@ -2968,26 +2976,38 @@ public class LocationManager { private static class GnssAntennaTransportManager extends ListenerTransportManager<GnssAntennaInfoTransport> { + GnssAntennaTransportManager() { + super(false); + } + @Override - protected void registerTransport(GnssAntennaInfoTransport transport) { - transport.getContext().registerReceiver(transport, - new IntentFilter(ACTION_GNSS_ANTENNA_INFOS_CHANGED)); + protected void registerTransport(GnssAntennaInfoTransport transport) + throws RemoteException { + getService().addGnssAntennaInfoListener(transport, transport.getPackage(), + transport.getAttributionTag(), + AppOpsManager.toReceiverId(transport.getListener())); } @Override - protected void unregisterTransport(GnssAntennaInfoTransport transport) { - transport.getContext().unregisterReceiver(transport); + protected void unregisterTransport(GnssAntennaInfoTransport transport) + throws RemoteException { + getService().removeGnssAntennaInfoListener(transport); } } private static class GnssNavigationTransportManager extends ListenerTransportManager<GnssNavigationTransport> { + GnssNavigationTransportManager() { + super(false); + } + @Override protected void registerTransport(GnssNavigationTransport transport) throws RemoteException { getService().addGnssNavigationMessageListener(transport, - transport.getPackage(), transport.getAttributionTag()); + transport.getPackage(), transport.getAttributionTag(), + AppOpsManager.toReceiverId(transport.getListener())); } @Override @@ -3000,6 +3020,10 @@ public class LocationManager { private static class ProviderRequestTransportManager extends ListenerTransportManager<ProviderRequestTransport> { + ProviderRequestTransportManager() { + super(false); + } + @Override protected void registerTransport(ProviderRequestTransport transport) throws RemoteException { @@ -3117,6 +3141,8 @@ public class LocationManager { } } + /** @deprecated */ + @Deprecated private static class GpsAdapter extends GnssStatus.Callback { private final GpsStatus.Listener mGpsListener; @@ -3127,7 +3153,7 @@ public class LocationManager { @Override public void onStarted() { - mGpsListener.onGpsStatusChanged(GPS_EVENT_STARTED); + mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED); } @Override @@ -3204,6 +3230,8 @@ public class LocationManager { } } + /** @deprecated */ + @Deprecated private static class GpsStatusTransport extends GnssStatusTransport { static volatile int sTtff; @@ -3323,11 +3351,12 @@ public class LocationManager { } } - private static class GnssAntennaInfoTransport extends BroadcastReceiver implements + private static class GnssAntennaInfoTransport extends IGnssAntennaInfoListener.Stub implements ListenerTransport<GnssAntennaInfo.Listener> { private final Executor mExecutor; - private final Context mContext; + private final String mPackageName; + private final String mAttributionTag; private volatile @Nullable GnssAntennaInfo.Listener mListener; @@ -3336,12 +3365,17 @@ public class LocationManager { Preconditions.checkArgument(executor != null, "invalid null executor"); Preconditions.checkArgument(listener != null, "invalid null listener"); mExecutor = executor; - mContext = context; + mPackageName = context.getPackageName(); + mAttributionTag = context.getAttributionTag(); mListener = listener; } - public Context getContext() { - return mContext; + public String getPackage() { + return mPackageName; + } + + public String getAttributionTag() { + return mAttributionTag; } @Override @@ -3355,12 +3389,8 @@ public class LocationManager { } @Override - public void onReceive(Context context, Intent intent) { - ArrayList<GnssAntennaInfo> infos = intent.getParcelableArrayListExtra( - EXTRA_GNSS_ANTENNA_INFOS); - if (infos != null) { - execute(mExecutor, callback -> callback.onGnssAntennaInfoReceived(infos)); - } + public void onGnssAntennaInfoChanged(List<GnssAntennaInfo> antennaInfos) { + execute(mExecutor, callback -> callback.onGnssAntennaInfoReceived(antennaInfos)); } } @@ -3413,13 +3443,13 @@ public class LocationManager { } private static class ProviderRequestTransport extends IProviderRequestListener.Stub - implements ListenerTransport<ProviderRequest.Listener> { + implements ListenerTransport<ChangedListener> { private final Executor mExecutor; - private volatile @Nullable ProviderRequest.Listener mListener; + private volatile @Nullable ProviderRequest.ChangedListener mListener; - ProviderRequestTransport(Executor executor, ProviderRequest.Listener listener) { + ProviderRequestTransport(Executor executor, ChangedListener listener) { Preconditions.checkArgument(executor != null, "invalid null executor"); Preconditions.checkArgument(listener != null, "invalid null callback"); mExecutor = executor; @@ -3432,7 +3462,7 @@ public class LocationManager { } @Override - public @Nullable ProviderRequest.Listener getListener() { + public @Nullable ProviderRequest.ChangedListener getListener() { return mListener; } @@ -3442,6 +3472,8 @@ public class LocationManager { } } + /** @deprecated */ + @Deprecated private static class BatchedLocationCallbackWrapper implements LocationListener { private final BatchedLocationCallback mCallback; @@ -3461,6 +3493,8 @@ public class LocationManager { } } + /** @deprecated */ + @Deprecated private static class BatchedLocationCallbackTransport extends LocationListenerTransport { BatchedLocationCallbackTransport(BatchedLocationCallback callback, Handler handler) { diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java index a6a0e7aa24ff..763835c9cbe2 100644 --- a/location/java/android/location/LocationManagerInternal.java +++ b/location/java/android/location/LocationManagerInternal.java @@ -21,6 +21,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.location.util.identity.CallerIdentity; +import com.android.internal.annotations.Immutable; + +import java.util.Set; + /** * Location manager local system service interface. * @@ -39,6 +43,21 @@ public abstract class LocationManagerInternal { } /** + * Interface for getting callbacks when a location provider's location tags change. + * + * @see LocationTagInfo + */ + public interface OnProviderLocationTagsChangeListener { + + /** + * Called when the location tags for a provider change. + * + * @param providerLocationTagInfo The tag info for a provider. + */ + void onLocationTagsChanged(@NonNull LocationTagInfo providerLocationTagInfo); + } + + /** * Returns true if the given provider is enabled for the given user. * * @param provider A location provider as listed by {@link LocationManager#getAllProviders()} @@ -88,4 +107,60 @@ public abstract class LocationManagerInternal { * provider, and the elapsed nanos since boot the current time was computed at. */ public abstract @Nullable LocationTime getGnssTimeMillis(); + + /** + * Sets a listener for changes in the location providers' tags. Passing + * {@code null} clears the current listener. + * + * @param listener The listener. + */ + public abstract void setOnProviderLocationTagsChangeListener( + @Nullable OnProviderLocationTagsChangeListener listener); + + /** + * This class represents the location permission tags used by the location provider + * packages in a given UID. These tags are strictly used for accessing state guarded + * by the location permission(s) by a location provider which are required for the + * provider to fulfill its function as being a location provider. + */ + @Immutable + public static class LocationTagInfo { + private final int mUid; + + @NonNull + private final String mPackageName; + + @Nullable + private final Set<String> mLocationTags; + + public LocationTagInfo(int uid, @NonNull String packageName, + @Nullable Set<String> locationTags) { + mUid = uid; + mPackageName = packageName; + mLocationTags = locationTags; + } + + /** + * @return The UID for which tags are related. + */ + public int getUid() { + return mUid; + } + + /** + * @return The package for which tags are related. + */ + @NonNull + public String getPackageName() { + return mPackageName; + } + + /** + * @return The tags for the package used for location related accesses. + */ + @Nullable + public Set<String> getTags() { + return mLocationTags; + } + } } diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java index b6ec32309b08..b72d36519e72 100644 --- a/location/java/android/location/provider/ProviderRequest.java +++ b/location/java/android/location/provider/ProviderRequest.java @@ -56,10 +56,13 @@ public final class ProviderRequest implements Parcelable { /** * Listener to be invoked when a new request is set to the provider. */ - public interface Listener { + public interface ChangedListener { /** * Invoked when a new request is set. + * + * @param provider the location provider associated with the request + * @param request the new {@link ProviderRequest} */ void onProviderRequestChanged(@NonNull String provider, @NonNull ProviderRequest request); } diff --git a/location/java/android/location/util/identity/CallerIdentity.java b/location/java/android/location/util/identity/CallerIdentity.java index 0bb7dbb71ae8..85a083ee84bc 100644 --- a/location/java/android/location/util/identity/CallerIdentity.java +++ b/location/java/android/location/util/identity/CallerIdentity.java @@ -42,7 +42,16 @@ public final class CallerIdentity { @VisibleForTesting public static CallerIdentity forTest(int uid, int pid, String packageName, @Nullable String attributionTag) { - return new CallerIdentity(uid, pid, packageName, attributionTag, null); + return forTest(uid, pid, packageName, attributionTag, null); + } + + /** + * Construct a CallerIdentity for test purposes. + */ + @VisibleForTesting + public static CallerIdentity forTest(int uid, int pid, String packageName, + @Nullable String attributionTag, @Nullable String listenerId) { + return new CallerIdentity(uid, pid, packageName, attributionTag, listenerId); } /** @@ -145,7 +154,10 @@ public final class CallerIdentity { return mAttributionTag; } - /** The calling listener id. */ + /** + * The calling listener id. A null listener id will match any other listener id for the purposes + * of {@link #equals(Object)}. + */ public String getListenerId() { return mListenerId; } @@ -168,6 +180,17 @@ public final class CallerIdentity { } } + /** + * Returns a CallerIdentity corrosponding to this CallerIdentity but with a null listener id. + */ + public CallerIdentity stripListenerId() { + if (mListenerId == null) { + return this; + } else { + return new CallerIdentity(mUid, mPid, mPackageName, mAttributionTag, null); + } + } + @Override public String toString() { int length = 10 + mPackageName.length(); @@ -201,15 +224,12 @@ public final class CallerIdentity { return false; } CallerIdentity that = (CallerIdentity) o; - return equalsIgnoringListenerId(that) && Objects.equals(mListenerId, that.mListenerId); - } - - public boolean equalsIgnoringListenerId(CallerIdentity that) { - return that != null - && mUid == that.mUid + return mUid == that.mUid && mPid == that.mPid && mPackageName.equals(that.mPackageName) - && Objects.equals(mAttributionTag, that.mAttributionTag); + && Objects.equals(mAttributionTag, that.mAttributionTag) + && (mListenerId == null || that.mListenerId == null || mListenerId.equals( + that.mListenerId)); } @Override diff --git a/location/lib/Android.bp b/location/lib/Android.bp index c0188c0df497..696125ca586a 100644 --- a/location/lib/Android.bp +++ b/location/lib/Android.bp @@ -14,6 +14,15 @@ // 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_sdk_library { name: "com.android.location.provider", srcs: ["java/**/*.java"], diff --git a/lowpan/tests/Android.bp b/lowpan/tests/Android.bp index ad2bc27d1b39..590892926ec1 100644 --- a/lowpan/tests/Android.bp +++ b/lowpan/tests/Android.bp @@ -14,6 +14,15 @@ // Make test APK // ============================================================ +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"], +} + android_test { name: "FrameworksLowpanApiTests", srcs: ["**/*.java"], diff --git a/media/Android.bp b/media/Android.bp index 8b06bb26fcf7..9268b22a929a 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -1,3 +1,12 @@ +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"], +} + aidl_interface { name: "audio_common-aidl", unstable: true, diff --git a/media/java/Android.bp b/media/java/Android.bp index 0810699abf0a..aea63a073e95 100644 --- a/media/java/Android.bp +++ b/media/java/Android.bp @@ -1,3 +1,12 @@ +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: "IMidiDeviceServer.aidl", srcs: ["android/media/midi/IMidiDeviceServer.aidl"], diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index bb1dbd436e0b..7e729d8c371f 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -1264,6 +1264,8 @@ public final class AudioAttributes implements Parcelable { * * @param usage one of the {@link AudioAttributes} usage constants * @return string representing the {@link AudioAttributes} usage constant passed as a parameter + * + * @hide */ @NonNull public static String usageToString(@AttributeSdkUsage int usage) { diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index 205c1f4b4057..383c93d7716d 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -16,8 +16,10 @@ package android.media; +import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.util.SparseIntArray; import java.lang.annotation.Retention; @@ -161,6 +163,14 @@ public final class AudioDeviceInfo { */ public static final int TYPE_BLE_SPEAKER = 27; + /** + * A device type describing an Echo Canceller loopback Reference. + * This device is only used when capturing with MediaRecorder.AudioSource.ECHO_REFERENCE, + * which requires privileged permission + * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT}. + * @hide */ + @RequiresPermission(Manifest.permission.CAPTURE_AUDIO_OUTPUT) + public static final int TYPE_ECHO_REFERENCE = 28; /** @hide */ @IntDef(flag = false, prefix = "TYPE", value = { @@ -188,7 +198,8 @@ public final class AudioDeviceInfo { TYPE_FM_TUNER, TYPE_TV_TUNER, TYPE_BLE_HEADSET, - TYPE_BLE_SPEAKER} + TYPE_BLE_SPEAKER, + TYPE_ECHO_REFERENCE} ) @Retention(RetentionPolicy.SOURCE) public @interface AudioDeviceType {} @@ -211,7 +222,8 @@ public final class AudioDeviceInfo { TYPE_LINE_DIGITAL, TYPE_IP, TYPE_BUS, - TYPE_BLE_HEADSET} + TYPE_BLE_HEADSET, + TYPE_ECHO_REFERENCE} ) @Retention(RetentionPolicy.SOURCE) public @interface AudioDeviceTypeIn {} @@ -297,6 +309,7 @@ public final class AudioDeviceInfo { case TYPE_BUS: case TYPE_REMOTE_SUBMIX: case TYPE_BLE_HEADSET: + case TYPE_ECHO_REFERENCE: return true; default: return false; @@ -621,6 +634,8 @@ public final class AudioDeviceInfo { INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUS, TYPE_BUS); INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_REMOTE_SUBMIX, TYPE_REMOTE_SUBMIX); INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLE_HEADSET, TYPE_BLE_HEADSET); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_ECHO_REFERENCE, TYPE_ECHO_REFERENCE); + // privileges mapping to output device EXT_TO_INT_DEVICE_MAPPING = new SparseIntArray(); @@ -678,6 +693,9 @@ public final class AudioDeviceInfo { EXT_TO_INT_INPUT_DEVICE_MAPPING.put( TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_IN_REMOTE_SUBMIX); EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_IN_BLE_HEADSET); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put( + TYPE_ECHO_REFERENCE, AudioSystem.DEVICE_IN_ECHO_REFERENCE); + } } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index d896c1fc82b5..b2de49deefca 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -568,7 +568,7 @@ public class AudioManager { public static final int FLAG_FROM_KEY = 1 << 12; /** @hide */ - @IntDef(flag = false, prefix = "FLAG", value = { + @IntDef(flag = true, prefix = "FLAG", value = { FLAG_SHOW_UI, FLAG_ALLOW_RINGER_MODES, FLAG_PLAY_SOUND, @@ -3123,52 +3123,57 @@ public class AudioManager { /** * @hide Home sound - * Played by the framework when the home app becomes active if config_enableHomeSound is set to - * true. This is currently only used on TV devices. + * <p> + * To be played by the framework when the home app becomes active if config_enableHomeSound is + * set to true. This is currently only used on TV devices. * Note that this sound is only available if a sound file is specified in audio_assets.xml. * @see #playSoundEffect(int) */ public static final int FX_HOME = 11; /** - * @hide Fast scroll sound 1 - * To be by the framework when a fast-scrolling is performed and - * {@link #areFastScrollSoundEffectsEnabled()} is true. + * @hide Navigation repeat sound 1 + * <p> + * To be played by the framework when a focus navigation is repeatedly triggered + * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true. * This is currently only used on TV devices. * Note that this sound is only available if a sound file is specified in audio_assets.xml * @see #playSoundEffect(int) */ - public static final int FX_FAST_SCROLL_1 = 12; + public static final int FX_FOCUS_NAVIGATION_REPEAT_1 = 12; /** - * @hide Fast scroll sound 2 - * To be by the framework when a fast-scrolling is performed and - * {@link #areFastScrollSoundEffectsEnabled()} is true. + * @hide Navigation repeat sound 2 + * <p> + * To be played by the framework when a focus navigation is repeatedly triggered + * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true. * This is currently only used on TV devices. * Note that this sound is only available if a sound file is specified in audio_assets.xml * @see #playSoundEffect(int) */ - public static final int FX_FAST_SCROLL_2 = 13; + public static final int FX_FOCUS_NAVIGATION_REPEAT_2 = 13; /** - * @hide Fast scroll sound 3 - * To be by the framework when a fast-scrolling is performed and - * {@link #areFastScrollSoundEffectsEnabled()} is true. + * @hide Navigation repeat sound 3 + * <p> + * To be played by the framework when a focus navigation is repeatedly triggered + * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true. * This is currently only used on TV devices. * Note that this sound is only available if a sound file is specified in audio_assets.xml * @see #playSoundEffect(int) */ - public static final int FX_FAST_SCROLL_3 = 14; + public static final int FX_FOCUS_NAVIGATION_REPEAT_3 = 14; /** - * @hide Fast scroll sound 4 - * To be by the framework when a fast-scrolling is performed and - * {@link #areFastScrollSoundEffectsEnabled()} is true. + * @hide Navigation repeat sound 4 + * <p> + * To be played by the framework when a focus navigation is repeatedly triggered + * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true. * This is currently only used on TV devices. * Note that this sound is only available if a sound file is specified in audio_assets.xml * @see #playSoundEffect(int) */ - public static final int FX_FAST_SCROLL_4 = 15; + public static final int FX_FOCUS_NAVIGATION_REPEAT_4 = 15; /** * @hide Number of sound effects @@ -3177,27 +3182,27 @@ public class AudioManager { public static final int NUM_SOUND_EFFECTS = 16; /** - * @hide Number of fast scroll sound effects + * @hide Number of FX_FOCUS_NAVIGATION_REPEAT_* sound effects */ - public static final int NUM_FAST_SCROLL_SOUND_EFFECTS = 4; + public static final int NUM_NAVIGATION_REPEAT_SOUND_EFFECTS = 4; /** * @hide - * @param n a value in [0, {@link #NUM_FAST_SCROLL_SOUND_EFFECTS}[ - * @return The id of a fast scroll sound effect or -1 if out of bounds + * @param n a value in [0, {@link #NUM_NAVIGATION_REPEAT_SOUND_EFFECTS}[ + * @return The id of a navigation repeat sound effect or -1 if out of bounds */ - public static int getNthFastScrollSoundEffectId(int n) { + public static int getNthNavigationRepeatSoundEffect(int n) { switch (n) { case 0: - return FX_FAST_SCROLL_1; + return FX_FOCUS_NAVIGATION_REPEAT_1; case 1: - return FX_FAST_SCROLL_2; + return FX_FOCUS_NAVIGATION_REPEAT_2; case 2: - return FX_FAST_SCROLL_3; + return FX_FOCUS_NAVIGATION_REPEAT_3; case 3: - return FX_FAST_SCROLL_4; + return FX_FOCUS_NAVIGATION_REPEAT_4; default: - Log.w(TAG, "Invalid fast-scroll sound effect id: " + n); + Log.w(TAG, "Invalid navigation repeat sound effect id: " + n); return -1; } } @@ -3205,9 +3210,9 @@ public class AudioManager { /** * @hide */ - public void setFastScrollSoundEffectsEnabled(boolean enabled) { + public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) { try { - getService().setFastScrollSoundEffectsEnabled(enabled); + getService().setNavigationRepeatSoundEffectsEnabled(enabled); } catch (RemoteException e) { } @@ -3215,11 +3220,11 @@ public class AudioManager { /** * @hide - * @return true if the fast scroll sound effects are enabled + * @return true if the navigation repeat sound effects are enabled */ - public boolean areFastScrollSoundEffectsEnabled() { + public boolean areNavigationRepeatSoundEffectsEnabled() { try { - return getService().areFastScrollSoundEffectsEnabled(); + return getService().areNavigationRepeatSoundEffectsEnabled(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index b19643761fd7..bf04b660425b 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -275,6 +275,12 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, private AudioAttributes mAudioAttributes; private boolean mIsSubmixFullVolume = false; + /** + * The log session id used for metrics. + * A null or empty string here means it is not set. + */ + private String mLogSessionId; + //--------------------------------------------------------- // Constructor, Finalize //-------------------- @@ -1913,6 +1919,25 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, return native_set_preferred_microphone_field_dimension(zoom) == AudioSystem.SUCCESS; } + /** + * Sets a string handle to this AudioRecord for metrics collection. + * + * @param logSessionId a string which is used to identify this object + * to the metrics service. Proper generated Ids must be obtained + * from the Java metrics service and should be considered opaque. + * Use null to remove the logSessionId association. + * @throws IllegalStateException if AudioRecord not initialized. + * + * @hide + */ + public void setLogSessionId(@Nullable String logSessionId) { + if (mState == STATE_UNINITIALIZED) { + throw new IllegalStateException("AudioRecord not initialized"); + } + native_setLogSessionId(logSessionId); + mLogSessionId = logSessionId; + } + //--------------------------------------------------------- // Interface definitions //-------------------- @@ -2072,6 +2097,8 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, private native int native_set_preferred_microphone_direction(int direction); private native int native_set_preferred_microphone_field_dimension(float zoom); + private native void native_setLogSessionId(@Nullable String logSessionId); + //--------------------------------------------------------- // Utility methods //------------------ diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 7fb83f17a9d4..88731d25706c 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -565,6 +565,12 @@ public class AudioTrack extends PlayerBase */ private int mOffloadPaddingFrames = 0; + /** + * The log session id used for metrics. + * A null or empty string here means it is not set. + */ + private String mLogSessionId; + //-------------------------------- // Used exclusively by native code //-------------------- @@ -837,6 +843,7 @@ public class AudioTrack extends PlayerBase } baseRegisterPlayer(mSessionId); + native_setPlayerIId(mPlayerIId); // mPlayerIId now ready to send to native AudioTrack. } /** @@ -922,12 +929,21 @@ public class AudioTrack extends PlayerBase private final int mSyncId; /** + * A special content id for {@link #TunerConfiguration(int, int)} + * indicating audio is delivered + * from an {@code AudioTrack} write, not tunneled from the tuner stack. + */ + public static final int CONTENT_ID_NONE = 0; + + /** * Constructs a TunerConfiguration instance for use in {@link AudioTrack.Builder} * * @param contentId selects the audio stream to use. * The contentId may be obtained from - * {@link android.media.tv.tuner.filter.Filter#getId()}. - * This is always a positive number. + * {@link android.media.tv.tuner.filter.Filter#getId()}, + * such obtained id is always a positive number. + * If audio is to be delivered through an {@code AudioTrack} write + * then {@code CONTENT_ID_NONE} may be used. * @param syncId selects the clock to use for synchronization * of audio with other streams such as video. * The syncId may be obtained from @@ -936,10 +952,10 @@ public class AudioTrack extends PlayerBase */ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public TunerConfiguration( - @IntRange(from = 1) int contentId, @IntRange(from = 1)int syncId) { - if (contentId < 1) { + @IntRange(from = 0) int contentId, @IntRange(from = 1)int syncId) { + if (contentId < 0) { throw new IllegalArgumentException( - "contentId " + contentId + " must be positive"); + "contentId " + contentId + " must be positive or CONTENT_ID_NONE"); } if (syncId < 1) { throw new IllegalArgumentException("syncId " + syncId + " must be positive"); @@ -3520,8 +3536,9 @@ public class AudioTrack extends PlayerBase native_enableDeviceCallback(); return true; } catch (IllegalStateException e) { - // Fail silently as track state could have changed in between start - // and enabling routing callback, return false to indicate not enabled + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "testEnableNativeRoutingCallbacks failed", e); + } } } return false; @@ -3571,7 +3588,7 @@ public class AudioTrack extends PlayerBase Handler handler) { synchronized (mRoutingChangeListeners) { if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { - testEnableNativeRoutingCallbacksLocked(); + mEnableSelfRoutingMonitor = testEnableNativeRoutingCallbacksLocked(); mRoutingChangeListeners.put( listener, new NativeRoutingEventHandlerDelegate(this, listener, handler != null ? handler : new Handler(mInitializationLooper))); @@ -3967,6 +3984,25 @@ public class AudioTrack extends PlayerBase } } + /** + * Sets a string handle to this AudioTrack for metrics collection. + * + * @param logSessionId a string which is used to identify this object + * to the metrics service. Proper generated Ids must be obtained + * from the Java metrics service and should be considered opaque. + * Use null to remove the logSessionId association. + * @throws IllegalStateException if AudioTrack not initialized. + * + * @hide + */ + public void setLogSessionId(@Nullable String logSessionId) { + if (mState == STATE_UNINITIALIZED) { + throw new IllegalStateException("track not initialized"); + } + native_setLogSessionId(logSessionId); + mLogSessionId = logSessionId; + } + //--------------------------------------------------------- // Inner classes //-------------------- @@ -4202,6 +4238,21 @@ public class AudioTrack extends PlayerBase private native int native_get_audio_description_mix_level_db(float[] level); private native int native_set_dual_mono_mode(int dualMonoMode); private native int native_get_dual_mono_mode(int[] dualMonoMode); + private native void native_setLogSessionId(@Nullable String logSessionId); + + /** + * Sets the audio service Player Interface Id. + * + * The playerIId does not change over the lifetime of the client + * Java AudioTrack and is set automatically on creation. + * + * This call informs the native AudioTrack for metrics logging purposes. + * + * @param id the value reported by AudioManager when registering the track. + * A value of -1 indicates invalid - the playerIId was never set. + * @throws IllegalStateException if AudioTrack not initialized. + */ + private native void native_setPlayerIId(int playerIId); //--------------------------------------------------------- // Utility methods diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 71ee57e3d471..0073b5cc93b9 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -351,9 +351,9 @@ interface IAudioService { oneway void unregisterCommunicationDeviceDispatcher( ICommunicationDeviceDispatcher dispatcher); - boolean areFastScrollSoundEffectsEnabled(); + boolean areNavigationRepeatSoundEffectsEnabled(); - oneway void setFastScrollSoundEffectsEnabled(boolean enabled); + oneway void setNavigationRepeatSoundEffectsEnabled(boolean enabled); boolean isHomeSoundEffectEnabled(); diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java index 44f8385b715e..9ab4aac891e5 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -131,7 +131,59 @@ public class ImageWriter implements AutoCloseable { */ public static @NonNull ImageWriter newInstance(@NonNull Surface surface, @IntRange(from = 1) int maxImages) { - return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN); + return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN, -1 /*width*/, + -1 /*height*/); + } + + /** + * <p> + * Create a new ImageWriter with given number of max Images, format and producer dimension. + * </p> + * <p> + * The {@code maxImages} parameter determines the maximum number of + * {@link Image} objects that can be be dequeued from the + * {@code ImageWriter} simultaneously. Requesting more buffers will use up + * more memory, so it is important to use only the minimum number necessary. + * </p> + * <p> + * The format specifies the image format of this ImageWriter. The format + * from the {@code surface} will be overridden with this format. For example, + * if the surface is obtained from a {@link android.graphics.SurfaceTexture}, the default + * format may be {@link PixelFormat#RGBA_8888}. If the application creates an ImageWriter + * with this surface and {@link ImageFormat#PRIVATE}, this ImageWriter will be able to operate + * with {@link ImageFormat#PRIVATE} Images. + * </p> + * <p> + * Note that the consumer end-point may or may not be able to support Images with different + * format, for such case, the application should only use this method if the consumer is able + * to consume such images. + * </p> + * <p> The input Image size can also be set by the client. </p> + * + * @param surface The destination Surface this writer produces Image data + * into. + * @param maxImages The maximum number of Images the user will want to + * access simultaneously for producing Image data. This should be + * as small as possible to limit memory use. Once maxImages + * Images are dequeued by the user, one of them has to be queued + * back before a new Image can be dequeued for access via + * {@link #dequeueInputImage()}. + * @param format The format of this ImageWriter. It can be any valid format specified by + * {@link ImageFormat} or {@link PixelFormat}. + * + * @param width Input size width. + * @param height Input size height. + * + * @return a new ImageWriter instance. + * + * @hide + */ + public static @NonNull ImageWriter newInstance(@NonNull Surface surface, + @IntRange(from = 1) int maxImages, @Format int format, int width, int height) { + if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { + throw new IllegalArgumentException("Invalid format is specified: " + format); + } + return new ImageWriter(surface, maxImages, format, width, height); } /** @@ -180,13 +232,13 @@ public class ImageWriter implements AutoCloseable { if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { throw new IllegalArgumentException("Invalid format is specified: " + format); } - return new ImageWriter(surface, maxImages, format); + return new ImageWriter(surface, maxImages, format, -1 /*width*/, -1 /*height*/); } /** * @hide */ - protected ImageWriter(Surface surface, int maxImages, int format) { + protected ImageWriter(Surface surface, int maxImages, int format, int width, int height) { if (surface == null || maxImages < 1) { throw new IllegalArgumentException("Illegal input argument: surface " + surface + ", maxImages: " + maxImages); @@ -196,7 +248,8 @@ public class ImageWriter implements AutoCloseable { // Note that the underlying BufferQueue is working in synchronous mode // to avoid dropping any buffers. - mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format); + mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format, width, + height); // nativeInit internally overrides UNKNOWN format. So does surface format query after // nativeInit and before getEstimatedNativeAllocBytes(). @@ -919,7 +972,7 @@ public class ImageWriter implements AutoCloseable { // Native implemented ImageWriter methods. private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImgs, - int format); + int format, int width, int height); private synchronized native void nativeClose(long nativeCtx); diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 607c8f11e01a..cf31e4141a6d 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -2414,37 +2414,44 @@ final public class MediaCodec implements PlaybackComponent { * This indicates that the requested key was not found when trying to * perform a decrypt operation. The operation can be retried after adding * the correct decryption key. + * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_NO_KEY}. */ - public static final int ERROR_NO_KEY = 1; + public static final int ERROR_NO_KEY = MediaDrm.ErrorCodes.ERROR_NO_KEY; /** * This indicates that the key used for decryption is no longer * valid due to license term expiration. The operation can be retried * after updating the expired keys. + * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_KEY_EXPIRED}. */ - public static final int ERROR_KEY_EXPIRED = 2; + public static final int ERROR_KEY_EXPIRED = MediaDrm.ErrorCodes.ERROR_KEY_EXPIRED; /** * This indicates that a required crypto resource was not able to be * allocated while attempting the requested operation. The operation * can be retried if the app is able to release resources. + * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_RESOURCE_BUSY} */ - public static final int ERROR_RESOURCE_BUSY = 3; + public static final int ERROR_RESOURCE_BUSY = MediaDrm.ErrorCodes.ERROR_RESOURCE_BUSY; /** * This indicates that the output protection levels supported by the * device are not sufficient to meet the requirements set by the * content owner in the license policy. + * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_INSUFFICIENT_OUTPUT_PROTECTION} */ - public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; + public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = + MediaDrm.ErrorCodes.ERROR_INSUFFICIENT_OUTPUT_PROTECTION; /** * This indicates that decryption was attempted on a session that is * not opened, which could be due to a failure to open the session, * closing the session prematurely, or the session being reclaimed * by the resource manager. + * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_SESSION_NOT_OPENED} */ - public static final int ERROR_SESSION_NOT_OPENED = 5; + public static final int ERROR_SESSION_NOT_OPENED = + MediaDrm.ErrorCodes.ERROR_SESSION_NOT_OPENED; /** * This indicates that an operation was attempted that could not be @@ -2453,23 +2460,28 @@ final public class MediaCodec implements PlaybackComponent { * device security features that aren't supported by the device, * or due to an internal error in the crypto system that prevents * the specified security policy from being met. + * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_UNSUPPORTED_OPERATION} */ - public static final int ERROR_UNSUPPORTED_OPERATION = 6; + public static final int ERROR_UNSUPPORTED_OPERATION = + MediaDrm.ErrorCodes.ERROR_UNSUPPORTED_OPERATION; /** * This indicates that the security level of the device is not * sufficient to meet the requirements set by the content owner * in the license policy. + * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_INSUFFICIENT_SECURITY} */ - public static final int ERROR_INSUFFICIENT_SECURITY = 7; + public static final int ERROR_INSUFFICIENT_SECURITY = + MediaDrm.ErrorCodes.ERROR_INSUFFICIENT_SECURITY; /** * This indicates that the video frame being decrypted exceeds * the size of the device's protected output buffers. When * encountering this error the app should try playing content * of a lower resolution. + * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_FRAME_TOO_LARGE} */ - public static final int ERROR_FRAME_TOO_LARGE = 8; + public static final int ERROR_FRAME_TOO_LARGE = MediaDrm.ErrorCodes.ERROR_FRAME_TOO_LARGE; /** * This error indicates that session state has been @@ -2477,26 +2489,37 @@ final public class MediaCodec implements PlaybackComponent { * of retaining crypto session state across device * suspend/resume. The session must be closed and a new * session opened to resume operation. + * @deprecated Please use {@link MediaDrm.ErrorCodes#ERROR_LOST_STATE} */ - public static final int ERROR_LOST_STATE = 9; + public static final int ERROR_LOST_STATE = MediaDrm.ErrorCodes.ERROR_LOST_STATE; /** @hide */ @IntDef({ - ERROR_NO_KEY, - ERROR_KEY_EXPIRED, - ERROR_RESOURCE_BUSY, - ERROR_INSUFFICIENT_OUTPUT_PROTECTION, - ERROR_SESSION_NOT_OPENED, - ERROR_UNSUPPORTED_OPERATION, - ERROR_INSUFFICIENT_SECURITY, - ERROR_FRAME_TOO_LARGE, - ERROR_LOST_STATE + MediaDrm.ErrorCodes.ERROR_NO_KEY, + MediaDrm.ErrorCodes.ERROR_KEY_EXPIRED, + MediaDrm.ErrorCodes.ERROR_RESOURCE_BUSY, + MediaDrm.ErrorCodes.ERROR_INSUFFICIENT_OUTPUT_PROTECTION, + MediaDrm.ErrorCodes.ERROR_SESSION_NOT_OPENED, + MediaDrm.ErrorCodes.ERROR_UNSUPPORTED_OPERATION, + MediaDrm.ErrorCodes.ERROR_INSUFFICIENT_SECURITY, + MediaDrm.ErrorCodes.ERROR_FRAME_TOO_LARGE, + MediaDrm.ErrorCodes.ERROR_LOST_STATE, + MediaDrm.ErrorCodes.ERROR_GENERIC_OEM, + MediaDrm.ErrorCodes.ERROR_GENERIC_PLUGIN, + MediaDrm.ErrorCodes.ERROR_LICENSE_PARSE, + MediaDrm.ErrorCodes.ERROR_MEDIA_FRAMEWORK, + MediaDrm.ErrorCodes.ERROR_ZERO_SUBSAMPLES }) @Retention(RetentionPolicy.SOURCE) public @interface CryptoErrorCode {} /** - * Retrieve the error code associated with a CryptoException + * Returns error code associated with this {@link CryptoException}. + * <p> + * Please refer to {@link MediaDrm.ErrorCodes} for the general error + * handling strategy and details about each possible return value. + * + * @return an error code defined in {@link MediaDrm.ErrorCodes}. */ @CryptoErrorCode public int getErrorCode() { diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 4d7ed1145990..06d0eb0b5423 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -19,6 +19,7 @@ package android.media; import static android.media.Utils.intersectSortedDistinctRanges; import static android.media.Utils.sortDistinctRanges; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -1133,6 +1134,7 @@ public final class MediaCodecInfo { * in the ranges returned by {@link #getInputChannelCountRanges} * */ + @IntRange(from = 1, to = 255) public int getMaxInputChannelCount() { int overall_max = 0; for (int i = mInputChannelRanges.length - 1; i >= 0; i--) { @@ -1151,6 +1153,7 @@ public final class MediaCodecInfo { * This returns the lowest channel count in the ranges returned by * {@link #getInputChannelCountRanges}. */ + @IntRange(from = 1, to = 255) public int getMinInputChannelCount() { int overall_min = MAX_INPUT_CHANNEL_COUNT; for (int i = mInputChannelRanges.length - 1; i >= 0; i--) { diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index adb8a54c0167..f7467a636024 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -23,7 +23,11 @@ import android.annotation.Nullable; import android.annotation.StringDef; import android.annotation.TestApi; import android.app.ActivityThread; +import android.app.Application; import android.compat.annotation.UnsupportedAppUsage; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; import android.media.metrics.PlaybackComponent; import android.os.Handler; import android.os.HandlerExecutor; @@ -39,7 +43,10 @@ import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.time.Instant; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Base64; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; @@ -144,6 +151,7 @@ public final class MediaDrm implements AutoCloseable { private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES; private long mNativeContext; + private final String mAppPackageName; /** * Specify no certificate type @@ -281,16 +289,371 @@ public final class MediaDrm implements AutoCloseable { /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. */ + mAppPackageName = ActivityThread.currentOpPackageName(); native_setup(new WeakReference<MediaDrm>(this), - getByteArrayFromUUID(uuid), ActivityThread.currentOpPackageName()); + getByteArrayFromUUID(uuid), mAppPackageName); mCloseGuard.open("release"); } /** - * Thrown when an unrecoverable failure occurs during a MediaDrm operation. - * Extends java.lang.IllegalStateException with the addition of an error + * Error codes that may be returned from {@link + * MediaDrmStateException#getErrorCode()} and {@link + * MediaCodec.CryptoException#getErrorCode()} + * <p> + * The description of each error code includes steps that may be taken to + * resolve the error condition. For some errors however, a recovery action + * cannot be predetermined. The description of those codes refers to a + * general strategy for handling the error condition programmatically, which + * is to try the following in listed order until successful: + * <ol> + * <li> retry the operation </li> + * <li> if the operation is related to a session, {@link + * #closeSession(byte[]) close} the session, {@link #openSession() open} a + * new session, and retry the operation </li> + * <li> {@link #close() close} the {@link MediaDrm} instance and any other + * related components such as the {@link MediaCodec codec} and retry + * playback, or </li> + * <li> try using a different configuration of the {@link MediaDrm} plugin, + * such as a different {@link #openSession(int) security level}. </li> + * </ol> + * <p> + * If the problem still persists after all the aforementioned steps, please + * report the failure to the {@link MediaDrm} plugin vendor along with the + * {@link LogMessage log messages} returned by {@link + * MediaDrm#getLogMessages()}, and a bugreport if possible. + */ + public final static class ErrorCodes { + private ErrorCodes() {} + + /** + * ERROR_UNKNOWN is used where no other defined error code is applicable + * to the current failure. + * <p> + * Please see the general error handling strategy for unexpected errors + * described in {@link ErrorCodes}. + */ + public static final int ERROR_UNKNOWN = 0; + + /** + * The requested key was not found when trying to perform a decrypt + * operation. + * <p> + * The operation can be retried after adding the correct decryption key. + */ + public static final int ERROR_NO_KEY = 1; + + /** + * The key used for decryption is no longer valid due to license term + * expiration. + * <p> + * The operation can be retried after updating the expired keys. + */ + public static final int ERROR_KEY_EXPIRED = 2; + + /** + * A required crypto resource was not able to be allocated while + * attempting the requested operation. + * <p> + * The operation can be retried if the app is able to release resources. + */ + public static final int ERROR_RESOURCE_BUSY = 3; + + /** + * The output protection levels supported by the device are not + * sufficient to meet the requirements set by the content owner in the + * license policy. + */ + public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; + + /** + * Decryption was attempted on a session that is not opened, which could + * be due to a failure to open the session, closing the session + * prematurely, the session being reclaimed by the resource manager, or + * a non-existent session id. + */ + public static final int ERROR_SESSION_NOT_OPENED = 5; + + /** + * An operation was attempted that could not be supported by the crypto + * system of the device in its current configuration. + * <p> + * This may occur when the license policy requires device security + * features that aren't supported by the device, or due to an internal + * error in the crypto system that prevents the specified security + * policy from being met. + */ + public static final int ERROR_UNSUPPORTED_OPERATION = 6; + + /** + * The security level of the device is not sufficient to meet the + * requirements set by the content owner in the license policy. + */ + public static final int ERROR_INSUFFICIENT_SECURITY = 7; + + /** + * The video frame being decrypted exceeds the size of the device's + * protected output buffers. + * <p> + * When encountering this error the app should try playing content + * of a lower resolution or skipping the problematic frame. + */ + public static final int ERROR_FRAME_TOO_LARGE = 8; + + /** + * The session state has been invalidated. This can occur on devices + * that are not capable of retaining crypto session state across device + * suspend/resume. + * <p> + * The session must be closed and a new session opened to resume + * operation. + */ + public static final int ERROR_LOST_STATE = 9; + + /** + * Certificate is malformed or is of the wrong type. + * <p> + * Ensure the certificate provided by the app or returned from the + * license server is valid. Check with the {@link MediaDrm} plugin + * vendor for the expected certificate format. + */ + public static final int ERROR_CERTIFICATE_MALFORMED = 10; + + /** + * Certificate has not been set. + * <p> + * Ensure the certificate has been provided by the app. Check with the + * {@link MediaDrm} plugin vendor for the expected method to provide + * {@link MediaDrm} a certificate. + */ + public static final int ERROR_CERTIFICATE_MISSING = 11; + + /** + * An error happened within the crypto library used by the drm plugin. + */ + public static final int ERROR_CRYPTO_LIBRARY = 12; + + /** + * Unexpected error reported by the device OEM subsystem. + * <p> + * Please see the general error handling strategy for unexpected errors + * described in {@link ErrorCodes}. + */ + public static final int ERROR_GENERIC_OEM = 13; + + /** + * Unexpected internal failure in {@link MediaDrm}/{@link MediaCrypto}. + * <p> + * Please see the general error handling strategy for unexpected errors + * described in {@link ErrorCodes}. + */ + public static final int ERROR_GENERIC_PLUGIN = 14; + + /** + * The init data parameter passed to {@link MediaDrm#getKeyRequest} is + * empty or invalid. + * <p> + * Init data is typically obtained from {@link + * MediaExtractor#getPsshInfo()} or {@link + * MediaExtractor#getDrmInitData()}. Check with the {@link MediaDrm} + * plugin vendor for the expected init data format. + */ + public static final int ERROR_INIT_DATA = 15; + + /** + * Either the key was not loaded from the license before attempting the + * operation, or the key ID parameter provided by the app is incorrect. + * <p> + * Ensure the proper keys are in the license, and check the key ID + * parameter provided by the app is correct. Check with the {@link + * MediaDrm} plugin vendor for the expected license format. + */ + public static final int ERROR_KEY_NOT_LOADED = 16; + + /** + * The license response was empty, fields are missing or otherwise + * unable to be parsed or decrypted. + * <p> + * Check for mistakes such as empty or overwritten buffers. Otherwise, + * check with the {@link MediaDrm} plugin vendor for the expected + * license format. + */ + public static final int ERROR_LICENSE_PARSE = 17; + + /** + * The operation (e.g. to renew or persist a license) is prohibited by + * the license policy. + * <p> + * Check the license policy configuration on the license server. + */ + public static final int ERROR_LICENSE_POLICY = 18; + + /** + * Failed to generate a release request because a field in the offline + * license is empty or malformed. + * <p> + * The license can't be released on the server, but the app may remove + * the offline license explicitly using {@link + * MediaDrm#removeOfflineLicense}. + */ + public static final int ERROR_LICENSE_RELEASE = 19; + + /** + * The license server detected an error in the license request. + * <p> + * Check for errors on the license server. + */ + public static final int ERROR_LICENSE_REQUEST_REJECTED = 20; + + /** + * Failed to restore an offline license because a field in the offline + * license is empty or malformed. + * <p> + * Try requesting the license again if the device is online. + */ + public static final int ERROR_LICENSE_RESTORE = 21; + + /** + * Offline license is in an invalid state for the attempted operation. + * <p> + * Check the sequence of API calls made that can affect offline license + * state. For example, this could happen when the app attempts to + * restore a license after it has been released. + */ + public static final int ERROR_LICENSE_STATE = 22; + + /** + * Failure in the media framework. + * <p> + * Try releasing media resources (e.g. {@link MediaCodec}, {@link + * MediaDrm}), and restarting playback. + */ + public static final int ERROR_MEDIA_FRAMEWORK = 23; + + /** + * Error loading the provisioned certificate. + * <p> + * Re-provisioning may resolve the problem; check with the {@link + * MediaDrm} plugin vendor for re-provisioning instructions. Otherwise, + * using a different security level may resolve the issue. + */ + public static final int ERROR_PROVISIONING_CERTIFICATE = 24; + + /** + * Required steps were not performed before provisioning was attempted. + * <p> + * Ask the {@link MediaDrm} plugin vendor for situations where this + * error may occur. + */ + public static final int ERROR_PROVISIONING_CONFIG = 25; + + /** + * The provisioning response was empty, fields are missing or otherwise + * unable to be parsed. + * <p> + * Check for mistakes such as empty or overwritten buffers. Otherwise, + * check with the {@link MediaDrm} plugin vendor for the expected + * provisioning response format. + */ + public static final int ERROR_PROVISIONING_PARSE = 26; + + /** + * Provisioning failed in a way that is likely to succeed on a + * subsequent attempt. + * <p> + * The app should retry the operation. + */ + public static final int ERROR_PROVISIONING_RETRY = 27; + + /** + * This indicates that apps using MediaDrm sessions are + * temporarily exceeding the capacity of available crypto + * resources. + * <p> + * The app should retry the operation later. + */ + public static final int ERROR_RESOURCE_CONTENTION = 28; + + /** + * Failed to generate a secure stop request because a field in the + * stored license is empty or malformed. + * <p> + * The secure stop can't be released on the server, but the app may + * remove it explicitly using {@link MediaDrm#removeSecureStop}. + */ + public static final int ERROR_SECURE_STOP_RELEASE = 29; + + /** + * The plugin was unable to read data from the filesystem. + * <p> + * Please see the general error handling strategy for unexpected errors + * described in {@link ErrorCodes}. + */ + public static final int ERROR_STORAGE_READ = 30; + + /** + * The plugin was unable to write data to the filesystem. + * <p> + * Please see the general error handling strategy for unexpected errors + * described in {@link ErrorCodes}. + */ + public static final int ERROR_STORAGE_WRITE = 31; + + /** + * {@link MediaCodec#queueSecureInputBuffer} called with 0 subsamples. + * <p> + * Check the {@link MediaCodec.CryptoInfo} object passed to {@link + * MediaCodec#queueSecureInputBuffer}. + */ + public static final int ERROR_ZERO_SUBSAMPLES = 32; + + } + + /** @hide */ + @IntDef({ + ErrorCodes.ERROR_NO_KEY, + ErrorCodes.ERROR_KEY_EXPIRED, + ErrorCodes.ERROR_RESOURCE_BUSY, + ErrorCodes.ERROR_INSUFFICIENT_OUTPUT_PROTECTION, + ErrorCodes.ERROR_SESSION_NOT_OPENED, + ErrorCodes.ERROR_UNSUPPORTED_OPERATION, + ErrorCodes.ERROR_INSUFFICIENT_SECURITY, + ErrorCodes.ERROR_FRAME_TOO_LARGE, + ErrorCodes.ERROR_LOST_STATE, + ErrorCodes.ERROR_CERTIFICATE_MALFORMED, + ErrorCodes.ERROR_CERTIFICATE_MISSING, + ErrorCodes.ERROR_CRYPTO_LIBRARY, + ErrorCodes.ERROR_GENERIC_OEM, + ErrorCodes.ERROR_GENERIC_PLUGIN, + ErrorCodes.ERROR_INIT_DATA, + ErrorCodes.ERROR_KEY_NOT_LOADED, + ErrorCodes.ERROR_LICENSE_PARSE, + ErrorCodes.ERROR_LICENSE_POLICY, + ErrorCodes.ERROR_LICENSE_RELEASE, + ErrorCodes.ERROR_LICENSE_REQUEST_REJECTED, + ErrorCodes.ERROR_LICENSE_RESTORE, + ErrorCodes.ERROR_LICENSE_STATE, + ErrorCodes.ERROR_MEDIA_FRAMEWORK, + ErrorCodes.ERROR_PROVISIONING_CERTIFICATE, + ErrorCodes.ERROR_PROVISIONING_CONFIG, + ErrorCodes.ERROR_PROVISIONING_PARSE, + ErrorCodes.ERROR_PROVISIONING_RETRY, + ErrorCodes.ERROR_SECURE_STOP_RELEASE, + ErrorCodes.ERROR_STORAGE_READ, + ErrorCodes.ERROR_STORAGE_WRITE, + ErrorCodes.ERROR_ZERO_SUBSAMPLES + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MediaDrmErrorCode {} + + /** + * Thrown when a general failure occurs during a MediaDrm operation. + * Extends {@link IllegalStateException} with the addition of an error * code that may be useful in diagnosing the failure. + * <p> + * Please refer to {@link ErrorCodes} for the general error handling + * strategy and details about each possible return value from {@link + * MediaDrmStateException#getErrorCode()}. */ public static final class MediaDrmStateException extends java.lang.IllegalStateException { private final int mErrorCode; @@ -311,15 +674,30 @@ public final class MediaDrm implements AutoCloseable { } /** - * Retrieve the associated error code + * Returns error code associated with this {@link + * MediaDrmStateException}. + * <p> + * Please refer to {@link ErrorCodes} for the general error handling + * strategy and details about each possible return value. * - * @hide + * @return an error code defined in {@link MediaDrm.ErrorCodes}. */ + @MediaDrmErrorCode public int getErrorCode() { return mErrorCode; } /** + * Returns true if the {@link MediaDrmStateException} is a transient + * issue, perhaps due to resource constraints, and that the operation + * (e.g. provisioning) may succeed on a subsequent attempt. + */ + public boolean isTransient() { + return mErrorCode == ErrorCodes.ERROR_PROVISIONING_RETRY + || mErrorCode == ErrorCodes.ERROR_RESOURCE_CONTENTION; + } + + /** * Retrieve a developer-readable diagnostic information string * associated with the exception. Do not show this to end-users, * since this string will not be localized or generally comprehensible @@ -332,7 +710,13 @@ public final class MediaDrm implements AutoCloseable { } /** - * Thrown when an error occurs in any method that has a session context. + * {@link SessionException} is a misnomer because it may occur in methods + * <b>without</b> a session context. + * <p> + * A {@link SessionException} is most likely to be thrown when an operation + * failed in a way that is likely to succeed on a subsequent attempt; call + * {@link #isTransient()} to determine whether the app should retry the + * failing operation. */ public static final class SessionException extends RuntimeException { public SessionException(int errorCode, @Nullable String detailMessage) { @@ -342,6 +726,7 @@ public final class MediaDrm implements AutoCloseable { /** * The SessionException has an unknown error code. + * @deprecated Unused. */ public static final int ERROR_UNKNOWN = 0; @@ -349,6 +734,10 @@ public final class MediaDrm implements AutoCloseable { * This indicates that apps using MediaDrm sessions are * temporarily exceeding the capacity of available crypto * resources. The app should retry the operation later. + * + * @deprecated Please use {@link #isTransient()} instead of comparing + * the return value of {@link #getErrorCode()} against + * {@link SessionException#ERROR_RESOURCE_CONTENTION}. */ public static final int ERROR_RESOURCE_CONTENTION = 1; @@ -361,12 +750,26 @@ public final class MediaDrm implements AutoCloseable { /** * Retrieve the error code associated with the SessionException + * + * @deprecated Please use {@link #isTransient()} instead of comparing + * the return value of {@link #getErrorCode()} against + * {@link SessionException#ERROR_RESOURCE_CONTENTION}. */ @SessionErrorCode public int getErrorCode() { return mErrorCode; } + /** + * Returns true if the {@link SessionException} is a transient + * issue, perhaps due to resource constraints, and that the operation + * (e.g. provisioning, generating requests) may succeed on a subsequent + * attempt. + */ + public boolean isTransient() { + return mErrorCode == ERROR_RESOURCE_CONTENTION; + } + private final int mErrorCode; } @@ -1144,12 +1547,78 @@ public final class MediaDrm implements AutoCloseable { * problem with the certifcate */ @NonNull - public native KeyRequest getKeyRequest( + public KeyRequest getKeyRequest( @NonNull byte[] scope, @Nullable byte[] init, @Nullable String mimeType, @KeyType int keyType, @Nullable HashMap<String, String> optionalParameters) - throws NotProvisionedException; + throws NotProvisionedException { + HashMap<String, String> internalParams; + if (optionalParameters == null) { + internalParams = new HashMap<>(); + } else { + internalParams = new HashMap<>(optionalParameters); + } + byte[] rawBytes = getNewestAvailablePackageCertificateRawBytes(); + byte[] hashBytes = null; + if (rawBytes != null) { + hashBytes = getDigestBytes(rawBytes, "SHA-256"); + } + if (hashBytes != null) { + Base64.Encoder encoderB64 = Base64.getEncoder(); + String hashBytesB64 = encoderB64.encodeToString(hashBytes); + internalParams.put("package_certificate_hash_bytes", hashBytesB64); + } + return getKeyRequestNative(scope, init, mimeType, keyType, internalParams); + } + + @Nullable + private byte[] getNewestAvailablePackageCertificateRawBytes() { + Application application = ActivityThread.currentApplication(); + if (application == null) { + Log.w(TAG, "pkg cert: Application is null"); + return null; + } + PackageManager pm = application.getPackageManager(); + if (pm == null) { + Log.w(TAG, "pkg cert: PackageManager is null"); + return null; + } + PackageInfo packageInfo = null; + try { + packageInfo = pm.getPackageInfo(mAppPackageName, + PackageManager.GET_SIGNING_CERTIFICATES); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, mAppPackageName, e); + } + if (packageInfo == null || packageInfo.signingInfo == null) { + Log.w(TAG, "pkg cert: PackageInfo or SigningInfo is null"); + return null; + } + Signature[] signers = packageInfo.signingInfo.getApkContentsSigners(); + if (signers != null && signers.length == 1) { + return signers[0].toByteArray(); + } + Log.w(TAG, "pkg cert: " + signers.length + " signers"); + return null; + } + + @Nullable + private static byte[] getDigestBytes(@NonNull byte[] rawBytes, @NonNull String algorithm) { + try { + MessageDigest messageDigest = MessageDigest.getInstance(algorithm); + return messageDigest.digest(rawBytes); + } catch (NoSuchAlgorithmException e) { + Log.w(TAG, algorithm, e); + } + return null; + } + @NonNull + private native KeyRequest getKeyRequestNative( + @NonNull byte[] scope, @Nullable byte[] init, + @Nullable String mimeType, @KeyType int keyType, + @Nullable HashMap<String, String> optionalParameters) + throws NotProvisionedException; /** * A key response is received from the license server by the app, then it is diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 67f1660fff78..f8a642a68dd7 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -980,6 +980,74 @@ public final class MediaFormat { public static final String KEY_BITRATE_MODE = "bitrate-mode"; /** + * A key describing the maximum Quantization Parameter allowed for encoding video. + * This key applies to all three video frame types (I, P, and B). This value fills + * in for any of the frame-specific #KEY_VIDEO_QP_I_MAX, #KEY_VIDEO_QP_P_MAX, or + * #KEY_VIDEO_QP_B_MAX keys that are not specified + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_MAX = "video-qp-max"; + + /** + * A key describing the maximum Quantization Parameter allowed for encoding video. + * This key applies to all three video frame types (I, P, and B). This value fills + * in for any of the frame-specific #KEY_VIDEO_QP_I_MIN, #KEY_VIDEO_QP_P_MIN, or + * #KEY_VIDEO_QP_B_MIN keys that are not specified + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_MIN = "video-qp-min"; + + /** + * A key describing the maximum Quantization Parameter allowed for encoding video. + * This value applies to video I-frames. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_I_MAX = "video-qp-i-max"; + + /** + * A key describing the minimum Quantization Parameter allowed for encoding video. + * This value applies to video I-frames. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_I_MIN = "video-qp-i-min"; + + /** + * A key describing the maximum Quantization Parameter allowed for encoding video. + * This value applies to video P-frames. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_P_MAX = "video-qp-p-max"; + + /** + * A key describing the minimum Quantization Parameter allowed for encoding video. + * This value applies to video P-frames. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_P_MIN = "video-qp-p-min"; + + /** + * A key describing the maximum Quantization Parameter allowed for encoding video. + * This value applies to video B-frames. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_B_MAX = "video-qp-b-max"; + + /** + * A key describing the minimum Quantization Parameter allowed for encoding video. + * This value applies to video B-frames. + * + * The associated value is an integer. + */ + public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min"; + + /** * A key describing the audio session ID of the AudioTrack associated * to a tunneled video codec. * The associated value is an integer. diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index c51c9dd06c24..9176dae8609f 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -19,6 +19,7 @@ package android.media; import static android.Manifest.permission.BIND_IMS_SERVICE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -91,6 +92,7 @@ import java.util.Scanner; import java.util.Set; import java.util.UUID; import java.util.Vector; +import java.util.concurrent.Executor; /** @@ -1358,6 +1360,7 @@ public class MediaPlayer extends PlayerBase private void startImpl() { baseStart(0); // unknown device at this point stayAwake(true); + tryToEnableNativeRoutingCallback(); _start(); } @@ -1383,6 +1386,7 @@ public class MediaPlayer extends PlayerBase stayAwake(false); _stop(); baseStop(); + tryToDisableNativeRoutingCallback(); } private native void _stop() throws IllegalStateException; @@ -1524,8 +1528,9 @@ public class MediaPlayer extends PlayerBase native_enableDeviceCallback(true); return true; } catch (IllegalStateException e) { - // Fail silently as media player state could have changed in between start - // and enabling routing callback, return false to indicate not enabled + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "testEnableNativeRoutingCallbacks failed", e); + } } } return false; @@ -1588,7 +1593,7 @@ public class MediaPlayer extends PlayerBase Handler handler) { synchronized (mRoutingChangeListeners) { if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { - testEnableNativeRoutingCallbacksLocked(); + mEnableSelfRoutingMonitor = testEnableNativeRoutingCallbacksLocked(); mRoutingChangeListeners.put( listener, new NativeRoutingEventHandlerDelegate(this, listener, handler != null ? handler : mEventHandler)); @@ -2172,7 +2177,7 @@ public class MediaPlayer extends PlayerBase mOnVideoSizeChangedListener = null; mOnTimedTextListener = null; mOnRtpRxNoticeListener = null; - mOnRtpRxNoticeHandler = null; + mOnRtpRxNoticeExecutor = null; synchronized (mTimeProviderLock) { if (mTimeProvider != null) { mTimeProvider.close(); @@ -3481,9 +3486,6 @@ public class MediaPlayer extends PlayerBase case MEDIA_STOPPED: { - tryToDisableNativeRoutingCallback(); - // FIXME see b/179218630 - //baseStop(); TimeProvider timeProvider = mTimeProvider; if (timeProvider != null) { timeProvider.onStopped(); @@ -3492,18 +3494,9 @@ public class MediaPlayer extends PlayerBase break; case MEDIA_STARTED: - { - // FIXME see b/179218630 - //baseStart(native_getRoutedDeviceId()); - tryToEnableNativeRoutingCallback(); - } // fall through case MEDIA_PAUSED: { - // FIXME see b/179218630 - //if (msg.what == MEDIA_PAUSED) { - // basePause(); - //} TimeProvider timeProvider = mTimeProvider; if (timeProvider != null) { timeProvider.onPaused(msg.what == MEDIA_PAUSED); @@ -3711,7 +3704,6 @@ public class MediaPlayer extends PlayerBase case MEDIA_RTP_RX_NOTICE: final OnRtpRxNoticeListener rtpRxNoticeListener = mOnRtpRxNoticeListener; - final Handler rtpRxNoticeHandler = mOnRtpRxNoticeHandler; if (rtpRxNoticeListener == null) { return; } @@ -3730,14 +3722,9 @@ public class MediaPlayer extends PlayerBase } finally { parcel.recycle(); } - if (rtpRxNoticeHandler == null) { - rtpRxNoticeListener.onRtpRxNotice(mMediaPlayer, noticeType, data); - } else { - rtpRxNoticeHandler.post( - () -> - rtpRxNoticeListener - .onRtpRxNotice(mMediaPlayer, noticeType, data)); - } + mOnRtpRxNoticeExecutor.execute(() -> + rtpRxNoticeListener + .onRtpRxNotice(mMediaPlayer, noticeType, data)); } return; @@ -4305,28 +4292,26 @@ public class MediaPlayer extends PlayerBase * * @see OnRtpRxNoticeListener * - * @param listener the listener called after a notice from RTP Rx - * @param handler the {@link Handler} that receives RTP Tx events. If null is passed, - * notifications will be posted on the thread that created this MediaPlayer - * instance. If the creating thread does not have a {@link Looper}, then - * notifications will be posted on the main thread. + * @param listener the listener called after a notice from RTP Rx. + * @param executor the {@link Executor} on which to post RTP Tx events. * @hide */ @SystemApi @RequiresPermission(BIND_IMS_SERVICE) public void setOnRtpRxNoticeListener( @NonNull Context context, - @NonNull OnRtpRxNoticeListener listener, @Nullable Handler handler) { + @NonNull @CallbackExecutor Executor executor, + @NonNull OnRtpRxNoticeListener listener) { Objects.requireNonNull(context); Preconditions.checkArgument( context.checkSelfPermission(BIND_IMS_SERVICE) == PERMISSION_GRANTED, BIND_IMS_SERVICE + " permission not granted."); mOnRtpRxNoticeListener = Objects.requireNonNull(listener); - mOnRtpRxNoticeHandler = handler; + mOnRtpRxNoticeExecutor = Objects.requireNonNull(executor); } private OnRtpRxNoticeListener mOnRtpRxNoticeListener; - private Handler mOnRtpRxNoticeHandler; + private Executor mOnRtpRxNoticeExecutor; /** * Register a callback to be invoked when a selected track has timed metadata available. diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 6cf99e288f1b..dc43ad342725 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -384,7 +384,12 @@ public class MediaRouter { } public Display[] getAllPresentationDisplays() { - return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION); + try { + return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION); + } catch (RuntimeException ex) { + Log.e(TAG, "Unable to get displays.", ex); + return null; + } } private void updatePresentationDisplays(int changedDisplayId) { diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java index 4407efad6052..5d0f0aa8a921 100644 --- a/media/java/android/media/PlayerBase.java +++ b/media/java/android/media/PlayerBase.java @@ -78,7 +78,7 @@ public abstract class PlayerBase { private final int mImplType; // uniquely identifies the Player Interface throughout the system (P I Id) - private int mPlayerIId = AudioPlaybackConfiguration.PLAYER_PIID_INVALID; + protected int mPlayerIId = AudioPlaybackConfiguration.PLAYER_PIID_INVALID; @GuardedBy("mLock") private int mState; diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index 66d579408db0..96bffee117ea 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -38,9 +38,7 @@ import android.view.KeyEvent; interface ISessionManager { ISession createSession(String packageName, in ISessionCallback sessionCb, String tag, in Bundle sessionInfo, int userId); - void notifySession2Created(in Session2Token sessionToken); List<MediaSession.Token> getSessions(in ComponentName compName, int userId); - ParceledListSlice getSession2Tokens(int userId); void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent, boolean needWakeLock); boolean dispatchMediaKeyEventToSessionAsSystemService(String packageName, @@ -73,8 +71,10 @@ interface ISessionManager { void setOnMediaKeyListener(in IOnMediaKeyListener listener); boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid); - void setCustomMediaKeyDispatcherForTesting(String name); - void setCustomSessionPolicyProviderForTesting(String name); + void setCustomMediaKeyDispatcher(String name); + void setCustomMediaSessionPolicyProvider(String name); + boolean hasCustomMediaKeyDispatcher(String componentName); + boolean hasCustomMediaSessionPolicyProvider(String componentName); int getSessionPolicies(in MediaSession.Token token); void setSessionPolicies(in MediaSession.Token token, int policies); } diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 13a3436569aa..98a13cfa6f3f 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -25,15 +25,16 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.ComponentName; import android.content.Context; -import android.content.pm.ParceledListSlice; import android.media.AudioManager; import android.media.IRemoteSessionCallback; +import android.media.MediaCommunicationManager; import android.media.MediaFrameworkPlatformInitializer; import android.media.MediaSession2; import android.media.Session2Token; import android.media.VolumeProvider; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; @@ -83,6 +84,7 @@ public final class MediaSessionManager { public static final int RESULT_MEDIA_KEY_HANDLED = 1; private final ISessionManager mService; + private final MediaCommunicationManager mCommunicationManager; private final OnMediaKeyEventDispatchedListenerStub mOnMediaKeyEventDispatchedListenerStub = new OnMediaKeyEventDispatchedListenerStub(); private final OnMediaKeyEventSessionChangedListenerStub @@ -127,6 +129,8 @@ public final class MediaSessionManager { .getMediaServiceManager() .getMediaSessionServiceRegisterer() .get()); + mCommunicationManager = (MediaCommunicationManager) context + .getSystemService(Context.MEDIA_COMMUNICATION_SERVICE); } /** @@ -163,17 +167,11 @@ public final class MediaSessionManager { * {@link MediaSession2.Builder} instead. * * @param token newly created session2 token + * @deprecated Don't use this method. A new media session is notified automatically. */ + @Deprecated public void notifySession2Created(@NonNull Session2Token token) { - Objects.requireNonNull(token, "token shouldn't be null"); - if (token.getType() != Session2Token.TYPE_SESSION) { - throw new IllegalArgumentException("token's type should be TYPE_SESSION"); - } - try { - mService.notifySession2Created(token); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } + // Does nothing } /** @@ -254,37 +252,7 @@ public final class MediaSessionManager { */ @NonNull public List<Session2Token> getSession2Tokens() { - return getSession2Tokens(UserHandle.myUserId()); - } - - /** - * Gets a list of {@link Session2Token} with type {@link Session2Token#TYPE_SESSION} for the - * given user. - * <p> - * The calling application needs to hold the - * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission in order to - * retrieve session tokens for user ids that do not belong to current process. - * - * @param userHandle The user handle to fetch sessions for. - * @return A list of {@link Session2Token} - * @hide - */ - @NonNull - @SuppressLint("UserHandle") - public List<Session2Token> getSession2Tokens(@NonNull UserHandle userHandle) { - Objects.requireNonNull(userHandle, "userHandle shouldn't be null"); - return getSession2Tokens(userHandle.getIdentifier()); - - } - - private List<Session2Token> getSession2Tokens(int userId) { - try { - ParceledListSlice slice = mService.getSession2Tokens(userId); - return slice == null ? new ArrayList<>() : slice.getList(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get session tokens", e); - } - return new ArrayList<>(); + return mCommunicationManager.getSession2Tokens(); } /** @@ -321,7 +289,7 @@ public final class MediaSessionManager { @NonNull OnActiveSessionsChangedListener sessionListener, @Nullable ComponentName notificationListener, @Nullable Handler handler) { addOnActiveSessionsChangedListener(sessionListener, notificationListener, - UserHandle.myUserId(), handler); + UserHandle.myUserId(), handler == null ? null : new HandlerExecutor(handler)); } /** @@ -337,38 +305,40 @@ public final class MediaSessionManager { * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission in order to * add listeners for user ids that do not belong to current process. * - * @param sessionListener The listener to add. * @param notificationListener The enabled notification listener component. May be null. * @param userHandle The user handle to listen for changes on. - * @param handler The handler to post updates on. + * @param executor The executor on which the listener should be invoked + * @param sessionListener The listener to add. * @hide */ - @SuppressLint({"ExecutorRegistration", "SamShouldBeLast", "UserHandle"}) + @SuppressLint("UserHandle") @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void addOnActiveSessionsChangedListener( - @NonNull OnActiveSessionsChangedListener sessionListener, - @Nullable ComponentName notificationListener, @NonNull UserHandle userHandle, - @Nullable Handler handler) { + @Nullable ComponentName notificationListener, + @NonNull UserHandle userHandle, @NonNull Executor executor, + @NonNull OnActiveSessionsChangedListener sessionListener) { Objects.requireNonNull(userHandle, "userHandle shouldn't be null"); + Objects.requireNonNull(executor, "executor shouldn't be null"); addOnActiveSessionsChangedListener(sessionListener, notificationListener, - userHandle.getIdentifier(), handler); + userHandle.getIdentifier(), executor); } private void addOnActiveSessionsChangedListener( @NonNull OnActiveSessionsChangedListener sessionListener, @Nullable ComponentName notificationListener, int userId, - @Nullable Handler handler) { + @Nullable Executor executor) { Objects.requireNonNull(sessionListener, "sessionListener shouldn't be null"); - if (handler == null) { - handler = new Handler(); + if (executor == null) { + executor = new HandlerExecutor(new Handler()); } + synchronized (mLock) { if (mListeners.get(sessionListener) != null) { Log.w(TAG, "Attempted to add session listener twice, ignoring."); return; } SessionsChangedWrapper wrapper = new SessionsChangedWrapper(mContext, sessionListener, - handler); + executor); try { mService.addSessionsListener(wrapper.mStub, notificationListener, userId); mListeners.put(sessionListener, wrapper); @@ -412,7 +382,8 @@ public final class MediaSessionManager { */ public void addOnSession2TokensChangedListener( @NonNull OnSession2TokensChangedListener listener) { - addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, new Handler()); + addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, + new HandlerExecutor(new Handler())); } /** @@ -428,7 +399,9 @@ public final class MediaSessionManager { */ public void addOnSession2TokensChangedListener( @NonNull OnSession2TokensChangedListener listener, @NonNull Handler handler) { - addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, handler); + Objects.requireNonNull(handler, "handler shouldn't be null"); + addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, + new HandlerExecutor(handler)); } /** @@ -445,20 +418,19 @@ public final class MediaSessionManager { * * @param userHandle The userHandle to listen for changes on * @param listener The listener to add - * @param handler The handler to call listener on. If {@code null}, calling thread's looper will - * be used. + * @param executor The executor on which the listener should be invoked * @hide */ @SuppressLint("UserHandle") public void addOnSession2TokensChangedListener(@NonNull UserHandle userHandle, - @NonNull OnSession2TokensChangedListener listener, @NonNull Handler handler) { + @NonNull OnSession2TokensChangedListener listener, @NonNull Executor executor) { Objects.requireNonNull(userHandle, "userHandle shouldn't be null"); - addOnSession2TokensChangedListener(userHandle.getIdentifier(), listener, handler); + Objects.requireNonNull(executor, "executor shouldn't be null"); + addOnSession2TokensChangedListener(userHandle.getIdentifier(), listener, executor); } private void addOnSession2TokensChangedListener(int userId, - OnSession2TokensChangedListener listener, Handler handler) { - Objects.requireNonNull(handler, "handler shouldn't be null"); + OnSession2TokensChangedListener listener, Executor executor) { Objects.requireNonNull(listener, "listener shouldn't be null"); synchronized (mLock) { if (mSession2TokensListeners.get(listener) != null) { @@ -466,7 +438,7 @@ public final class MediaSessionManager { return; } Session2TokensChangedWrapper wrapper = - new Session2TokensChangedWrapper(listener, handler); + new Session2TokensChangedWrapper(listener, executor); try { mService.addSession2TokensListener(wrapper.getStub(), userId); mSession2TokensListeners.put(listener, wrapper); @@ -529,8 +501,7 @@ public final class MediaSessionManager { } if (shouldRegisterCallback) { try { - mService.registerRemoteSessionCallback( - mRemoteSessionCallbackStub); + mService.registerRemoteSessionCallback(mRemoteSessionCallbackStub); } catch (RemoteException e) { Log.e(TAG, "Failed to register remote volume controller callback", e); } @@ -847,7 +818,7 @@ public final class MediaSessionManager { /** * Add a {@link OnMediaKeyEventDispatchedListener}. * - * @param executor The executor on which the callback should be invoked + * @param executor The executor on which the listener should be invoked * @param listener A {@link OnMediaKeyEventDispatchedListener}. * @hide */ @@ -898,7 +869,7 @@ public final class MediaSessionManager { /** * Add a {@link OnMediaKeyEventDispatchedListener}. * - * @param executor The executor on which the callback should be invoked + * @param executor The executor on which the listener should be invoked * @param listener A {@link OnMediaKeyEventSessionChangedListener}. * @hide */ @@ -958,9 +929,9 @@ public final class MediaSessionManager { * @hide */ @VisibleForTesting - public void setCustomMediaKeyDispatcherForTesting(@Nullable String name) { + public void setCustomMediaKeyDispatcher(@Nullable String name) { try { - mService.setCustomMediaKeyDispatcherForTesting(name); + mService.setCustomMediaKeyDispatcher(name); } catch (RemoteException e) { Log.e(TAG, "Failed to set custom media key dispatcher name", e); } @@ -968,22 +939,58 @@ public final class MediaSessionManager { /** * Set the component name for the custom - * {@link com.android.server.media.SessionPolicyProvider} class. Set to null to restore to the - * custom {@link com.android.server.media.SessionPolicyProvider} class name retrieved from the - * config value. + * {@link com.android.server.media.MediaSessionPolicyProvider} class. Set to null to restore to + * the custom {@link com.android.server.media.MediaSessionPolicyProvider} class name retrieved + * from the config value. * * @hide */ @VisibleForTesting - public void setCustomSessionPolicyProviderForTesting(@Nullable String name) { + public void setCustomMediaSessionPolicyProvider(@Nullable String name) { try { - mService.setCustomSessionPolicyProviderForTesting(name); + mService.setCustomMediaSessionPolicyProvider(name); } catch (RemoteException e) { Log.e(TAG, "Failed to set custom session policy provider name", e); } } /** + * Get the component name for the custom {@link com.android.server.media.MediaKeyDispatcher} + * class. + * + * @hide + */ + @VisibleForTesting + public boolean hasCustomMediaKeyDispatcher(@NonNull String componentName) { + Objects.requireNonNull(componentName, "componentName shouldn't be null"); + try { + return mService.hasCustomMediaKeyDispatcher(componentName); + } catch (RemoteException e) { + Log.e(TAG, "Failed to check if custom media key dispatcher with given component" + + " name exists", e); + } + return false; + } + + /** + * Get the component name for the custom + * {@link com.android.server.media.MediaSessionPolicyProvider} class. + * + * @hide + */ + @VisibleForTesting + public boolean hasCustomMediaSessionPolicyProvider(@NonNull String componentName) { + Objects.requireNonNull(componentName, "componentName shouldn't be null"); + try { + return mService.hasCustomMediaSessionPolicyProvider(componentName); + } catch (RemoteException e) { + Log.e(TAG, "Failed to check if custom media session policy provider with given" + + " component name exists", e); + } + return false; + } + + /** * Get session policies of the specified {@link MediaSession.Token}. * * @hide @@ -1221,62 +1228,61 @@ public final class MediaSessionManager { private static final class SessionsChangedWrapper { private Context mContext; private OnActiveSessionsChangedListener mListener; - private Handler mHandler; + private Executor mExecutor; public SessionsChangedWrapper(Context context, OnActiveSessionsChangedListener listener, - Handler handler) { + Executor executor) { mContext = context; mListener = listener; - mHandler = handler; + mExecutor = executor; } private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() { @Override public void onActiveSessionsChanged(final List<MediaSession.Token> tokens) { - final Handler handler = mHandler; - if (handler != null) { - handler.post(new Runnable() { - @Override - public void run() { - final Context context = mContext; - if (context != null) { - ArrayList<MediaController> controllers = new ArrayList<>(); - int size = tokens.size(); - for (int i = 0; i < size; i++) { - controllers.add(new MediaController(context, tokens.get(i))); - } - final OnActiveSessionsChangedListener listener = mListener; - if (listener != null) { - listener.onActiveSessionsChanged(controllers); - } - } - } - }); + if (mExecutor != null) { + final Executor executor = mExecutor; + executor.execute(() -> callOnActiveSessionsChangedListener(tokens)); } } }; + private void callOnActiveSessionsChangedListener(final List<MediaSession.Token> tokens) { + final Context context = mContext; + if (context != null) { + ArrayList<MediaController> controllers = new ArrayList<>(); + int size = tokens.size(); + for (int i = 0; i < size; i++) { + controllers.add(new MediaController(context, tokens.get(i))); + } + final OnActiveSessionsChangedListener listener = mListener; + if (listener != null) { + listener.onActiveSessionsChanged(controllers); + } + } + } + private void release() { mListener = null; mContext = null; - mHandler = null; + mExecutor = null; } } private static final class Session2TokensChangedWrapper { private final OnSession2TokensChangedListener mListener; - private final Handler mHandler; + private final Executor mExecutor; private final ISession2TokensListener.Stub mStub = new ISession2TokensListener.Stub() { @Override public void onSession2TokensChanged(final List<Session2Token> tokens) { - mHandler.post(() -> mListener.onSession2TokensChanged(tokens)); + mExecutor.execute(() -> mListener.onSession2TokensChanged(tokens)); } }; - Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Handler handler) { + Session2TokensChangedWrapper(OnSession2TokensChangedListener listener, Executor executor) { mListener = listener; - mHandler = (handler == null) ? new Handler() : new Handler(handler.getLooper()); + mExecutor = executor; } public ISession2TokensListener.Stub getStub() { diff --git a/media/java/android/media/soundtrigger/OWNERS b/media/java/android/media/soundtrigger/OWNERS index 6a351d396836..e5d037003ac4 100644 --- a/media/java/android/media/soundtrigger/OWNERS +++ b/media/java/android/media/soundtrigger/OWNERS @@ -1 +1,2 @@ +ytai@google.com elaurent@google.com diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 740bc2dbeb43..c0185dcc4539 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -2525,7 +2525,9 @@ public final class TvInputManager { /** * Pauses TV program recording in the current recording session. * - * @param params A set of extra parameters which might be handled with this event. + * @param params Domain-specific data for this request. Keys <em>must</em> be a scoped + * name, i.e. prefixed with a package name you own, so that different developers + * will not create conflicting keys. * {@link TvRecordingClient#pauseRecording(Bundle)}. */ void pauseRecording(@NonNull Bundle params) { @@ -2543,7 +2545,9 @@ public final class TvInputManager { /** * Resumes TV program recording in the current recording session. * - * @param params A set of extra parameters which might be handled with this event. + * @param params Domain-specific data for this request. Keys <em>must</em> be a scoped + * name, i.e. prefixed with a package name you own, so that different developers + * will not create conflicting keys. * {@link TvRecordingClient#resumeRecording(Bundle)}. */ void resumeRecording(@NonNull Bundle params) { diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index ee0be010c233..952bbf56d5fa 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -149,7 +149,7 @@ public class Tuner implements AutoCloseable { /** * Invalid 64-bit filter ID. */ - public static final long INVALID_FILTER_ID_64BIT = + public static final long INVALID_FILTER_ID_LONG = android.hardware.tv.tuner.V1_1.Constants.Constant64Bit.INVALID_FILTER_ID_64BIT; /** * Invalid frequency that is used as the default frontend frequency setting. @@ -932,8 +932,8 @@ public class Tuner implements AutoCloseable { public int connectFrontendToCiCam(int ciCamId) { if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1, "linkFrontendToCiCam")) { - if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND) - && checkCiCamResource(ciCamId)) { + if (checkCiCamResource(ciCamId) + && checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { return nativeLinkCiCam(ciCamId); } } @@ -978,7 +978,8 @@ public class Tuner implements AutoCloseable { public int disconnectFrontendToCiCam(int ciCamId) { if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1, "unlinkFrontendToCiCam")) { - if (mFrontendCiCamHandle != null && mFrontendCiCamId == ciCamId) { + if (mFrontendCiCamHandle != null && mFrontendCiCamId != null + && mFrontendCiCamId == ciCamId) { int result = nativeUnlinkCiCam(ciCamId); if (result == RESULT_SUCCESS) { mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId); @@ -1409,6 +1410,7 @@ public class Tuner implements AutoCloseable { boolean granted = mTunerResourceManager.requestCiCam(request, ciCamHandle); if (granted) { mFrontendCiCamHandle = ciCamHandle[0]; + mFrontendCiCamId = ciCamId; } return granted; } diff --git a/media/java/android/media/tv/tuner/filter/DownloadEvent.java b/media/java/android/media/tv/tuner/filter/DownloadEvent.java index 9f97b6141de0..394211be646b 100644 --- a/media/java/android/media/tv/tuner/filter/DownloadEvent.java +++ b/media/java/android/media/tv/tuner/filter/DownloadEvent.java @@ -16,6 +16,7 @@ package android.media.tv.tuner.filter; +import android.annotation.IntRange; import android.annotation.SystemApi; /** @@ -51,6 +52,7 @@ public class DownloadEvent extends FilterEvent { /** * Gets MPU sequence number of filtered data. */ + @IntRange(from = 0) public int getMpuSequenceNumber() { return mMpuSequenceNumber; } diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 51b685aeb8be..2f3e2d8d5dd9 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -309,7 +309,8 @@ public class Filter implements AutoCloseable { } /** - * Gets the filter Id. + * Gets the filter Id in 32-bit. For any Tuner SoC that supports 64-bit filter architecture, + * use {@link #getIdLong()}. */ public int getId() { synchronized (mLock) { @@ -319,9 +320,10 @@ public class Filter implements AutoCloseable { } /** - * Gets the 64-bit filter Id. + * Gets the 64-bit filter Id. For any Tuner SoC that supports 32-bit filter architecture, + * use {@link #getId()}. */ - public long getId64Bit() { + public long getIdLong() { synchronized (mLock) { TunerUtils.checkResourceState(TAG, mIsClosed); return nativeGetId64Bit(); diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java index 91be5c38d693..dbd85e9997d8 100644 --- a/media/java/android/media/tv/tuner/filter/MediaEvent.java +++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java @@ -17,6 +17,7 @@ package android.media.tv.tuner.filter; import android.annotation.BytesLong; +import android.annotation.IntRange; import android.annotation.Nullable; import android.annotation.SystemApi; import android.media.MediaCodec.LinearBlock; @@ -154,6 +155,7 @@ public class MediaEvent extends FilterEvent { /** * Gets MPU sequence number of filtered data. */ + @IntRange(from = 0) public int getMpuSequenceNumber() { return mMpuSequenceNumber; } diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java index 6a41c74ef73a..58a81d99ff99 100644 --- a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java +++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java @@ -17,6 +17,7 @@ package android.media.tv.tuner.filter; import android.annotation.BytesLong; +import android.annotation.IntRange; import android.annotation.SystemApi; import android.media.tv.tuner.filter.RecordSettings.ScHevcIndex; @@ -69,6 +70,7 @@ public class MmtpRecordEvent extends FilterEvent { * {@link android.media.tv.tuner.TunerVersionChecker#getTunerVersion()} to get the version * information. */ + @IntRange(from = 0) public int getMpuSequenceNumber() { return mMpuSequenceNumber; } diff --git a/media/java/android/media/tv/tuner/filter/PesEvent.java b/media/java/android/media/tv/tuner/filter/PesEvent.java index 695e596c98b7..bfb7460d9c7b 100644 --- a/media/java/android/media/tv/tuner/filter/PesEvent.java +++ b/media/java/android/media/tv/tuner/filter/PesEvent.java @@ -16,6 +16,7 @@ package android.media.tv.tuner.filter; +import android.annotation.IntRange; import android.annotation.SystemApi; /** @@ -53,6 +54,7 @@ public class PesEvent extends FilterEvent { /** * Gets MPU sequence number of filtered data. */ + @IntRange(from = 0) public int getMpuSequenceNumber() { return mMpuSequenceNumber; } diff --git a/media/java/android/media/tv/tunerresourcemanager/Android.bp b/media/java/android/media/tv/tunerresourcemanager/Android.bp index c38d9194df44..c904ca2be00c 100644 --- a/media/java/android/media/tv/tunerresourcemanager/Android.bp +++ b/media/java/android/media/tv/tunerresourcemanager/Android.bp @@ -1,3 +1,12 @@ +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-media-tv-tunerresourcemanager-sources-aidl", srcs: [ diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java index 042d02fa3d48..1d2657a280a2 100644 --- a/media/java/android/mtp/MtpServer.java +++ b/media/java/android/mtp/MtpServer.java @@ -144,11 +144,6 @@ public class MtpServer implements Runnable { native_remove_storage(storage.getStorageId()); } - public static void configure(boolean usePtp) { - native_configure(usePtp); - } - - public static native final void native_configure(boolean usePtp); private native final void native_setup( MtpDatabase database, FileDescriptor controlFd, diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 6160b8152295..ce4550492740 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -1,3 +1,20 @@ +package { + default_applicable_licenses: ["frameworks_base_media_jni_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_media_jni_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_library_shared { name: "libmedia_jni", diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp index 5d959a3930f4..b291ac95bf4f 100644 --- a/media/jni/android_media_ImageWriter.cpp +++ b/media/jni/android_media_ImageWriter.cpp @@ -364,7 +364,7 @@ static void ImageWriter_classInit(JNIEnv* env, jclass clazz) { } static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobject jsurface, - jint maxImages, jint userFormat) { + jint maxImages, jint userFormat, jint userWidth, jint userHeight) { status_t res; ALOGV("%s: maxImages:%d", __FUNCTION__, maxImages); @@ -405,20 +405,38 @@ static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobje // Get the dimension and format of the producer. sp<ANativeWindow> anw = producer; int32_t width, height, surfaceFormat; - if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) { - ALOGE("%s: Query Surface width failed: %s (%d)", __FUNCTION__, strerror(-res), res); - jniThrowRuntimeException(env, "Failed to query Surface width"); - return 0; + if (userWidth < 0) { + if ((res = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, &width)) != OK) { + ALOGE("%s: Query Surface width failed: %s (%d)", __FUNCTION__, strerror(-res), res); + jniThrowRuntimeException(env, "Failed to query Surface width"); + return 0; + } + } else { + width = userWidth; } + ctx->setBufferWidth(width); - if ((res = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, &height)) != OK) { - ALOGE("%s: Query Surface height failed: %s (%d)", __FUNCTION__, strerror(-res), res); - jniThrowRuntimeException(env, "Failed to query Surface height"); - return 0; + if (userHeight < 0) { + if ((res = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, &height)) != OK) { + ALOGE("%s: Query Surface height failed: %s (%d)", __FUNCTION__, strerror(-res), res); + jniThrowRuntimeException(env, "Failed to query Surface height"); + return 0; + } + } else { + height = userHeight; } ctx->setBufferHeight(height); + if ((userWidth > 0) && (userHeight > 0)) { + res = native_window_set_buffers_user_dimensions(anw.get(), userWidth, userHeight); + if (res != OK) { + ALOGE("%s: Set buffer dimensions failed: %s (%d)", __FUNCTION__, strerror(-res), res); + jniThrowRuntimeException(env, "Set buffer dimensions failed"); + return 0; + } + } + // Query surface format if no valid user format is specified, otherwise, override surface format // with user format. if (userFormat == IMAGE_FORMAT_UNKNOWN) { @@ -1045,7 +1063,7 @@ static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz, static JNINativeMethod gImageWriterMethods[] = { {"nativeClassInit", "()V", (void*)ImageWriter_classInit }, - {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;II)J", + {"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;IIII)J", (void*)ImageWriter_init }, {"nativeClose", "(J)V", (void*)ImageWriter_close }, {"nativeAttachAndQueueImage", "(JJIJIIIIII)I", (void*)ImageWriter_attachAndQueueImage }, diff --git a/media/jni/android_media_JetPlayer.cpp b/media/jni/android_media_JetPlayer.cpp index 481f80b278f8..10a5b586c2b9 100644 --- a/media/jni/android_media_JetPlayer.cpp +++ b/media/jni/android_media_JetPlayer.cpp @@ -43,12 +43,34 @@ struct fields_t { jfieldID nativePlayerInJavaObj; // stores in Java the native JetPlayer object }; -static fields_t javaJetPlayerFields; +static fields_t javaJetPlayerFields {}; +#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj" +#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative" + +static void initializeJavaIDs(JNIEnv* env) { + static std::once_flag sJniInitialized; + + std::call_once(sJniInitialized, [](JNIEnv* env) { + // Get the JetPlayer java class + jclass jetPlayerClass = FindClassOrDie(env, kClassPathName); + javaJetPlayerFields.jetClass = MakeGlobalRefOrDie(env, jetPlayerClass); + + // Get the mNativePlayerInJavaObj variable field + javaJetPlayerFields.nativePlayerInJavaObj = + GetFieldIDOrDie(env, jetPlayerClass, JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "J"); + + // Get the callback to post events from this native code to Java + javaJetPlayerFields.postNativeEventInJava = GetStaticMethodIDOrDie(env, + javaJetPlayerFields.jetClass, JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME, + "(Ljava/lang/Object;III)V"); + }, env); +} // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- + /* * This function is called from JetPlayer instance's render thread */ @@ -79,6 +101,8 @@ static jboolean android_media_JetPlayer_setup(JNIEnv *env, jobject thiz, jobject weak_this, jint maxTracks, jint trackBufferSize) { + initializeJavaIDs(env); + //ALOGV("android_media_JetPlayer_setup(): entering."); JetPlayer* lpJet = new JetPlayer(env->NewGlobalRef(weak_this), maxTracks, trackBufferSize); @@ -511,28 +535,9 @@ static const JNINativeMethod gMethods[] = { {"native_clearQueue", "()Z", (void *)android_media_JetPlayer_clearQueue}, }; -#define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj" -#define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative" int register_android_media_JetPlayer(JNIEnv *env) { - javaJetPlayerFields.jetClass = NULL; - javaJetPlayerFields.postNativeEventInJava = NULL; - javaJetPlayerFields.nativePlayerInJavaObj = NULL; - - // Get the JetPlayer java class - jclass jetPlayerClass = FindClassOrDie(env, kClassPathName); - javaJetPlayerFields.jetClass = MakeGlobalRefOrDie(env, jetPlayerClass); - - // Get the mNativePlayerInJavaObj variable field - javaJetPlayerFields.nativePlayerInJavaObj = GetFieldIDOrDie(env, - jetPlayerClass, JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "J"); - - // Get the callback to post events from this native code to Java - javaJetPlayerFields.postNativeEventInJava = GetStaticMethodIDOrDie(env, - javaJetPlayerFields.jetClass, JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME, - "(Ljava/lang/Object;III)V"); - return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 71c86cc7d42d..1870a939f0dc 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -61,6 +61,7 @@ #include <media/stagefright/foundation/AString.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/PersistentSurface.h> +#include <mediadrm/DrmUtils.h> #include <mediadrm/ICrypto.h> #include <private/android/AHardwareBufferHelpers.h> @@ -312,6 +313,7 @@ status_t JMediaCodec::configure( mGraphicOutput = (mime.startsWithIgnoreCase("video/") || mime.startsWithIgnoreCase("image/")) && !(flags & CONFIGURE_FLAG_ENCODE); mHasCryptoOrDescrambler = (crypto != nullptr) || (descrambler != nullptr); + mCrypto = crypto; return mCodec->configure( format, mSurfaceTextureClient, crypto, descrambler, flags); @@ -1103,6 +1105,8 @@ void JMediaCodec::onMessageReceived(const sp<AMessage> &msg) { } } +jint MediaErrorToJavaError(status_t err); + } // namespace android //////////////////////////////////////////////////////////////////////////////// @@ -1150,7 +1154,8 @@ static void throwCodecException(JNIEnv *env, status_t err, int32_t actionCode, c env->Throw(exception); } -static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { +static void throwCryptoException(JNIEnv *env, status_t err, const char *msg, + const sp<ICrypto> &crypto) { ScopedLocalRef<jclass> clazz( env, env->FindClass("android/media/MediaCodec$CryptoException")); CHECK(clazz.get() != NULL); @@ -1159,7 +1164,7 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { env->GetMethodID(clazz.get(), "<init>", "(ILjava/lang/String;)V"); CHECK(constructID != NULL); - const char *defaultMsg = "Unknown Error"; + std::string defaultMsg = "Unknown Error"; /* translate OS errors to Java API CryptoException errorCodes (which are positive) */ switch (err) { @@ -1199,11 +1204,17 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { err = gCryptoErrorCodes.cryptoErrorLostState; defaultMsg = "Session state was lost, open a new session and retry"; break; - default: /* Other negative DRM error codes go out as is. */ + default: /* Other negative DRM error codes go out best-effort. */ + err = MediaErrorToJavaError(err); + defaultMsg = StrCryptoError(err); break; } - jstring msgObj = env->NewStringUTF(msg != NULL ? msg : defaultMsg); + std::string msgStr(msg != NULL ? msg : defaultMsg.c_str()); + if (crypto != NULL) { + msgStr = DrmUtils::GetExceptionMessage(err, msgStr.c_str(), crypto); + } + jstring msgObj = env->NewStringUTF(msgStr.c_str()); jthrowable exception = (jthrowable)env->NewObject(clazz.get(), constructID, err, msgObj); @@ -1213,7 +1224,7 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) { static jint throwExceptionAsNecessary( JNIEnv *env, status_t err, int32_t actionCode = ACTION_CODE_FATAL, - const char *msg = NULL) { + const char *msg = NULL, const sp<ICrypto>& crypto = NULL) { switch (err) { case OK: return 0; @@ -1237,7 +1248,7 @@ static jint throwExceptionAsNecessary( default: if (isCryptoError(err)) { - throwCryptoException(env, err, msg); + throwCryptoException(env, err, msg, crypto); return 0; } throwCodecException(env, err, actionCode, msg); @@ -1899,7 +1910,8 @@ static void android_media_MediaCodec_queueSecureInputBuffer( subSamples = NULL; throwExceptionAsNecessary( - env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); + env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str(), + codec->getCrypto()); } static jobject android_media_MediaCodec_mapHardwareBuffer(JNIEnv *env, jclass, jobject bufferObj) { diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h index a58f9a74b563..f16bcf3c88e4 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -164,6 +164,8 @@ struct JMediaCodec : public AHandler { bool hasCryptoOrDescrambler() { return mHasCryptoOrDescrambler; } + const sp<ICrypto> &getCrypto() { return mCrypto; } + protected: virtual ~JMediaCodec(); @@ -193,6 +195,8 @@ private: status_t mInitStatus; + sp<ICrypto> mCrypto; + template <typename T> status_t createByteBufferFromABuffer( JNIEnv *env, bool readOnly, bool clearBuffer, const sp<T> &buffer, diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp index 517672ee6127..f491be884b2f 100644 --- a/media/jni/android_media_MediaCrypto.cpp +++ b/media/jni/android_media_MediaCrypto.cpp @@ -202,10 +202,11 @@ static void android_media_MediaCrypto_native_setup( uuid = NULL; if (err != OK) { - jniThrowException( + std::string strerr(StrCryptoError(err)); + jniThrowExceptionFmt( env, "android/media/MediaCryptoException", - "Failed to instantiate crypto object."); + "Failed to instantiate crypto object: %s", strerr.c_str()); return; } @@ -295,7 +296,8 @@ static void android_media_MediaCrypto_setMediaDrmSession( } else if (err == NO_INIT) { msg += ": crypto plugin not initialized"; } else { - msg.appendFormat(": general failure (%d)", err); + std::string strerr(StrCryptoError(err)); + msg.appendFormat(": general failure (%s)", strerr.c_str()); } jniThrowException(env, "android/media/MediaCryptoException", msg.string()); } diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp index 22c3572e963b..56f6c45bb50e 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -343,11 +343,56 @@ void JNIDrmListener::notify(DrmPlugin::EventType eventType, int extra, } } +jint MediaErrorToJavaError(status_t err) { +#define STATUS_CASE(status) \ + case status: \ + return J##status + + switch (err) { + STATUS_CASE(ERROR_DRM_UNKNOWN); + STATUS_CASE(ERROR_DRM_NO_LICENSE); + STATUS_CASE(ERROR_DRM_LICENSE_EXPIRED); + STATUS_CASE(ERROR_DRM_RESOURCE_BUSY); + STATUS_CASE(ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION); + STATUS_CASE(ERROR_DRM_SESSION_NOT_OPENED); + STATUS_CASE(ERROR_DRM_CANNOT_HANDLE); + STATUS_CASE(ERROR_DRM_INSUFFICIENT_SECURITY); + STATUS_CASE(ERROR_DRM_FRAME_TOO_LARGE); + STATUS_CASE(ERROR_DRM_SESSION_LOST_STATE); + STATUS_CASE(ERROR_DRM_CERTIFICATE_MALFORMED); + STATUS_CASE(ERROR_DRM_CERTIFICATE_MISSING); + STATUS_CASE(ERROR_DRM_CRYPTO_LIBRARY); + STATUS_CASE(ERROR_DRM_GENERIC_OEM); + STATUS_CASE(ERROR_DRM_GENERIC_PLUGIN); + STATUS_CASE(ERROR_DRM_INIT_DATA); + STATUS_CASE(ERROR_DRM_KEY_NOT_LOADED); + STATUS_CASE(ERROR_DRM_LICENSE_PARSE); + STATUS_CASE(ERROR_DRM_LICENSE_POLICY); + STATUS_CASE(ERROR_DRM_LICENSE_RELEASE); + STATUS_CASE(ERROR_DRM_LICENSE_REQUEST_REJECTED); + STATUS_CASE(ERROR_DRM_LICENSE_RESTORE); + STATUS_CASE(ERROR_DRM_LICENSE_STATE); + STATUS_CASE(ERROR_DRM_MEDIA_FRAMEWORK); + STATUS_CASE(ERROR_DRM_PROVISIONING_CERTIFICATE); + STATUS_CASE(ERROR_DRM_PROVISIONING_CONFIG); + STATUS_CASE(ERROR_DRM_PROVISIONING_PARSE); + STATUS_CASE(ERROR_DRM_PROVISIONING_RETRY); + STATUS_CASE(ERROR_DRM_RESOURCE_CONTENTION); + STATUS_CASE(ERROR_DRM_SECURE_STOP_RELEASE); + STATUS_CASE(ERROR_DRM_STORAGE_READ); + STATUS_CASE(ERROR_DRM_STORAGE_WRITE); + STATUS_CASE(ERROR_DRM_ZERO_SUBSAMPLES); +#undef STATUS_CASE + } + return static_cast<jint>(err); +} + static void throwStateException(JNIEnv *env, const char *msg, status_t err) { ALOGE("Illegal state exception: %s (%d)", msg, err); + jint jerr = MediaErrorToJavaError(err); jobject exception = env->NewObject(gFields.stateException.classId, - gFields.stateException.init, static_cast<int>(err), + gFields.stateException.init, static_cast<int>(jerr), env->NewStringUTF(msg)); env->Throw(static_cast<jthrowable>(exception)); } @@ -377,43 +422,11 @@ static bool isSessionException(status_t err) { } static bool throwExceptionAsNecessary( - JNIEnv *env, status_t err, const char *msg = NULL) { - - const char *drmMessage = NULL; - - switch (err) { - case ERROR_DRM_UNKNOWN: - drmMessage = "General DRM error"; - break; - case ERROR_DRM_NO_LICENSE: - drmMessage = "No license"; - break; - case ERROR_DRM_LICENSE_EXPIRED: - drmMessage = "License expired"; - break; - case ERROR_DRM_SESSION_NOT_OPENED: - drmMessage = "Session not opened"; - break; - case ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED: - drmMessage = "Not initialized"; - break; - case ERROR_DRM_DECRYPT: - drmMessage = "Decrypt error"; - break; - case ERROR_DRM_CANNOT_HANDLE: - drmMessage = "Invalid parameter or data format"; - break; - case ERROR_DRM_INVALID_STATE: - drmMessage = "Invalid state"; - break; - default: - break; - } - - String8 vendorMessage; - if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) { - vendorMessage = String8::format("DRM vendor-defined error: %d", err); - drmMessage = vendorMessage.string(); + JNIEnv *env, const sp<IDrm> &drm, status_t err, const char *msg = NULL) { + std::string msgStr; + if (drm != NULL && err != OK) { + msgStr = DrmUtils::GetExceptionMessage(err, msg, drm); + msg = msgStr.c_str(); } if (err == BAD_VALUE || err == ERROR_DRM_CANNOT_HANDLE) { @@ -439,15 +452,6 @@ static bool throwExceptionAsNecessary( throwSessionException(env, msg, err); return true; } else if (err != OK) { - String8 errbuf; - if (drmMessage != NULL) { - if (msg == NULL) { - msg = drmMessage; - } else { - errbuf = String8::format("%s: %s", msg, drmMessage); - msg = errbuf.string(); - } - } throwStateException(env, msg, err); return true; } @@ -1045,7 +1049,7 @@ static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative( status_t err = JDrm::IsCryptoSchemeSupported(uuid.array(), mimeType, securityLevel, &isSupported); - if (throwExceptionAsNecessary(env, err, "Failed to query crypto scheme support")) { + if (throwExceptionAsNecessary(env, NULL, err, "Failed to query crypto scheme support")) { return false; } return isSupported; @@ -1068,7 +1072,7 @@ static jbyteArray android_media_MediaDrm_openSession( status_t err = drm->openSession(level, sessionId); - if (throwExceptionAsNecessary(env, err, "Failed to open session")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to open session")) { return NULL; } @@ -1087,7 +1091,7 @@ static void android_media_MediaDrm_closeSession( status_t err = drm->closeSession(sessionId); - throwExceptionAsNecessary(env, err, "Failed to close session"); + throwExceptionAsNecessary(env, drm, err, "Failed to close session"); } static jobject android_media_MediaDrm_getKeyRequest( @@ -1140,7 +1144,7 @@ static jobject android_media_MediaDrm_getKeyRequest( status_t err = drm->getKeyRequest(sessionId, initData, mimeType, keyType, optParams, request, defaultUrl, &keyRequestType); - if (throwExceptionAsNecessary(env, err, "Failed to get key request")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get key request")) { return NULL; } @@ -1210,7 +1214,7 @@ static jbyteArray android_media_MediaDrm_provideKeyResponse( status_t err = drm->provideKeyResponse(sessionId, response, keySetId); - if (throwExceptionAsNecessary(env, err, "Failed to handle key response")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to handle key response")) { return NULL; } return VectorToJByteArray(env, keySetId); @@ -1234,7 +1238,7 @@ static void android_media_MediaDrm_removeKeys( status_t err = drm->removeKeys(keySetId); - throwExceptionAsNecessary(env, err, "Failed to remove keys"); + throwExceptionAsNecessary(env, drm, err, "Failed to remove keys"); } static void android_media_MediaDrm_restoreKeys( @@ -1257,7 +1261,7 @@ static void android_media_MediaDrm_restoreKeys( status_t err = drm->restoreKeys(sessionId, keySetId); - throwExceptionAsNecessary(env, err, "Failed to restore keys"); + throwExceptionAsNecessary(env, drm, err, "Failed to restore keys"); } static jobject android_media_MediaDrm_queryKeyStatus( @@ -1273,7 +1277,7 @@ static jobject android_media_MediaDrm_queryKeyStatus( status_t err = drm->queryKeyStatus(sessionId, infoMap); - if (throwExceptionAsNecessary(env, err, "Failed to query key status")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to query key status")) { return NULL; } @@ -1303,7 +1307,7 @@ static jobject android_media_MediaDrm_getProvisionRequestNative( String8 certAuthority = JStringToString8(env, jcertAuthority); status_t err = drm->getProvisionRequest(certType, certAuthority, request, defaultUrl); - if (throwExceptionAsNecessary(env, err, "Failed to get provision request")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get provision request")) { return NULL; } @@ -1358,7 +1362,7 @@ static jobject android_media_MediaDrm_provideProvisionResponseNative( env->SetObjectField(certificateObj, gFields.certificate.wrappedPrivateKey, jwrappedKey); } - throwExceptionAsNecessary(env, err, "Failed to handle provision response"); + throwExceptionAsNecessary(env, drm, err, "Failed to handle provision response"); return certificateObj; } @@ -1374,7 +1378,7 @@ static jobject android_media_MediaDrm_getSecureStops( status_t err = drm->getSecureStops(secureStops); - if (throwExceptionAsNecessary(env, err, "Failed to get secure stops")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get secure stops")) { return NULL; } @@ -1393,7 +1397,7 @@ static jobject android_media_MediaDrm_getSecureStopIds( status_t err = drm->getSecureStopIds(secureStopIds); - if (throwExceptionAsNecessary(env, err, "Failed to get secure stop Ids")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get secure stop Ids")) { return NULL; } @@ -1412,7 +1416,7 @@ static jbyteArray android_media_MediaDrm_getSecureStop( status_t err = drm->getSecureStop(JByteArrayToVector(env, ssid), secureStop); - if (throwExceptionAsNecessary(env, err, "Failed to get secure stop")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get secure stop")) { return NULL; } @@ -1431,7 +1435,7 @@ static void android_media_MediaDrm_releaseSecureStops( status_t err = drm->releaseSecureStops(ssRelease); - throwExceptionAsNecessary(env, err, "Failed to release secure stops"); + throwExceptionAsNecessary(env, drm, err, "Failed to release secure stops"); } static void android_media_MediaDrm_removeSecureStop( @@ -1444,7 +1448,7 @@ static void android_media_MediaDrm_removeSecureStop( status_t err = drm->removeSecureStop(JByteArrayToVector(env, ssid)); - throwExceptionAsNecessary(env, err, "Failed to remove secure stop"); + throwExceptionAsNecessary(env, drm, err, "Failed to remove secure stop"); } static void android_media_MediaDrm_removeAllSecureStops( @@ -1457,7 +1461,7 @@ static void android_media_MediaDrm_removeAllSecureStops( status_t err = drm->removeAllSecureStops(); - throwExceptionAsNecessary(env, err, "Failed to remove all secure stops"); + throwExceptionAsNecessary(env, drm, err, "Failed to remove all secure stops"); } @@ -1496,7 +1500,7 @@ static jint android_media_MediaDrm_getConnectedHdcpLevel(JNIEnv *env, status_t err = drm->getHdcpLevels(&connected, &max); - if (throwExceptionAsNecessary(env, err, "Failed to get HDCP levels")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get HDCP levels")) { return gHdcpLevels.kHdcpLevelUnknown; } return HdcpLevelTojint(connected); @@ -1515,7 +1519,7 @@ static jint android_media_MediaDrm_getMaxHdcpLevel(JNIEnv *env, status_t err = drm->getHdcpLevels(&connected, &max); - if (throwExceptionAsNecessary(env, err, "Failed to get HDCP levels")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get HDCP levels")) { return gHdcpLevels.kHdcpLevelUnknown; } return HdcpLevelTojint(max); @@ -1532,7 +1536,7 @@ static jint android_media_MediaDrm_getOpenSessionCount(JNIEnv *env, uint32_t open = 0, max = 0; status_t err = drm->getNumberOfSessions(&open, &max); - if (throwExceptionAsNecessary(env, err, "Failed to get number of sessions")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get number of sessions")) { return 0; } return open; @@ -1549,7 +1553,7 @@ static jint android_media_MediaDrm_getMaxSessionCount(JNIEnv *env, uint32_t open = 0, max = 0; status_t err = drm->getNumberOfSessions(&open, &max); - if (throwExceptionAsNecessary(env, err, "Failed to get number of sessions")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get number of sessions")) { return 0; } return max; @@ -1569,7 +1573,7 @@ static jint android_media_MediaDrm_getSecurityLevel(JNIEnv *env, status_t err = drm->getSecurityLevel(sessionId, &level); - if (throwExceptionAsNecessary(env, err, "Failed to get security level")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get security level")) { return gSecurityLevels.kSecurityLevelUnknown; } @@ -1601,7 +1605,7 @@ static jobject android_media_MediaDrm_getOfflineLicenseKeySetIds( status_t err = drm->getOfflineLicenseKeySetIds(keySetIds); - if (throwExceptionAsNecessary(env, err, "Failed to get offline key set Ids")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get offline key set Ids")) { return NULL; } @@ -1618,7 +1622,7 @@ static void android_media_MediaDrm_removeOfflineLicense( status_t err = drm->removeOfflineLicense(JByteArrayToVector(env, keySetId)); - throwExceptionAsNecessary(env, err, "Failed to remove offline license"); + throwExceptionAsNecessary(env, drm, err, "Failed to remove offline license"); } static jint android_media_MediaDrm_getOfflineLicenseState(JNIEnv *env, @@ -1635,7 +1639,7 @@ static jint android_media_MediaDrm_getOfflineLicenseState(JNIEnv *env, status_t err = drm->getOfflineLicenseState(keySetId, &state); - if (throwExceptionAsNecessary(env, err, "Failed to get offline license state")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get offline license state")) { return gOfflineLicenseStates.kOfflineLicenseStateUnknown; } @@ -1668,7 +1672,7 @@ static jstring android_media_MediaDrm_getPropertyString( status_t err = drm->getPropertyString(name, value); - if (throwExceptionAsNecessary(env, err, "Failed to get property")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get property")) { return NULL; } @@ -1694,7 +1698,7 @@ static jbyteArray android_media_MediaDrm_getPropertyByteArray( status_t err = drm->getPropertyByteArray(name, value); - if (throwExceptionAsNecessary(env, err, "Failed to get property")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get property")) { return NULL; } @@ -1726,7 +1730,7 @@ static void android_media_MediaDrm_setPropertyString( status_t err = drm->setPropertyString(name, value); - throwExceptionAsNecessary(env, err, "Failed to set property"); + throwExceptionAsNecessary(env, drm, err, "Failed to set property"); } static void android_media_MediaDrm_setPropertyByteArray( @@ -1754,7 +1758,7 @@ static void android_media_MediaDrm_setPropertyByteArray( status_t err = drm->setPropertyByteArray(name, value); - throwExceptionAsNecessary(env, err, "Failed to set property"); + throwExceptionAsNecessary(env, drm, err, "Failed to set property"); } static void android_media_MediaDrm_setCipherAlgorithmNative( @@ -1778,7 +1782,7 @@ static void android_media_MediaDrm_setCipherAlgorithmNative( status_t err = drm->setCipherAlgorithm(sessionId, algorithm); - throwExceptionAsNecessary(env, err, "Failed to set cipher algorithm"); + throwExceptionAsNecessary(env, drm, err, "Failed to set cipher algorithm"); } static void android_media_MediaDrm_setMacAlgorithmNative( @@ -1802,7 +1806,7 @@ static void android_media_MediaDrm_setMacAlgorithmNative( status_t err = drm->setMacAlgorithm(sessionId, algorithm); - throwExceptionAsNecessary(env, err, "Failed to set mac algorithm"); + throwExceptionAsNecessary(env, drm, err, "Failed to set mac algorithm"); } @@ -1830,7 +1834,7 @@ static jbyteArray android_media_MediaDrm_encryptNative( status_t err = drm->encrypt(sessionId, keyId, input, iv, output); - if (throwExceptionAsNecessary(env, err, "Failed to encrypt")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to encrypt")) { return NULL; } @@ -1860,7 +1864,7 @@ static jbyteArray android_media_MediaDrm_decryptNative( Vector<uint8_t> output; status_t err = drm->decrypt(sessionId, keyId, input, iv, output); - if (throwExceptionAsNecessary(env, err, "Failed to decrypt")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to decrypt")) { return NULL; } @@ -1890,7 +1894,7 @@ static jbyteArray android_media_MediaDrm_signNative( status_t err = drm->sign(sessionId, keyId, message, signature); - if (throwExceptionAsNecessary(env, err, "Failed to sign")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to sign")) { return NULL; } @@ -1921,7 +1925,7 @@ static jboolean android_media_MediaDrm_verifyNative( status_t err = drm->verify(sessionId, keyId, message, signature, match); - throwExceptionAsNecessary(env, err, "Failed to verify"); + throwExceptionAsNecessary(env, drm, err, "Failed to verify"); return match; } @@ -1970,7 +1974,7 @@ static jbyteArray android_media_MediaDrm_signRSANative( status_t err = drm->signRSA(sessionId, algorithm, message, wrappedKey, signature); - if (throwExceptionAsNecessary(env, err, "Failed to sign")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to sign")) { return NULL; } @@ -2017,7 +2021,7 @@ static void android_media_MediaDrm_setPlaybackId( playbackId = JStringToString8(env, jplaybackId); } status_t err = drm->setPlaybackId(sessionId, playbackId.c_str()); - throwExceptionAsNecessary(env, err, "Failed to set playbackId"); + throwExceptionAsNecessary(env, drm, err, "Failed to set playbackId"); } static jobject android_media_MediaDrm_getLogMessages( @@ -2030,7 +2034,7 @@ static jobject android_media_MediaDrm_getLogMessages( Vector<drm::V1_4::LogMessage> logs; status_t err = drm->getLogMessages(logs); ALOGI("drm->getLogMessages %zu logs", logs.size()); - if (throwExceptionAsNecessary(env, err, "Failed to get log messages")) { + if (throwExceptionAsNecessary(env, drm, err, "Failed to get log messages")) { return NULL; } return hidlLogMessagesToJavaList(env, logs); @@ -2056,7 +2060,7 @@ static const JNINativeMethod gMethods[] = { { "closeSessionNative", "([B)V", (void *)android_media_MediaDrm_closeSession }, - { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)" + { "getKeyRequestNative", "([B[BLjava/lang/String;ILjava/util/HashMap;)" "Landroid/media/MediaDrm$KeyRequest;", (void *)android_media_MediaDrm_getKeyRequest }, diff --git a/media/jni/android_media_MediaDrm.h b/media/jni/android_media_MediaDrm.h index b1f544cb2dbe..dc0793af2d17 100644 --- a/media/jni/android_media_MediaDrm.h +++ b/media/jni/android_media_MediaDrm.h @@ -28,6 +28,44 @@ namespace { +enum { + // TODO(b/180483929): use reverse jni e.g. android_media_MediaDrm_native_init + // KEEP IN SYNC with MediaDrm$ErrorCodes in MediaDrm.java! + JERROR_DRM_UNKNOWN = 0, + JERROR_DRM_NO_LICENSE = 1, + JERROR_DRM_LICENSE_EXPIRED = 2, + JERROR_DRM_RESOURCE_BUSY = 3, + JERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION = 4, + JERROR_DRM_SESSION_NOT_OPENED = 5, + JERROR_DRM_CANNOT_HANDLE = 6, + JERROR_DRM_INSUFFICIENT_SECURITY = 7, + JERROR_DRM_FRAME_TOO_LARGE = 8, + JERROR_DRM_SESSION_LOST_STATE = 9, + JERROR_DRM_CERTIFICATE_MALFORMED = 10, + JERROR_DRM_CERTIFICATE_MISSING = 11, + JERROR_DRM_CRYPTO_LIBRARY = 12, + JERROR_DRM_GENERIC_OEM = 13, + JERROR_DRM_GENERIC_PLUGIN = 14, + JERROR_DRM_INIT_DATA = 15, + JERROR_DRM_KEY_NOT_LOADED = 16, + JERROR_DRM_LICENSE_PARSE = 17, + JERROR_DRM_LICENSE_POLICY = 18, + JERROR_DRM_LICENSE_RELEASE = 19, + JERROR_DRM_LICENSE_REQUEST_REJECTED = 20, + JERROR_DRM_LICENSE_RESTORE = 21, + JERROR_DRM_LICENSE_STATE = 22, + JERROR_DRM_MEDIA_FRAMEWORK = 23, + JERROR_DRM_PROVISIONING_CERTIFICATE = 24, + JERROR_DRM_PROVISIONING_CONFIG = 25, + JERROR_DRM_PROVISIONING_PARSE = 26, + JERROR_DRM_PROVISIONING_RETRY = 27, + JERROR_DRM_RESOURCE_CONTENTION = 28, + JERROR_DRM_SECURE_STOP_RELEASE = 29, + JERROR_DRM_STORAGE_READ = 30, + JERROR_DRM_STORAGE_WRITE = 31, + JERROR_DRM_ZERO_SUBSAMPLES = 32, +}; + struct ListenerArgs { jbyteArray jSessionId; jbyteArray jData; @@ -98,6 +136,8 @@ private: DISALLOW_EVIL_CONSTRUCTORS(JDrm); }; +jint MediaErrorToJavaError(status_t err); + } // namespace android #endif // _ANDROID_MEDIA_DRM_H_ diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index eee9f1e08131..7562d3955ef7 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -217,23 +217,34 @@ namespace android { void LnbClientCallbackImpl::onEvent(const LnbEventType lnbEventType) { ALOGD("LnbClientCallbackImpl::onEvent, type=%d", lnbEventType); JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mLnbObj, - gFields.onLnbEventID, - (jint)lnbEventType); + jobject lnb(env->NewLocalRef(mLnbObj)); + if (!env->IsSameObject(lnb, nullptr)) { + env->CallVoidMethod( + lnb, + gFields.onLnbEventID, + (jint)lnbEventType); + } else { + ALOGE("LnbClientCallbackImpl::onEvent:" + "Lnb object has been freed. Ignoring callback."); + } } void LnbClientCallbackImpl::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) { ALOGD("LnbClientCallbackImpl::onDiseqcMessage"); JNIEnv *env = AndroidRuntime::getJNIEnv(); - jbyteArray array = env->NewByteArray(diseqcMessage.size()); - env->SetByteArrayRegion( - array, 0, diseqcMessage.size(), reinterpret_cast<jbyte*>(diseqcMessage[0])); - - env->CallVoidMethod( - mLnbObj, - gFields.onLnbDiseqcMessageID, - array); + jobject lnb(env->NewLocalRef(mLnbObj)); + if (!env->IsSameObject(lnb, nullptr)) { + jbyteArray array = env->NewByteArray(diseqcMessage.size()); + env->SetByteArrayRegion( + array, 0, diseqcMessage.size(), reinterpret_cast<jbyte*>(diseqcMessage[0])); + env->CallVoidMethod( + lnb, + gFields.onLnbDiseqcMessageID, + array); + } else { + ALOGE("LnbClientCallbackImpl::onDiseqcMessage:" + "Lnb object has been freed. Ignoring callback."); + } } void LnbClientCallbackImpl::setLnb(jweak lnbObj) { @@ -254,19 +265,31 @@ LnbClientCallbackImpl::~LnbClientCallbackImpl() { void DvrClientCallbackImpl::onRecordStatus(RecordStatus status) { ALOGD("DvrClientCallbackImpl::onRecordStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mDvrObj, - gFields.onDvrRecordStatusID, - (jint) status); + jobject dvr(env->NewLocalRef(mDvrObj)); + if (!env->IsSameObject(dvr, nullptr)) { + env->CallVoidMethod( + dvr, + gFields.onDvrRecordStatusID, + (jint) status); + } else { + ALOGE("DvrClientCallbackImpl::onRecordStatus:" + "Dvr object has been freed. Ignoring callback."); + } } void DvrClientCallbackImpl::onPlaybackStatus(PlaybackStatus status) { ALOGD("DvrClientCallbackImpl::onPlaybackStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mDvrObj, - gFields.onDvrPlaybackStatusID, - (jint) status); + jobject dvr(env->NewLocalRef(mDvrObj)); + if (!env->IsSameObject(dvr, nullptr)) { + env->CallVoidMethod( + dvr, + gFields.onDvrPlaybackStatusID, + (jint) status); + } else { + ALOGE("DvrClientCallbackImpl::onPlaybackStatus:" + "Dvr object has been freed. Ignoring callback."); + } } void DvrClientCallbackImpl::setDvr(jweak dvrObj) { @@ -538,7 +561,7 @@ jobjectArray FilterClientCallbackImpl::getTsRecordEvent( const std::vector<DemuxFilterEventExt::Event>& eventsExt) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TsRecordEvent"); - jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJJ)V"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJJI)V"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; @@ -591,7 +614,7 @@ jobjectArray FilterClientCallbackImpl::getMmtpRecordEvent( const std::vector<DemuxFilterEventExt::Event>& eventsExt) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MmtpRecordEvent"); - jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJIJ)V"); + jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJIJII)V"); for (int i = 0; i < events.size(); i++) { auto event = events[i]; @@ -810,10 +833,16 @@ void FilterClientCallbackImpl::onFilterEvent_1_1(const DemuxFilterEvent& filterE } } } - env->CallVoidMethod( - mFilterObj, - gFields.onFilterEventID, - array); + jobject filter(env->NewLocalRef(mFilterObj)); + if (!env->IsSameObject(filter, nullptr)) { + env->CallVoidMethod( + filter, + gFields.onFilterEventID, + array); + } else { + ALOGE("FilterClientCallbackImpl::onFilterEvent_1_1:" + "Filter object has been freed. Ignoring callback."); + } } void FilterClientCallbackImpl::onFilterEvent(const DemuxFilterEvent& filterEvent) { @@ -828,10 +857,16 @@ void FilterClientCallbackImpl::onFilterEvent(const DemuxFilterEvent& filterEvent void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) { ALOGD("FilterClientCallbackImpl::onFilterStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mFilterObj, - gFields.onFilterStatusID, - (jint)status); + jobject filter(env->NewLocalRef(mFilterObj)); + if (!env->IsSameObject(filter, nullptr)) { + env->CallVoidMethod( + filter, + gFields.onFilterStatusID, + (jint)status); + } else { + ALOGE("FilterClientCallbackImpl::onFilterStatus:" + "Filter object has been freed. Ignoring callback."); + } } void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filterClient) { @@ -841,6 +876,15 @@ void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filte mFilterClient = filterClient; } +FilterClientCallbackImpl::~FilterClientCallbackImpl() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (mFilterObj != NULL) { + env->DeleteWeakGlobalRef(mFilterObj); + mFilterObj = NULL; + } + mFilterClient = NULL; +} + /////////////// FrontendClientCallbackImpl /////////////////////// FrontendClientCallbackImpl::FrontendClientCallbackImpl(jweak tunerObj) : mObject(tunerObj) {} @@ -848,10 +892,16 @@ FrontendClientCallbackImpl::FrontendClientCallbackImpl(jweak tunerObj) : mObject void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) { ALOGD("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType); JNIEnv *env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mObject, - gFields.onFrontendEventID, - (jint)frontendEventType); + jobject frontend(env->NewLocalRef(mObject)); + if (!env->IsSameObject(frontend, nullptr)) { + env->CallVoidMethod( + frontend, + gFields.onFrontendEventID, + (jint)frontendEventType); + } else { + ALOGE("FrontendClientCallbackImpl::onEvent:" + "Frontend object has been freed. Ignoring callback."); + } } void FrontendClientCallbackImpl::onScanMessage( @@ -859,11 +909,17 @@ void FrontendClientCallbackImpl::onScanMessage( ALOGD("FrontendClientCallbackImpl::onScanMessage, type=%d", type); JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/Tuner"); + jobject frontend(env->NewLocalRef(mObject)); + if (env->IsSameObject(frontend, nullptr)) { + ALOGE("FrontendClientCallbackImpl::onScanMessage:" + "Frontend object has been freed. Ignoring callback."); + return; + } switch(type) { case FrontendScanMessageType::LOCKED: { if (message.isLocked()) { env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onLocked", "()V")); } break; @@ -871,14 +927,14 @@ void FrontendClientCallbackImpl::onScanMessage( case FrontendScanMessageType::END: { if (message.isEnd()) { env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onScanStopped", "()V")); } break; } case FrontendScanMessageType::PROGRESS_PERCENT: { env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onProgress", "(I)V"), (jint) message.progressPercent()); break; @@ -889,7 +945,7 @@ void FrontendClientCallbackImpl::onScanMessage( env->SetIntArrayRegion(freqs, 0, v.size(), reinterpret_cast<jint*>(&v[0])); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onFrequenciesReport", "([I)V"), freqs); break; @@ -900,21 +956,21 @@ void FrontendClientCallbackImpl::onScanMessage( env->SetIntArrayRegion(symbolRates, 0, v.size(), reinterpret_cast<jint*>(&v[0])); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onSymbolRates", "([I)V"), symbolRates); break; } case FrontendScanMessageType::HIERARCHY: { env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onHierarchy", "(I)V"), (jint) message.hierarchy()); break; } case FrontendScanMessageType::ANALOG_TYPE: { env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onSignalType", "(I)V"), (jint) message.analogType()); break; @@ -926,7 +982,7 @@ void FrontendClientCallbackImpl::onScanMessage( env->SetIntArrayRegion(plpIds, 0, jintV.size(), &jintV[0]); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onPlpIds", "([I)V"), plpIds); break; @@ -938,7 +994,7 @@ void FrontendClientCallbackImpl::onScanMessage( env->SetIntArrayRegion(groupIds, 0, jintV.size(), &jintV[0]); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onGroupIds", "([I)V"), groupIds); break; @@ -950,7 +1006,7 @@ void FrontendClientCallbackImpl::onScanMessage( env->SetIntArrayRegion(streamIds, 0, jintV.size(), &jintV[0]); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onInputStreamIds", "([I)V"), streamIds); break; @@ -961,21 +1017,21 @@ void FrontendClientCallbackImpl::onScanMessage( if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::sStd) { standard = (jint) std.sStd(); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onDvbsStandard", "(I)V"), standard); } else if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::tStd) { standard = (jint) std.tStd(); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onDvbtStandard", "(I)V"), standard); } else if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::sifStd) { standard = (jint) std.sifStd(); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onAnalogSifStandard", "(I)V"), standard); } @@ -996,7 +1052,7 @@ void FrontendClientCallbackImpl::onScanMessage( env->SetObjectArrayElement(array, i, obj); } env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onAtsc3PlpInfos", "([Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;)V"), array); @@ -1010,6 +1066,12 @@ void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1 ALOGD("FrontendClientCallbackImpl::onScanMessageExt1_1, type=%d", type); JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/Tuner"); + jobject frontend(env->NewLocalRef(mObject)); + if (env->IsSameObject(frontend, nullptr)) { + ALOGE("FrontendClientCallbackImpl::onScanMessageExt1_1:" + "Frontend object has been freed. Ignoring callback."); + return; + } switch(type) { case FrontendScanMessageTypeExt1_1::MODULATION: { jint modulation = -1; @@ -1056,7 +1118,7 @@ void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1 } if (modulation > 0) { env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onModulationReported", "(I)V"), modulation); } @@ -1065,15 +1127,15 @@ void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1 case FrontendScanMessageTypeExt1_1::HIGH_PRIORITY: { bool isHighPriority = message.isHighPriority(); env->CallVoidMethod( - mObject, - env->GetMethodID(clazz, "onPriorityReported", "(B)V"), + frontend, + env->GetMethodID(clazz, "onPriorityReported", "(Z)V"), isHighPriority); break; } case FrontendScanMessageTypeExt1_1::DVBC_ANNEX: { jint dvbcAnnex = (jint) message.annex(); env->CallVoidMethod( - mObject, + frontend, env->GetMethodID(clazz, "onDvbcAnnexReported", "(I)V"), dvbcAnnex); break; @@ -1083,6 +1145,14 @@ void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1 } } +FrontendClientCallbackImpl::~FrontendClientCallbackImpl() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (mObject != NULL) { + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + } +} + /////////////// Tuner /////////////////////// sp<TunerClient> JTuner::mTunerClient; @@ -1158,15 +1228,22 @@ jobject JTuner::openFrontendByHandle(int feHandle) { if (mDemuxClient != NULL) { mDemuxClient->setFrontendDataSource(mFeClient); } - sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject); - mFeClient->setCallback(feClientCb); JNIEnv *env = AndroidRuntime::getJNIEnv(); + jobject tuner(env->NewLocalRef(mObject)); + if (env->IsSameObject(tuner, nullptr)) { + ALOGE("openFrontendByHandle" + "Tuner object has been freed. Failed to open frontend."); + return NULL; + } + + sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject); + mFeClient->setCallback(feClientCb); // TODO: add more fields to frontend return env->NewObject( env->FindClass("android/media/tv/tuner/Tuner$Frontend"), gFields.frontendInitID, - mObject, + tuner, (jint) mFeId); } @@ -1714,16 +1791,14 @@ jobject JTuner::openDvr(DvrType type, jlong bufferSize) { dvrObj = env->NewObject( env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"), - gFields.dvrRecorderInitID, - mObject); + gFields.dvrRecorderInitID); dvrClient->incStrong(dvrObj); env->SetLongField(dvrObj, gFields.dvrRecorderContext, (jlong)dvrClient.get()); } else { dvrObj = env->NewObject( env->FindClass("android/media/tv/tuner/dvr/DvrPlayback"), - gFields.dvrPlaybackInitID, - mObject); + gFields.dvrPlaybackInitID); dvrClient->incStrong(dvrObj); env->SetLongField(dvrObj, gFields.dvrPlaybackContext, (jlong)dvrClient.get()); } @@ -2126,6 +2201,10 @@ jobject JTuner::getFrontendStatus(jintArray types) { intBandwidth = static_cast<jint>(bandwidth.dvbt()); break; } + case FrontendBandwidth::hidl_discriminator::dvbc: { + intBandwidth = static_cast<jint>(bandwidth.dvbc()); + break; + } case FrontendBandwidth::hidl_discriminator::isdbt: { intBandwidth = static_cast<jint>(bandwidth.isdbt()); break; @@ -2646,12 +2725,10 @@ static FrontendSettings getDvbsFrontendSettings(JNIEnv *env, const jobject& sett FrontendDvbsVcmMode vcmMode = static_cast<FrontendDvbsVcmMode>( env->GetIntField(settings, env->GetFieldID(clazz, "mVcmMode", "I"))); - FrontendDvbsCodeRate coderate = getDvbsCodeRate(env, settings); FrontendDvbsSettings frontendDvbsSettings { .frequency = freq, .modulation = modulation, - .coderate = coderate, .symbolRate = symbolRate, .rolloff = rolloff, .pilot = pilot, @@ -2659,6 +2736,13 @@ static FrontendSettings getDvbsFrontendSettings(JNIEnv *env, const jobject& sett .standard = standard, .vcmMode = vcmMode, }; + + jobject jcodeRate = env->GetObjectField(settings, env->GetFieldID(clazz, "mCodeRate", + "Landroid/media/tv/tuner/frontend/DvbsCodeRate;")); + if (jcodeRate != NULL) { + frontendDvbsSettings.coderate = getDvbsCodeRate(env, settings); + } + frontendSettings.dvbs(frontendDvbsSettings); return frontendSettings; } @@ -2670,7 +2754,7 @@ static void getDvbsFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings, static_cast<FrontendDvbsScanType>( env->GetIntField(settings, env->GetFieldID(clazz, "mScanType", "I"))); bool isDiseqcRxMessage = static_cast<bool>(env->GetBooleanField( - settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "B"))); + settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "Z"))); FrontendDvbsSettingsExt1_1 dvbsExt1_1 { .scanType = scanType, @@ -2910,6 +2994,10 @@ static void getDtmbFrontendSettings(JNIEnv *env, const jobject& settings, static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) { ALOGD("getFrontendSettings %d", type); + if (type == static_cast<int>(::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) { + return FrontendSettings(); + } + FrontendType feType = static_cast<FrontendType>(type); switch(feType) { case FrontendType::ANALOG: @@ -3483,26 +3571,28 @@ static DemuxFilterSettings getFilterConfiguration( .tpid = tpid, }; - DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype); - switch (tsType) { - case DemuxTsFilterType::SECTION: - tsFilterSettings.filterSettings.section( - getFilterSectionSettings(env, settingsObj)); - break; - case DemuxTsFilterType::AUDIO: - case DemuxTsFilterType::VIDEO: - tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); - break; - case DemuxTsFilterType::PES: - tsFilterSettings.filterSettings.pesData( - getFilterPesDataSettings(env, settingsObj)); - break; - case DemuxTsFilterType::RECORD: - tsFilterSettings.filterSettings.record( - getFilterRecordSettings(env, settingsObj)); - break; - default: - break; + if (settingsObj != NULL) { + DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype); + switch (tsType) { + case DemuxTsFilterType::SECTION: + tsFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + case DemuxTsFilterType::AUDIO: + case DemuxTsFilterType::VIDEO: + tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); + break; + case DemuxTsFilterType::PES: + tsFilterSettings.filterSettings.pesData( + getFilterPesDataSettings(env, settingsObj)); + break; + case DemuxTsFilterType::RECORD: + tsFilterSettings.filterSettings.record( + getFilterRecordSettings(env, settingsObj)); + break; + default: + break; + } } filterSettings.ts(tsFilterSettings); break; @@ -3514,60 +3604,55 @@ static DemuxFilterSettings getFilterConfiguration( DemuxMmtpFilterSettings mmtpFilterSettings { .mmtpPid = mmtpPid, }; - DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype); - switch (mmtpType) { - case DemuxMmtpFilterType::SECTION: - mmtpFilterSettings.filterSettings.section( - getFilterSectionSettings(env, settingsObj)); - break; - case DemuxMmtpFilterType::AUDIO: - case DemuxMmtpFilterType::VIDEO: - mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); - break; - case DemuxMmtpFilterType::PES: - mmtpFilterSettings.filterSettings.pesData( - getFilterPesDataSettings(env, settingsObj)); - break; - case DemuxMmtpFilterType::RECORD: - mmtpFilterSettings.filterSettings.record( - getFilterRecordSettings(env, settingsObj)); - break; - case DemuxMmtpFilterType::DOWNLOAD: - mmtpFilterSettings.filterSettings.download( - getFilterDownloadSettings(env, settingsObj)); - break; - default: - break; + + if (settingsObj != NULL) { + DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype); + switch (mmtpType) { + case DemuxMmtpFilterType::SECTION: + mmtpFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::AUDIO: + case DemuxMmtpFilterType::VIDEO: + mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::PES: + mmtpFilterSettings.filterSettings.pesData( + getFilterPesDataSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::RECORD: + mmtpFilterSettings.filterSettings.record( + getFilterRecordSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::DOWNLOAD: + mmtpFilterSettings.filterSettings.download( + getFilterDownloadSettings(env, settingsObj)); + break; + default: + break; + } } filterSettings.mmtp(mmtpFilterSettings); break; } case DemuxFilterMainType::IP: { DemuxIpAddress ipAddr = getDemuxIpAddress(env, filterConfigObj); - DemuxIpFilterSettings ipFilterSettings { .ipAddr = ipAddr, }; + DemuxIpFilterType ipType = static_cast<DemuxIpFilterType>(subtype); - switch (ipType) { - case DemuxIpFilterType::SECTION: { - ipFilterSettings.filterSettings.section( - getFilterSectionSettings(env, settingsObj)); - break; - } - case DemuxIpFilterType::IP: { - jclass clazz = env->FindClass( - "android/media/tv/tuner/filter/IpFilterConfiguration"); - bool bPassthrough = static_cast<bool>( - env->GetBooleanField( - filterConfigObj, env->GetFieldID( - clazz, "mPassthrough", "Z"))); - ipFilterSettings.filterSettings.bPassthrough(bPassthrough); - break; - } - default: { - break; - } + if (ipType == DemuxIpFilterType::SECTION && settingsObj != NULL) { + ipFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + } else if (ipType == DemuxIpFilterType::IP) { + jclass clazz = env->FindClass( + "android/media/tv/tuner/filter/IpFilterConfiguration"); + bool bPassthrough = static_cast<bool>( + env->GetBooleanField( + filterConfigObj, env->GetFieldID( + clazz, "mPassthrough", "Z"))); + ipFilterSettings.filterSettings.bPassthrough(bPassthrough); } filterSettings.ip(ipFilterSettings); break; @@ -3584,24 +3669,17 @@ static DemuxFilterSettings getFilterConfiguration( .packetType = packetType, .isCompressedIpPacket = isCompressedIpPacket, }; + DemuxTlvFilterType tlvType = static_cast<DemuxTlvFilterType>(subtype); - switch (tlvType) { - case DemuxTlvFilterType::SECTION: { - tlvFilterSettings.filterSettings.section( - getFilterSectionSettings(env, settingsObj)); - break; - } - case DemuxTlvFilterType::TLV: { - bool bPassthrough = static_cast<bool>( - env->GetBooleanField( - filterConfigObj, env->GetFieldID( - clazz, "mPassthrough", "Z"))); - tlvFilterSettings.filterSettings.bPassthrough(bPassthrough); - break; - } - default: { - break; - } + if (tlvType == DemuxTlvFilterType::SECTION && settingsObj != NULL) { + tlvFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + } else if (tlvType == DemuxTlvFilterType::TLV) { + bool bPassthrough = static_cast<bool>( + env->GetBooleanField( + filterConfigObj, env->GetFieldID( + clazz, "mPassthrough", "Z"))); + tlvFilterSettings.filterSettings.bPassthrough(bPassthrough); } filterSettings.tlv(tlvFilterSettings); break; @@ -3616,14 +3694,17 @@ static DemuxFilterSettings getFilterConfiguration( .packetType = packetType, .lengthType = lengthType, }; - DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype); - switch (alpType) { - case DemuxAlpFilterType::SECTION: - alpFilterSettings.filterSettings.section( - getFilterSectionSettings(env, settingsObj)); - break; - default: - break; + + if (settingsObj != NULL) { + DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype); + switch (alpType) { + case DemuxAlpFilterType::SECTION: + alpFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + default: + break; + } } filterSettings.alp(alpFilterSettings); break; diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 0e30b18eb2d4..fafef4221541 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -118,6 +118,7 @@ struct MediaEvent : public RefBase { }; struct FilterClientCallbackImpl : public FilterClientCallback { + ~FilterClientCallbackImpl(); virtual void onFilterEvent_1_1(const DemuxFilterEvent& filterEvent, const DemuxFilterEventExt& filterEventExt); virtual void onFilterEvent(const DemuxFilterEvent& filterEvent); @@ -155,7 +156,7 @@ private: struct FrontendClientCallbackImpl : public FrontendClientCallback { FrontendClientCallbackImpl(jweak tunerObj); - + ~FrontendClientCallbackImpl(); virtual void onEvent(FrontendEventType frontendEventType); virtual void onScanMessage( FrontendScanMessageType type, const FrontendScanMessage& message); diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index 4efdcaccf7a1..ffed4747d3ea 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -32,6 +32,7 @@ #include <android_runtime/AndroidRuntime.h> #include <android_runtime/Log.h> +#include "core_jni_helpers.h" #include <jni.h> #include <media/stagefright/NuMediaExtractor.h> #include <nativehelper/JNIHelp.h> @@ -48,6 +49,7 @@ using namespace android; // ---------------------------------------------------------------------------- +// MtpDatabase methods static jmethodID method_beginSendObject; static jmethodID method_endSendObject; static jmethodID method_rescanFile; @@ -75,6 +77,7 @@ static jmethodID method_endCopyObject; static jmethodID method_getObjectReferences; static jmethodID method_setObjectReferences; +// MtpDatabase fields. static jfieldID field_context; // MtpPropertyList methods @@ -86,6 +89,59 @@ static jmethodID method_getDataTypes; static jmethodID method_getLongValues; static jmethodID method_getStringValues; +// Initializer for the jfieldIDs and jmethodIDs above. This method must be invoked +// before using these static fields and methods for JNI accesses. +static void initializeJavaIDs(JNIEnv* env) { + static std::once_flag sJniInitialized; + +#define GET_METHOD_ID(name, jclass, signature) \ + method_##name = GetMethodIDOrDie(env, jclass, #name, signature); + + std::call_once(sJniInitialized, [](JNIEnv* env) { + const jclass mdb_class = FindClassOrDie(env, "android/mtp/MtpDatabase"); + GET_METHOD_ID(beginSendObject, mdb_class, "(Ljava/lang/String;III)I"); + GET_METHOD_ID(endSendObject, mdb_class, "(IZ)V"); + GET_METHOD_ID(rescanFile, mdb_class, "(Ljava/lang/String;II)V"); + GET_METHOD_ID(getObjectList, mdb_class, "(III)[I"); + GET_METHOD_ID(getNumObjects, mdb_class, "(III)I"); + GET_METHOD_ID(getSupportedPlaybackFormats, mdb_class, "()[I"); + GET_METHOD_ID(getSupportedCaptureFormats, mdb_class, "()[I"); + GET_METHOD_ID(getSupportedObjectProperties, mdb_class, "(I)[I"); + GET_METHOD_ID(getSupportedDeviceProperties, mdb_class, "()[I"); + GET_METHOD_ID(setObjectProperty, mdb_class, "(IIJLjava/lang/String;)I"); + GET_METHOD_ID(getDeviceProperty, mdb_class, "(I[J[C)I"); + GET_METHOD_ID(setDeviceProperty, mdb_class, "(IJLjava/lang/String;)I"); + GET_METHOD_ID(getObjectPropertyList, mdb_class, "(IIIII)Landroid/mtp/MtpPropertyList;"); + GET_METHOD_ID(getObjectInfo, mdb_class, "(I[I[C[J)Z"); + GET_METHOD_ID(getObjectFilePath, mdb_class, "(I[C[J)I"); + GET_METHOD_ID(openFilePath, mdb_class, "(Ljava/lang/String;Z)I"); + GET_METHOD_ID(getThumbnailInfo, mdb_class, "(I[J)Z"); + GET_METHOD_ID(getThumbnailData, mdb_class, "(I)[B"); + GET_METHOD_ID(beginDeleteObject, mdb_class, "(I)I"); + GET_METHOD_ID(endDeleteObject, mdb_class, "(IZ)V"); + GET_METHOD_ID(beginMoveObject, mdb_class, "(III)I"); + GET_METHOD_ID(endMoveObject, mdb_class, "(IIIIIZ)V"); + GET_METHOD_ID(beginCopyObject, mdb_class, "(III)I"); + GET_METHOD_ID(endCopyObject, mdb_class, "(IZ)V"); + GET_METHOD_ID(getObjectReferences, mdb_class, "(I)[I"); + GET_METHOD_ID(setObjectReferences, mdb_class, "(I[I)I"); + field_context = GetFieldIDOrDie(env, mdb_class, "mNativeContext", "J"); + + const jclass mpl_class = FindClassOrDie(env, "android/mtp/MtpPropertyList"); + GET_METHOD_ID(getCode, mpl_class, "()I"); + GET_METHOD_ID(getCount, mpl_class, "()I"); + GET_METHOD_ID(getObjectHandles, mpl_class, "()[I"); + GET_METHOD_ID(getPropertyCodes, mpl_class, "()[I"); + GET_METHOD_ID(getDataTypes, mpl_class, "()[I"); + GET_METHOD_ID(getLongValues, mpl_class, "()[J"); + GET_METHOD_ID(getStringValues, mpl_class, "()[Ljava/lang/String;"); + + return 0; + }, env); + +#undef GET_METHOD_ID +} + IMtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) { return (IMtpDatabase *)env->GetLongField(database, field_context); @@ -1280,6 +1336,7 @@ MtpProperty* MtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) { static void android_mtp_MtpDatabase_setup(JNIEnv *env, jobject thiz) { + initializeJavaIDs(env); MtpDatabase* database = new MtpDatabase(env, thiz); env->SetLongField(thiz, field_context, (jlong)database); checkAndClearExceptionFromCallback(env, __FUNCTION__); @@ -1314,69 +1371,9 @@ static const JNINativeMethod gMtpPropertyGroupMethods[] = { {"format_date_time", "(J)Ljava/lang/String;", (void *)android_mtp_MtpPropertyGroup_format_date_time}, }; - -#define GET_METHOD_ID(name, jclass, signature) \ - method_##name = env->GetMethodID(jclass, #name, signature); \ - if (method_##name == NULL) { \ - ALOGE("Can't find " #name); \ - return -1; \ - } \ - + \ int register_android_mtp_MtpDatabase(JNIEnv *env) { - jclass clazz; - - clazz = env->FindClass("android/mtp/MtpDatabase"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpDatabase"); - return -1; - } - GET_METHOD_ID(beginSendObject, clazz, "(Ljava/lang/String;III)I"); - GET_METHOD_ID(endSendObject, clazz, "(IZ)V"); - GET_METHOD_ID(rescanFile, clazz, "(Ljava/lang/String;II)V"); - GET_METHOD_ID(getObjectList, clazz, "(III)[I"); - GET_METHOD_ID(getNumObjects, clazz, "(III)I"); - GET_METHOD_ID(getSupportedPlaybackFormats, clazz, "()[I"); - GET_METHOD_ID(getSupportedCaptureFormats, clazz, "()[I"); - GET_METHOD_ID(getSupportedObjectProperties, clazz, "(I)[I"); - GET_METHOD_ID(getSupportedDeviceProperties, clazz, "()[I"); - GET_METHOD_ID(setObjectProperty, clazz, "(IIJLjava/lang/String;)I"); - GET_METHOD_ID(getDeviceProperty, clazz, "(I[J[C)I"); - GET_METHOD_ID(setDeviceProperty, clazz, "(IJLjava/lang/String;)I"); - GET_METHOD_ID(getObjectPropertyList, clazz, "(IIIII)Landroid/mtp/MtpPropertyList;"); - GET_METHOD_ID(getObjectInfo, clazz, "(I[I[C[J)Z"); - GET_METHOD_ID(getObjectFilePath, clazz, "(I[C[J)I"); - GET_METHOD_ID(openFilePath, clazz, "(Ljava/lang/String;Z)I"); - GET_METHOD_ID(getThumbnailInfo, clazz, "(I[J)Z"); - GET_METHOD_ID(getThumbnailData, clazz, "(I)[B"); - GET_METHOD_ID(beginDeleteObject, clazz, "(I)I"); - GET_METHOD_ID(endDeleteObject, clazz, "(IZ)V"); - GET_METHOD_ID(beginMoveObject, clazz, "(III)I"); - GET_METHOD_ID(endMoveObject, clazz, "(IIIIIZ)V"); - GET_METHOD_ID(beginCopyObject, clazz, "(III)I"); - GET_METHOD_ID(endCopyObject, clazz, "(IZ)V"); - GET_METHOD_ID(getObjectReferences, clazz, "(I)[I"); - GET_METHOD_ID(setObjectReferences, clazz, "(I[I)I"); - - field_context = env->GetFieldID(clazz, "mNativeContext", "J"); - if (field_context == NULL) { - ALOGE("Can't find MtpDatabase.mNativeContext"); - return -1; - } - - clazz = env->FindClass("android/mtp/MtpPropertyList"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpPropertyList"); - return -1; - } - GET_METHOD_ID(getCode, clazz, "()I"); - GET_METHOD_ID(getCount, clazz, "()I"); - GET_METHOD_ID(getObjectHandles, clazz, "()[I"); - GET_METHOD_ID(getPropertyCodes, clazz, "()[I"); - GET_METHOD_ID(getDataTypes, clazz, "()[I"); - GET_METHOD_ID(getLongValues, clazz, "()[J"); - GET_METHOD_ID(getStringValues, clazz, "()[Ljava/lang/String;"); - if (AndroidRuntime::registerNativeMethods(env, "android/mtp/MtpDatabase", gMtpDatabaseMethods, NELEM(gMtpDatabaseMethods))) return -1; diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp index 060eaf9ccad4..3d2b00fec26c 100644 --- a/media/jni/android_mtp_MtpDevice.cpp +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -34,6 +34,7 @@ #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" +#include "core_jni_helpers.h" #include "nativehelper/ScopedLocalRef.h" #include "private/android_filesystem_config.h" @@ -107,6 +108,95 @@ static jfieldID field_event_parameter1; static jfieldID field_event_parameter2; static jfieldID field_event_parameter3; +// Initializer for the jclasses, jfieldIDs and jmethodIDs declared above. This method must be +// invoked before using these static fields for JNI accesses. +static void initializeJavaIDs(JNIEnv* env) { + static std::once_flag sJniInitialized; + + std::call_once(sJniInitialized, [](JNIEnv* env) { + clazz_deviceInfo = + (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpDeviceInfo")); + constructor_deviceInfo = GetMethodIDOrDie(env, clazz_deviceInfo, "<init>", "()V"); + field_deviceInfo_manufacturer = + GetFieldIDOrDie(env, clazz_deviceInfo, "mManufacturer", "Ljava/lang/String;"); + field_deviceInfo_model = + GetFieldIDOrDie(env, clazz_deviceInfo, "mModel", "Ljava/lang/String;"); + field_deviceInfo_version = + GetFieldIDOrDie(env, clazz_deviceInfo, "mVersion", "Ljava/lang/String;"); + field_deviceInfo_serialNumber = + GetFieldIDOrDie(env, clazz_deviceInfo, "mSerialNumber", "Ljava/lang/String;"); + field_deviceInfo_operationsSupported = + GetFieldIDOrDie(env, clazz_deviceInfo, "mOperationsSupported", "[I"); + field_deviceInfo_eventsSupported = + GetFieldIDOrDie(env, clazz_deviceInfo, "mEventsSupported", "[I"); + + clazz_storageInfo = + (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpStorageInfo")); + constructor_storageInfo = GetMethodIDOrDie(env, clazz_storageInfo, "<init>", "()V"); + field_storageInfo_storageId = GetFieldIDOrDie(env, clazz_storageInfo, "mStorageId", "I"); + field_storageInfo_maxCapacity = + GetFieldIDOrDie(env, clazz_storageInfo, "mMaxCapacity", "J"); + field_storageInfo_freeSpace = + GetFieldIDOrDie(env, clazz_storageInfo, "mFreeSpace", "J"); + field_storageInfo_description = + GetFieldIDOrDie(env, clazz_storageInfo, "mDescription", "Ljava/lang/String;"); + field_storageInfo_volumeIdentifier = + GetFieldIDOrDie(env, clazz_storageInfo, "mVolumeIdentifier", "Ljava/lang/String;"); + + clazz_objectInfo = + (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpObjectInfo")); + constructor_objectInfo = GetMethodIDOrDie(env, clazz_objectInfo, "<init>", "()V"); + field_objectInfo_handle = GetFieldIDOrDie(env, clazz_objectInfo, "mHandle", "I"); + field_objectInfo_storageId = GetFieldIDOrDie(env, clazz_objectInfo, "mStorageId", "I"); + field_objectInfo_format = GetFieldIDOrDie(env, clazz_objectInfo, "mFormat", "I"); + field_objectInfo_protectionStatus = + GetFieldIDOrDie(env, clazz_objectInfo, "mProtectionStatus", "I"); + field_objectInfo_compressedSize = + GetFieldIDOrDie(env, clazz_objectInfo, "mCompressedSize", "I"); + field_objectInfo_thumbFormat = GetFieldIDOrDie(env, clazz_objectInfo, "mThumbFormat", "I"); + field_objectInfo_thumbCompressedSize = + GetFieldIDOrDie(env, clazz_objectInfo, "mThumbCompressedSize", "I"); + field_objectInfo_thumbPixWidth = + GetFieldIDOrDie(env, clazz_objectInfo, "mThumbPixWidth", "I"); + field_objectInfo_thumbPixHeight = + GetFieldIDOrDie(env, clazz_objectInfo, "mThumbPixHeight", "I"); + field_objectInfo_imagePixWidth = + GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixWidth", "I"); + field_objectInfo_imagePixHeight = + GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixHeight", "I"); + field_objectInfo_imagePixDepth = + GetFieldIDOrDie(env, clazz_objectInfo, "mImagePixDepth", "I"); + field_objectInfo_parent = GetFieldIDOrDie(env, clazz_objectInfo, "mParent", "I"); + field_objectInfo_associationType = + GetFieldIDOrDie(env, clazz_objectInfo, "mAssociationType", "I"); + field_objectInfo_associationDesc = + GetFieldIDOrDie(env, clazz_objectInfo, "mAssociationDesc", "I"); + field_objectInfo_sequenceNumber = + GetFieldIDOrDie(env, clazz_objectInfo, "mSequenceNumber", "I"); + field_objectInfo_name = + GetFieldIDOrDie(env, clazz_objectInfo, "mName", "Ljava/lang/String;"); + field_objectInfo_dateCreated = GetFieldIDOrDie(env, clazz_objectInfo, "mDateCreated", "J"); + field_objectInfo_dateModified = + GetFieldIDOrDie(env, clazz_objectInfo, "mDateModified", "J"); + field_objectInfo_keywords = + GetFieldIDOrDie(env, clazz_objectInfo, "mKeywords", "Ljava/lang/String;"); + + clazz_event = (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpEvent")); + constructor_event = GetMethodIDOrDie(env, clazz_event, "<init>", "()V"); + field_event_eventCode = GetFieldIDOrDie(env, clazz_event, "mEventCode", "I"); + field_event_parameter1 = GetFieldIDOrDie(env, clazz_event, "mParameter1", "I"); + field_event_parameter2 = GetFieldIDOrDie(env, clazz_event, "mParameter2", "I"); + field_event_parameter3 = GetFieldIDOrDie(env, clazz_event, "mParameter3", "I"); + + const jclass clazz = FindClassOrDie(env, "android/mtp/MtpDevice"); + field_context = GetFieldIDOrDie(env, clazz, "mNativeContext", "J"); + + clazz_io_exception = (jclass)env->NewGlobalRef(FindClassOrDie(env, "java/io/IOException")); + clazz_operation_canceled_exception = + (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/os/OperationCanceledException")); + }, env); +} + class JavaArrayWriter { public: JavaArrayWriter(JNIEnv* env, jbyteArray array) : @@ -132,6 +222,11 @@ private: MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice) { + // get_device_from_object() is called by the majority of JNI methods in this file. Call + // `initializeJavaIDs()` here to ensure JNI methodID's, fieldIDs and classes are initialized + // before use. + initializeJavaIDs(env); + return (MtpDevice*)env->GetLongField(javaDevice, field_context); } @@ -200,6 +295,8 @@ android_mtp_MtpDevice_open(JNIEnv *env, jobject thiz, jstring deviceName, jint f MtpDevice* device = MtpDevice::open(deviceNameStr, fd); env->ReleaseStringUTFChars(deviceName, deviceNameStr); + // The jfieldID `field_context` needs to be initialized before use below. + initializeJavaIDs(env); if (device) env->SetLongField(thiz, field_context, (jlong)device); return (jboolean)(device != NULL); @@ -781,256 +878,7 @@ static const JNINativeMethod gMethods[] = { int register_android_mtp_MtpDevice(JNIEnv *env) { - jclass clazz; - ALOGD("register_android_mtp_MtpDevice\n"); - - clazz = env->FindClass("android/mtp/MtpDeviceInfo"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpDeviceInfo"); - return -1; - } - constructor_deviceInfo = env->GetMethodID(clazz, "<init>", "()V"); - if (constructor_deviceInfo == NULL) { - ALOGE("Can't find android/mtp/MtpDeviceInfo constructor"); - return -1; - } - field_deviceInfo_manufacturer = env->GetFieldID(clazz, "mManufacturer", "Ljava/lang/String;"); - if (field_deviceInfo_manufacturer == NULL) { - ALOGE("Can't find MtpDeviceInfo.mManufacturer"); - return -1; - } - field_deviceInfo_model = env->GetFieldID(clazz, "mModel", "Ljava/lang/String;"); - if (field_deviceInfo_model == NULL) { - ALOGE("Can't find MtpDeviceInfo.mModel"); - return -1; - } - field_deviceInfo_version = env->GetFieldID(clazz, "mVersion", "Ljava/lang/String;"); - if (field_deviceInfo_version == NULL) { - ALOGE("Can't find MtpDeviceInfo.mVersion"); - return -1; - } - field_deviceInfo_serialNumber = env->GetFieldID(clazz, "mSerialNumber", "Ljava/lang/String;"); - if (field_deviceInfo_serialNumber == NULL) { - ALOGE("Can't find MtpDeviceInfo.mSerialNumber"); - return -1; - } - field_deviceInfo_operationsSupported = env->GetFieldID(clazz, "mOperationsSupported", "[I"); - if (field_deviceInfo_operationsSupported == NULL) { - ALOGE("Can't find MtpDeviceInfo.mOperationsSupported"); - return -1; - } - field_deviceInfo_eventsSupported = env->GetFieldID(clazz, "mEventsSupported", "[I"); - if (field_deviceInfo_eventsSupported == NULL) { - ALOGE("Can't find MtpDeviceInfo.mEventsSupported"); - return -1; - } - clazz_deviceInfo = (jclass)env->NewGlobalRef(clazz); - - clazz = env->FindClass("android/mtp/MtpStorageInfo"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpStorageInfo"); - return -1; - } - constructor_storageInfo = env->GetMethodID(clazz, "<init>", "()V"); - if (constructor_storageInfo == NULL) { - ALOGE("Can't find android/mtp/MtpStorageInfo constructor"); - return -1; - } - field_storageInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I"); - if (field_storageInfo_storageId == NULL) { - ALOGE("Can't find MtpStorageInfo.mStorageId"); - return -1; - } - field_storageInfo_maxCapacity = env->GetFieldID(clazz, "mMaxCapacity", "J"); - if (field_storageInfo_maxCapacity == NULL) { - ALOGE("Can't find MtpStorageInfo.mMaxCapacity"); - return -1; - } - field_storageInfo_freeSpace = env->GetFieldID(clazz, "mFreeSpace", "J"); - if (field_storageInfo_freeSpace == NULL) { - ALOGE("Can't find MtpStorageInfo.mFreeSpace"); - return -1; - } - field_storageInfo_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;"); - if (field_storageInfo_description == NULL) { - ALOGE("Can't find MtpStorageInfo.mDescription"); - return -1; - } - field_storageInfo_volumeIdentifier = env->GetFieldID(clazz, "mVolumeIdentifier", "Ljava/lang/String;"); - if (field_storageInfo_volumeIdentifier == NULL) { - ALOGE("Can't find MtpStorageInfo.mVolumeIdentifier"); - return -1; - } - clazz_storageInfo = (jclass)env->NewGlobalRef(clazz); - - clazz = env->FindClass("android/mtp/MtpObjectInfo"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpObjectInfo"); - return -1; - } - constructor_objectInfo = env->GetMethodID(clazz, "<init>", "()V"); - if (constructor_objectInfo == NULL) { - ALOGE("Can't find android/mtp/MtpObjectInfo constructor"); - return -1; - } - field_objectInfo_handle = env->GetFieldID(clazz, "mHandle", "I"); - if (field_objectInfo_handle == NULL) { - ALOGE("Can't find MtpObjectInfo.mHandle"); - return -1; - } - field_objectInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I"); - if (field_objectInfo_storageId == NULL) { - ALOGE("Can't find MtpObjectInfo.mStorageId"); - return -1; - } - field_objectInfo_format = env->GetFieldID(clazz, "mFormat", "I"); - if (field_objectInfo_format == NULL) { - ALOGE("Can't find MtpObjectInfo.mFormat"); - return -1; - } - field_objectInfo_protectionStatus = env->GetFieldID(clazz, "mProtectionStatus", "I"); - if (field_objectInfo_protectionStatus == NULL) { - ALOGE("Can't find MtpObjectInfo.mProtectionStatus"); - return -1; - } - field_objectInfo_compressedSize = env->GetFieldID(clazz, "mCompressedSize", "I"); - if (field_objectInfo_compressedSize == NULL) { - ALOGE("Can't find MtpObjectInfo.mCompressedSize"); - return -1; - } - field_objectInfo_thumbFormat = env->GetFieldID(clazz, "mThumbFormat", "I"); - if (field_objectInfo_thumbFormat == NULL) { - ALOGE("Can't find MtpObjectInfo.mThumbFormat"); - return -1; - } - field_objectInfo_thumbCompressedSize = env->GetFieldID(clazz, "mThumbCompressedSize", "I"); - if (field_objectInfo_thumbCompressedSize == NULL) { - ALOGE("Can't find MtpObjectInfo.mThumbCompressedSize"); - return -1; - } - field_objectInfo_thumbPixWidth = env->GetFieldID(clazz, "mThumbPixWidth", "I"); - if (field_objectInfo_thumbPixWidth == NULL) { - ALOGE("Can't find MtpObjectInfo.mThumbPixWidth"); - return -1; - } - field_objectInfo_thumbPixHeight = env->GetFieldID(clazz, "mThumbPixHeight", "I"); - if (field_objectInfo_thumbPixHeight == NULL) { - ALOGE("Can't find MtpObjectInfo.mThumbPixHeight"); - return -1; - } - field_objectInfo_imagePixWidth = env->GetFieldID(clazz, "mImagePixWidth", "I"); - if (field_objectInfo_imagePixWidth == NULL) { - ALOGE("Can't find MtpObjectInfo.mImagePixWidth"); - return -1; - } - field_objectInfo_imagePixHeight = env->GetFieldID(clazz, "mImagePixHeight", "I"); - if (field_objectInfo_imagePixHeight == NULL) { - ALOGE("Can't find MtpObjectInfo.mImagePixHeight"); - return -1; - } - field_objectInfo_imagePixDepth = env->GetFieldID(clazz, "mImagePixDepth", "I"); - if (field_objectInfo_imagePixDepth == NULL) { - ALOGE("Can't find MtpObjectInfo.mImagePixDepth"); - return -1; - } - field_objectInfo_parent = env->GetFieldID(clazz, "mParent", "I"); - if (field_objectInfo_parent == NULL) { - ALOGE("Can't find MtpObjectInfo.mParent"); - return -1; - } - field_objectInfo_associationType = env->GetFieldID(clazz, "mAssociationType", "I"); - if (field_objectInfo_associationType == NULL) { - ALOGE("Can't find MtpObjectInfo.mAssociationType"); - return -1; - } - field_objectInfo_associationDesc = env->GetFieldID(clazz, "mAssociationDesc", "I"); - if (field_objectInfo_associationDesc == NULL) { - ALOGE("Can't find MtpObjectInfo.mAssociationDesc"); - return -1; - } - field_objectInfo_sequenceNumber = env->GetFieldID(clazz, "mSequenceNumber", "I"); - if (field_objectInfo_sequenceNumber == NULL) { - ALOGE("Can't find MtpObjectInfo.mSequenceNumber"); - return -1; - } - field_objectInfo_name = env->GetFieldID(clazz, "mName", "Ljava/lang/String;"); - if (field_objectInfo_name == NULL) { - ALOGE("Can't find MtpObjectInfo.mName"); - return -1; - } - field_objectInfo_dateCreated = env->GetFieldID(clazz, "mDateCreated", "J"); - if (field_objectInfo_dateCreated == NULL) { - ALOGE("Can't find MtpObjectInfo.mDateCreated"); - return -1; - } - field_objectInfo_dateModified = env->GetFieldID(clazz, "mDateModified", "J"); - if (field_objectInfo_dateModified == NULL) { - ALOGE("Can't find MtpObjectInfo.mDateModified"); - return -1; - } - field_objectInfo_keywords = env->GetFieldID(clazz, "mKeywords", "Ljava/lang/String;"); - if (field_objectInfo_keywords == NULL) { - ALOGE("Can't find MtpObjectInfo.mKeywords"); - return -1; - } - clazz_objectInfo = (jclass)env->NewGlobalRef(clazz); - - clazz = env->FindClass("android/mtp/MtpEvent"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpEvent"); - return -1; - } - constructor_event = env->GetMethodID(clazz, "<init>", "()V"); - if (constructor_event == NULL) { - ALOGE("Can't find android/mtp/MtpEvent constructor"); - return -1; - } - field_event_eventCode = env->GetFieldID(clazz, "mEventCode", "I"); - if (field_event_eventCode == NULL) { - ALOGE("Can't find MtpObjectInfo.mEventCode"); - return -1; - } - field_event_parameter1 = env->GetFieldID(clazz, "mParameter1", "I"); - if (field_event_parameter1 == NULL) { - ALOGE("Can't find MtpObjectInfo.mParameter1"); - return -1; - } - field_event_parameter2 = env->GetFieldID(clazz, "mParameter2", "I"); - if (field_event_parameter2 == NULL) { - ALOGE("Can't find MtpObjectInfo.mParameter2"); - return -1; - } - field_event_parameter3 = env->GetFieldID(clazz, "mParameter3", "I"); - if (field_event_parameter3 == NULL) { - ALOGE("Can't find MtpObjectInfo.mParameter3"); - return -1; - } - clazz_event = (jclass)env->NewGlobalRef(clazz); - - clazz = env->FindClass("android/mtp/MtpDevice"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpDevice"); - return -1; - } - field_context = env->GetFieldID(clazz, "mNativeContext", "J"); - if (field_context == NULL) { - ALOGE("Can't find MtpDevice.mNativeContext"); - return -1; - } - clazz = env->FindClass("java/io/IOException"); - if (clazz == NULL) { - ALOGE("Can't find java.io.IOException"); - return -1; - } - clazz_io_exception = (jclass)env->NewGlobalRef(clazz); - clazz = env->FindClass("android/os/OperationCanceledException"); - if (clazz == NULL) { - ALOGE("Can't find android.os.OperationCanceledException"); - return -1; - } - clazz_operation_canceled_exception = (jclass)env->NewGlobalRef(clazz); - return AndroidRuntime::registerNativeMethods(env, "android/mtp/MtpDevice", gMethods, NELEM(gMethods)); } diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp index 8a1ae9252ca3..b4844f79b540 100644 --- a/media/jni/android_mtp_MtpServer.cpp +++ b/media/jni/android_mtp_MtpServer.cpp @@ -24,6 +24,7 @@ #include <fcntl.h> #include <utils/threads.h> +#include "core_jni_helpers.h" #include "jni.h" #include <nativehelper/JNIPlatformHelp.h> #include "android_runtime/AndroidRuntime.h" @@ -34,6 +35,8 @@ using namespace android; +static Mutex sMutex; + // MtpServer fields static jfieldID field_MtpServer_nativeContext; @@ -44,7 +47,25 @@ static jfieldID field_MtpStorage_description; static jfieldID field_MtpStorage_removable; static jfieldID field_MtpStorage_maxFileSize; -static Mutex sMutex; +// Initializer for the jfieldIDs above. This method must be invoked before accessing MtpServer and +// MtpStorage fields. +static void initializeJavaIDs(JNIEnv* env) { + static std::once_flag sJniInitialized; + + std::call_once(sJniInitialized, [](JNIEnv *env) { + const jclass storage_clazz = FindClassOrDie(env, "android/mtp/MtpStorage"); + field_MtpStorage_storageId = GetFieldIDOrDie(env, storage_clazz, "mStorageId", "I"); + field_MtpStorage_path = + GetFieldIDOrDie(env, storage_clazz, "mPath", "Ljava/lang/String;"); + field_MtpStorage_description = + GetFieldIDOrDie(env, storage_clazz, "mDescription", "Ljava/lang/String;"); + field_MtpStorage_removable = GetFieldIDOrDie(env, storage_clazz, "mRemovable", "Z"); + field_MtpStorage_maxFileSize = GetFieldIDOrDie(env, storage_clazz, "mMaxFileSize", "J"); + + const jclass server_clazz = FindClassOrDie(env, "android/mtp/MtpServer"); + field_MtpServer_nativeContext = GetFieldIDOrDie(env, server_clazz, "mNativeContext", "J"); + }, env); +} // ---------------------------------------------------------------------------- @@ -52,6 +73,7 @@ static Mutex sMutex; extern IMtpDatabase* getMtpDatabase(JNIEnv *env, jobject database); static inline MtpServer* getMtpServer(JNIEnv *env, jobject thiz) { + initializeJavaIDs(env); return (MtpServer*)env->GetLongField(thiz, field_MtpServer_nativeContext); } @@ -60,6 +82,8 @@ android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, job jboolean usePtp, jstring deviceInfoManufacturer, jstring deviceInfoModel, jstring deviceInfoDeviceVersion, jstring deviceInfoSerialNumber) { + initializeJavaIDs(env); + const char *deviceInfoManufacturerStr = env->GetStringUTFChars(deviceInfoManufacturer, NULL); const char *deviceInfoModelStr = env->GetStringUTFChars(deviceInfoModel, NULL); const char *deviceInfoDeviceVersionStr = env->GetStringUTFChars(deviceInfoDeviceVersion, NULL); @@ -224,50 +248,6 @@ static const JNINativeMethod gMethods[] = { int register_android_mtp_MtpServer(JNIEnv *env) { - jclass clazz; - - clazz = env->FindClass("android/mtp/MtpStorage"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpStorage"); - return -1; - } - field_MtpStorage_storageId = env->GetFieldID(clazz, "mStorageId", "I"); - if (field_MtpStorage_storageId == NULL) { - ALOGE("Can't find MtpStorage.mStorageId"); - return -1; - } - field_MtpStorage_path = env->GetFieldID(clazz, "mPath", "Ljava/lang/String;"); - if (field_MtpStorage_path == NULL) { - ALOGE("Can't find MtpStorage.mPath"); - return -1; - } - field_MtpStorage_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;"); - if (field_MtpStorage_description == NULL) { - ALOGE("Can't find MtpStorage.mDescription"); - return -1; - } - field_MtpStorage_removable = env->GetFieldID(clazz, "mRemovable", "Z"); - if (field_MtpStorage_removable == NULL) { - ALOGE("Can't find MtpStorage.mRemovable"); - return -1; - } - field_MtpStorage_maxFileSize = env->GetFieldID(clazz, "mMaxFileSize", "J"); - if (field_MtpStorage_maxFileSize == NULL) { - ALOGE("Can't find MtpStorage.mMaxFileSize"); - return -1; - } - - clazz = env->FindClass("android/mtp/MtpServer"); - if (clazz == NULL) { - ALOGE("Can't find android/mtp/MtpServer"); - return -1; - } - field_MtpServer_nativeContext = env->GetFieldID(clazz, "mNativeContext", "J"); - if (field_MtpServer_nativeContext == NULL) { - ALOGE("Can't find MtpServer.mNativeContext"); - return -1; - } - return AndroidRuntime::registerNativeMethods(env, "android/mtp/MtpServer", gMethods, NELEM(gMethods)); } diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp index 40e4c54c2921..c2fc91d5cfea 100644 --- a/media/jni/audioeffect/Android.bp +++ b/media/jni/audioeffect/Android.bp @@ -1,3 +1,12 @@ +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_media_jni_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_media_jni_license"], +} + cc_library_shared { name: "libaudioeffect_jni", diff --git a/media/jni/soundpool/Android.bp b/media/jni/soundpool/Android.bp index 6141308a8fdb..b3406cd89046 100644 --- a/media/jni/soundpool/Android.bp +++ b/media/jni/soundpool/Android.bp @@ -1,3 +1,22 @@ +package { + default_applicable_licenses: [ + "frameworks_base_media_jni_soundpool_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_media_jni_soundpool_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + tidy_errors = [ // https://clang.llvm.org/extra/clang-tidy/checks/list.html // For many categories, the checks are too many to specify individually. @@ -26,26 +45,30 @@ tidy_errors = [ "modernize-return-braced-init-list", "modernize-shrink-to-fit", "modernize-unary-static-assert", - "modernize-use-auto", // debatable - auto can obscure type + // "modernize-use-auto", // found in StreamManager.h, debatable - auto can obscure type "modernize-use-bool-literals", "modernize-use-default-member-init", "modernize-use-emplace", "modernize-use-equals-default", "modernize-use-equals-delete", - "modernize-use-nodiscard", + // "modernize-use-nodiscard", // found in SteamManager.h "modernize-use-noexcept", "modernize-use-nullptr", "modernize-use-override", //"modernize-use-trailing-return-type", // not necessarily more readable "modernize-use-transparent-functors", "modernize-use-uncaught-exceptions", - "modernize-use-using", + //"modernize-use-using", // found in SoundManager.h "performance-*", // Remove some pedantic stylistic requirements. "-google-readability-casting", // C++ casts not always necessary and may be verbose "-google-readability-todo", // do not require TODO(info) "-google-build-using-namespace", // Reenable and fix later. + + "-google-explicit-constructor", // found in StreamManager.h + "-misc-non-private-member-variables-in-classes", // found in SoundManager.h + "-performance-unnecessary-value-param", // found in StreamManager.h ] cc_defaults { @@ -77,8 +100,7 @@ cc_defaults { tidy_checks: tidy_errors, tidy_checks_as_errors: tidy_errors, tidy_flags: [ - "-format-style='file'", - "--header-filter='frameworks/base/media/jni/soundpool'", + "-format-style=file", ], } diff --git a/media/jni/soundpool/tests/Android.bp b/media/jni/soundpool/tests/Android.bp index 52f59ed69503..7d31c106f9c4 100644 --- a/media/jni/soundpool/tests/Android.bp +++ b/media/jni/soundpool/tests/Android.bp @@ -1,3 +1,14 @@ +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_media_jni_soundpool_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: [ + "frameworks_base_media_jni_soundpool_license", + ], +} + cc_binary { name: "soundpool_stress", host_supported: false, diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp index 359ef364083c..78cb5b003a38 100644 --- a/media/jni/tuner/DemuxClient.cpp +++ b/media/jni/tuner/DemuxClient.cpp @@ -216,7 +216,7 @@ Result DemuxClient::disconnectCiCam() { Result DemuxClient::close() { if (mTunerDemux != NULL) { Status s = mTunerDemux->close(); - mDemux = NULL; + mTunerDemux = NULL; return ClientHelper::getServiceSpecificErrorCode(s); } diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h index bbabc282464a..736b8f9cd4b4 100644 --- a/media/jni/tuner/FilterClient.h +++ b/media/jni/tuner/FilterClient.h @@ -267,9 +267,6 @@ private: AidlMQ* mFilterMQ; EventFlag* mFilterMQEventFlag; - sp<FilterClientCallback> mCallback; - sp<HidlFilterCallback> mHidlCallback; - native_handle_t* mAvSharedHandle; uint64_t mAvSharedMemSize; bool mIsMediaFilter; diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp index 9e3664275dac..f54e2663843c 100644 --- a/media/jni/tuner/FrontendClient.cpp +++ b/media/jni/tuner/FrontendClient.cpp @@ -78,8 +78,6 @@ namespace android { FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int type) { mTunerFrontend = tunerFrontend; - mAidlCallback = NULL; - mHidlCallback = NULL; mType = type; } @@ -87,22 +85,21 @@ FrontendClient::~FrontendClient() { mTunerFrontend = NULL; mFrontend = NULL; mFrontend_1_1 = NULL; - mAidlCallback = NULL; - mHidlCallback = NULL; mId = -1; mType = -1; } Result FrontendClient::setCallback(sp<FrontendClientCallback> frontendClientCallback) { if (mTunerFrontend != NULL) { - mAidlCallback = ::ndk::SharedRefBase::make<TunerFrontendCallback>(frontendClientCallback); - mAidlCallback->setFrontendType(mType); - Status s = mTunerFrontend->setCallback(mAidlCallback); + shared_ptr<TunerFrontendCallback> aidlCallback = + ::ndk::SharedRefBase::make<TunerFrontendCallback>(frontendClientCallback); + aidlCallback->setFrontendType(mType); + Status s = mTunerFrontend->setCallback(aidlCallback); return ClientHelper::getServiceSpecificErrorCode(s); } - mHidlCallback = new HidlFrontendCallback(frontendClientCallback); - return mFrontend->setCallback(mHidlCallback); + sp<HidlFrontendCallback> hidlCallback = new HidlFrontendCallback(frontendClientCallback); + return mFrontend->setCallback(hidlCallback); } void FrontendClient::setHidlFrontend(sp<IFrontend> frontend) { @@ -591,14 +588,15 @@ vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt( break; } case TunerFrontendStatus::codeRates: { - int size = s.get<TunerFrontendStatus::codeRates>().size(); - status.codeRates().resize(size); - for (int i = 0; i < size; i++) { - auto aidlCodeRate = s.get<TunerFrontendStatus::codeRates>()[i]; - status.codeRates()[i] = - static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate); + vector<hardware::tv::tuner::V1_1::FrontendInnerFec> codeRates; + for (auto aidlCodeRate : s.get<TunerFrontendStatus::codeRates>()) { + codeRates.push_back( + static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate)); + } + if (codeRates.size() > 0) { + status.codeRates(codeRates); + hidlStatus.push_back(status); } - hidlStatus.push_back(status); break; } case TunerFrontendStatus::bandwidth: { diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h index f71616cb32b7..1dd950eee9e1 100644 --- a/media/jni/tuner/FrontendClient.h +++ b/media/jni/tuner/FrontendClient.h @@ -226,9 +226,6 @@ private: */ sp<::android::hardware::tv::tuner::V1_1::IFrontend> mFrontend_1_1; - shared_ptr<TunerFrontendCallback> mAidlCallback; - sp<HidlFrontendCallback> mHidlCallback; - int mId; int mType; }; diff --git a/media/jni/tuner/LnbClient.cpp b/media/jni/tuner/LnbClient.cpp index 5b6e46eba418..073c49a2d6ac 100644 --- a/media/jni/tuner/LnbClient.cpp +++ b/media/jni/tuner/LnbClient.cpp @@ -45,14 +45,15 @@ void LnbClient::setHidlLnb(sp<ILnb> lnb) { Result LnbClient::setCallback(sp<LnbClientCallback> cb) { if (mTunerLnb != NULL) { - mAidlCallback = ::ndk::SharedRefBase::make<TunerLnbCallback>(cb); - Status s = mTunerLnb->setCallback(mAidlCallback); + shared_ptr<TunerLnbCallback> aidlCallback = + ::ndk::SharedRefBase::make<TunerLnbCallback>(cb); + Status s = mTunerLnb->setCallback(aidlCallback); return ClientHelper::getServiceSpecificErrorCode(s); } if (mLnb != NULL) { - mHidlCallback = new HidlLnbCallback(cb); - return mLnb->setCallback(mHidlCallback); + sp<HidlLnbCallback> hidlCallback = new HidlLnbCallback(cb); + return mLnb->setCallback(hidlCallback); } return Result::INVALID_STATE; diff --git a/media/jni/tuner/LnbClient.h b/media/jni/tuner/LnbClient.h index 465dc2331ecf..7c6118c98eb5 100644 --- a/media/jni/tuner/LnbClient.h +++ b/media/jni/tuner/LnbClient.h @@ -126,9 +126,6 @@ private: */ sp<ILnb> mLnb; - shared_ptr<TunerLnbCallback> mAidlCallback; - sp<HidlLnbCallback> mHidlCallback; - LnbId mId; }; } // namespace android diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp index cf17ed6f4383..c9a7e8340dda 100644 --- a/media/jni/tuner/TunerClient.cpp +++ b/media/jni/tuner/TunerClient.cpp @@ -49,8 +49,6 @@ TunerClient::TunerClient() { if (mTunerService == NULL) { ALOGE("Failed to get tuner service"); } else { - // TODO: b/178124017 update TRM in TunerService independently. - mTunerService->updateTunerResources(); mTunerService->getTunerHalVersion(&mTunerVersion); } } diff --git a/media/lib/remotedisplay/Android.bp b/media/lib/remotedisplay/Android.bp index 5f4b930f350e..bfb0cb854be3 100644 --- a/media/lib/remotedisplay/Android.bp +++ b/media/lib/remotedisplay/Android.bp @@ -14,6 +14,15 @@ // 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_sdk_library { name: "com.android.media.remotedisplay", srcs: ["java/**/*.java"], diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp index 3b2578754087..6504176eb6f1 100644 --- a/media/lib/signer/Android.bp +++ b/media/lib/signer/Android.bp @@ -14,6 +14,15 @@ // 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_sdk_library { name: "com.android.mediadrm.signer", srcs: ["java/**/*.java"], diff --git a/media/lib/tvremote/Android.bp b/media/lib/tvremote/Android.bp index c5d14191c6c8..a58f677c3c30 100644 --- a/media/lib/tvremote/Android.bp +++ b/media/lib/tvremote/Android.bp @@ -14,6 +14,15 @@ // 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_sdk_library { name: "com.android.media.tv.remoteprovider", srcs: ["java/**/*.java"], diff --git a/media/lib/tvremote/tests/Android.bp b/media/lib/tvremote/tests/Android.bp index f00eed070798..f02cfc393c81 100644 --- a/media/lib/tvremote/tests/Android.bp +++ b/media/lib/tvremote/tests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "TvRemoteTests", srcs: ["src/**/*.java"], diff --git a/media/mca/filterfw/Android.bp b/media/mca/filterfw/Android.bp index 0e0ecf331afc..ef3583f1a77f 100644 --- a/media/mca/filterfw/Android.bp +++ b/media/mca/filterfw/Android.bp @@ -13,6 +13,15 @@ // 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"], +} + cc_library_shared { name: "libfilterfw", diff --git a/media/mca/filterfw/native/Android.bp b/media/mca/filterfw/native/Android.bp index 7a8a6a18664e..7e4a34e8d934 100644 --- a/media/mca/filterfw/native/Android.bp +++ b/media/mca/filterfw/native/Android.bp @@ -16,6 +16,15 @@ //#################### // Build module libfilterfw_static +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"], +} + cc_library_static { name: "libfilterfw_native", diff --git a/media/mca/filterpacks/Android.bp b/media/mca/filterpacks/Android.bp index 34fb27dd1663..b50df6eb572c 100644 --- a/media/mca/filterpacks/Android.bp +++ b/media/mca/filterpacks/Android.bp @@ -13,6 +13,15 @@ // 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"], +} + cc_library_static { name: "libfilterpack_base", srcs: [ diff --git a/media/mca/samples/CameraEffectsRecordingSample/Android.bp b/media/mca/samples/CameraEffectsRecordingSample/Android.bp index 96e81ab9b9cc..541660c1e4a2 100644 --- a/media/mca/samples/CameraEffectsRecordingSample/Android.bp +++ b/media/mca/samples/CameraEffectsRecordingSample/Android.bp @@ -15,6 +15,15 @@ // Build activity +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"], +} + android_test { name: "CameraEffectsRecordingSample", srcs: ["**/*.java"], @@ -23,4 +32,3 @@ android_test { enabled: false, }, } - diff --git a/media/mca/tests/Android.bp b/media/mca/tests/Android.bp index 6b11dd9db7ba..f02b4c0a4bfb 100644 --- a/media/mca/tests/Android.bp +++ b/media/mca/tests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "CameraEffectsTests", libs: [ diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp index 2da45b6a78be..7acb8c744ba7 100644 --- a/media/native/midi/Android.bp +++ b/media/native/midi/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + cc_library_shared { name: "libamidi", diff --git a/media/packages/BluetoothMidiService/Android.bp b/media/packages/BluetoothMidiService/Android.bp index 25c34c3631dc..94a7a17638a5 100644 --- a/media/packages/BluetoothMidiService/Android.bp +++ b/media/packages/BluetoothMidiService/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_library { name: "BluetoothMidiLib", srcs: [ diff --git a/media/packages/BluetoothMidiService/tests/unit/Android.bp b/media/packages/BluetoothMidiService/tests/unit/Android.bp index fa4612bc22e7..67c7e4230f30 100644 --- a/media/packages/BluetoothMidiService/tests/unit/Android.bp +++ b/media/packages/BluetoothMidiService/tests/unit/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "BluetoothMidiTests", srcs: ["src/**/*.java"], diff --git a/media/tests/AudioPolicyTest/Android.bp b/media/tests/AudioPolicyTest/Android.bp index ed3383752695..95d1c6ccb67c 100644 --- a/media/tests/AudioPolicyTest/Android.bp +++ b/media/tests/AudioPolicyTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "audiopolicytest", srcs: ["**/*.java"], diff --git a/media/tests/CameraBrowser/Android.bp b/media/tests/CameraBrowser/Android.bp index 8e3ca1943b17..1408640a6b0d 100644 --- a/media/tests/CameraBrowser/Android.bp +++ b/media/tests/CameraBrowser/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "CameraBrowser", srcs: ["**/*.java"], diff --git a/media/tests/EffectsTest/Android.bp b/media/tests/EffectsTest/Android.bp index 214e8c02f742..644e453b8956 100644 --- a/media/tests/EffectsTest/Android.bp +++ b/media/tests/EffectsTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "EffectsTest", srcs: ["**/*.java"], diff --git a/media/tests/MediaDump/Android.bp b/media/tests/MediaDump/Android.bp index 0eba8b24f6cd..f54b97a06558 100644 --- a/media/tests/MediaDump/Android.bp +++ b/media/tests/MediaDump/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "MediaDump", // Only compile source java files in this apk. diff --git a/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp index 3f1954a0874a..48d56d898138 100644 --- a/media/tests/MediaFrameworkTest/Android.bp +++ b/media/tests/MediaFrameworkTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "mediaframeworktest", srcs: ["**/*.java"], diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioAttributesUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioAttributesUnitTest.java new file mode 100644 index 000000000000..3a4bec046da9 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioAttributesUnitTest.java @@ -0,0 +1,81 @@ +/* + * 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.mediaframeworktest.unit; + +import static org.junit.Assert.assertEquals; + +import android.media.AudioAttributes; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class AudioAttributesUnitTest { + + @Test + public void testUsageToString_returnCorrectStrings() { + assertEquals("USAGE_UNKNOWN", AudioAttributes.usageToString(AudioAttributes.USAGE_UNKNOWN)); + assertEquals("USAGE_MEDIA", AudioAttributes.usageToString(AudioAttributes.USAGE_MEDIA)); + assertEquals("USAGE_VOICE_COMMUNICATION", + AudioAttributes.usageToString(AudioAttributes.USAGE_VOICE_COMMUNICATION)); + assertEquals("USAGE_VOICE_COMMUNICATION_SIGNALLING", + AudioAttributes.usageToString( + AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)); + assertEquals("USAGE_ALARM", AudioAttributes.usageToString(AudioAttributes.USAGE_ALARM)); + assertEquals("USAGE_NOTIFICATION", + AudioAttributes.usageToString(AudioAttributes.USAGE_NOTIFICATION)); + assertEquals("USAGE_NOTIFICATION_RINGTONE", + AudioAttributes.usageToString(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)); + assertEquals("USAGE_NOTIFICATION_COMMUNICATION_REQUEST", + AudioAttributes.usageToString( + AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST)); + assertEquals("USAGE_NOTIFICATION_COMMUNICATION_INSTANT", + AudioAttributes.usageToString( + AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT)); + assertEquals("USAGE_NOTIFICATION_COMMUNICATION_DELAYED", + AudioAttributes.usageToString( + AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED)); + assertEquals("USAGE_NOTIFICATION_EVENT", + AudioAttributes.usageToString(AudioAttributes.USAGE_NOTIFICATION_EVENT)); + assertEquals("USAGE_ASSISTANCE_ACCESSIBILITY", + AudioAttributes.usageToString(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)); + assertEquals("USAGE_ASSISTANCE_NAVIGATION_GUIDANCE", + AudioAttributes.usageToString( + AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)); + assertEquals("USAGE_ASSISTANCE_SONIFICATION", + AudioAttributes.usageToString(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)); + assertEquals("USAGE_GAME", AudioAttributes.usageToString(AudioAttributes.USAGE_GAME)); + assertEquals("USAGE_ASSISTANT", + AudioAttributes.usageToString(AudioAttributes.USAGE_ASSISTANT)); + assertEquals("USAGE_CALL_ASSISTANT", + AudioAttributes.usageToString(AudioAttributes.USAGE_CALL_ASSISTANT)); + assertEquals("USAGE_EMERGENCY", + AudioAttributes.usageToString(AudioAttributes.USAGE_EMERGENCY)); + assertEquals("USAGE_SAFETY", AudioAttributes.usageToString(AudioAttributes.USAGE_SAFETY)); + assertEquals("USAGE_VEHICLE_STATUS", + AudioAttributes.usageToString(AudioAttributes.USAGE_VEHICLE_STATUS)); + assertEquals("USAGE_ANNOUNCEMENT", + AudioAttributes.usageToString(AudioAttributes.USAGE_ANNOUNCEMENT)); + } + + @Test + public void testUsageToString_unknownUsage() { + assertEquals("unknown usage -1", AudioAttributes.usageToString(-1)); + } +} diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp index a439b79fa2f0..d41bc02906ab 100644 --- a/media/tests/MediaRouter/Android.bp +++ b/media/tests/MediaRouter/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "mediaroutertest", @@ -17,4 +26,4 @@ android_test { platform_apis: true, certificate: "platform", -}
\ No newline at end of file +} diff --git a/media/tests/MtpTests/Android.bp b/media/tests/MtpTests/Android.bp index 7d2c7c693fa5..3016873e1de8 100644 --- a/media/tests/MtpTests/Android.bp +++ b/media/tests/MtpTests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "MtpTests", srcs: ["**/*.java"], diff --git a/media/tests/ScoAudioTest/Android.bp b/media/tests/ScoAudioTest/Android.bp index ad2b91716145..f8a893bf7f2c 100644 --- a/media/tests/ScoAudioTest/Android.bp +++ b/media/tests/ScoAudioTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "scoaudiotest", platform_apis: true, diff --git a/media/tests/SoundPoolTest/Android.bp b/media/tests/SoundPoolTest/Android.bp index 473f531f675b..0a50106d52c8 100644 --- a/media/tests/SoundPoolTest/Android.bp +++ b/media/tests/SoundPoolTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "SoundPoolTest", srcs: ["**/*.java"], diff --git a/media/tests/TunerTest/Android.bp b/media/tests/TunerTest/Android.bp index 5c3e9ab9812f..8e8816cee876 100644 --- a/media/tests/TunerTest/Android.bp +++ b/media/tests/TunerTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "mediatunertest", diff --git a/media/tests/audiotests/Android.bp b/media/tests/audiotests/Android.bp index 5db0ab0cde6d..c52c0336979c 100644 --- a/media/tests/audiotests/Android.bp +++ b/media/tests/audiotests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_test { name: "shared_mem_test", gtest: false, diff --git a/media/tests/players/Android.bp b/media/tests/players/Android.bp index 23c5f04c93a6..3b6df707badb 100644 --- a/media/tests/players/Android.bp +++ b/media/tests/players/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + cc_test_library { name: "invoke_mock_media_player", srcs: ["invoke_mock_media_player.cpp"], diff --git a/mime/Android.bp b/mime/Android.bp index 23a8fbf5059c..a3ea65cb2efe 100644 --- a/mime/Android.bp +++ b/mime/Android.bp @@ -13,6 +13,15 @@ // 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_defaults { name: "mimemap-defaults", srcs: [ diff --git a/native/android/Android.bp b/native/android/Android.bp index 253ef679a879..9566b9242fc7 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -13,6 +13,15 @@ // limitations under the License. // The headers module is in frameworks/native/Android.bp. +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"], +} + ndk_library { name: "libandroid", symbol_file: "libandroid.map.txt", diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index e51add276647..e8cf63f64572 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -27,14 +27,13 @@ #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> -#include <ui/HdrCapabilities.h> +#include <ui/DynamicDisplayInfo.h> #include <utils/Timers.h> using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; using namespace android; -using android::hardware::configstore::V1_0::ISurfaceFlingerConfigs; using Transaction = SurfaceComposerClient::Transaction; @@ -72,14 +71,13 @@ static bool getHdrSupport(const sp<SurfaceControl>& surfaceControl) { return false; } - HdrCapabilities hdrCapabilities; - status_t err = client->getHdrCapabilities(display, &hdrCapabilities); - if (err) { + ui::DynamicDisplayInfo info; + if (status_t err = client->getDynamicDisplayInfo(display, &info); err != NO_ERROR) { ALOGE("unable to get hdr capabilities"); - return false; + return err; } - return !hdrCapabilities.getSupportedHdrTypes().empty(); + return !info.hdrCapabilities.getSupportedHdrTypes().empty(); } static bool isDataSpaceValid(const sp<SurfaceControl>& surfaceControl, ADataSpace dataSpace) { diff --git a/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp index 1a51218616d2..fdca3fad2383 100644 --- a/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp +++ b/native/android/tests/activitymanager/UidImportanceHelperApps/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "UidImportanceHelperApp", manifest: "HelperAppManifest.xml", @@ -8,4 +17,3 @@ android_test_helper_app { "general-tests", ], } - diff --git a/native/android/tests/activitymanager/nativeTests/Android.bp b/native/android/tests/activitymanager/nativeTests/Android.bp index d4b5015ad8f3..528ac12c16b7 100644 --- a/native/android/tests/activitymanager/nativeTests/Android.bp +++ b/native/android/tests/activitymanager/nativeTests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_test { name: "ActivityManagerNativeTestCases", diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp index 3d633ea7089d..1709dfd973d6 100644 --- a/native/graphics/jni/Android.bp +++ b/native/graphics/jni/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + cc_library_shared { name: "libjnigraphics", diff --git a/native/webview/loader/Android.bp b/native/webview/loader/Android.bp index dfa5bdde0785..bb9b8903273e 100644 --- a/native/webview/loader/Android.bp +++ b/native/webview/loader/Android.bp @@ -17,6 +17,15 @@ // Loader library which handles address space reservation and relro sharing. // Does NOT link any native chromium code. +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"], +} + cc_library_shared { name: "libwebviewchromium_loader", diff --git a/native/webview/plat_support/Android.bp b/native/webview/plat_support/Android.bp index 1a3b36d046e1..2e94e8452fdd 100644 --- a/native/webview/plat_support/Android.bp +++ b/native/webview/plat_support/Android.bp @@ -18,6 +18,38 @@ // Native support library (libwebviewchromium_plat_support.so) - does NOT link // any native chromium code. +package { + default_applicable_licenses: [ + "frameworks_base_native_webview_plat_support_license", + ], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// See: http://go/android-license-faq +license { + name: "frameworks_base_native_webview_plat_support_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "SPDX-license-identifier-BSD", + ], + license_text: [ + "LICENSE", + ], +} + cc_library_shared { name: "libwebviewchromium_plat_support", diff --git a/nfc-extras/Android.bp b/nfc-extras/Android.bp index cbacd48b080e..43b2830f544b 100644 --- a/nfc-extras/Android.bp +++ b/nfc-extras/Android.bp @@ -14,6 +14,15 @@ // 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_sdk_library { name: "com.android.nfc_extras", srcs: ["java/**/*.java"], diff --git a/nfc-extras/tests/Android.bp b/nfc-extras/tests/Android.bp index fc52006d14d2..4c1f2fb77e72 100644 --- a/nfc-extras/tests/Android.bp +++ b/nfc-extras/tests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "NfcExtrasTests", diff --git a/obex/Android.bp b/obex/Android.bp index 6558eb36d001..95eac81e80be 100644 --- a/obex/Android.bp +++ b/obex/Android.bp @@ -14,6 +14,36 @@ // limitations under the License. // +package { + default_applicable_licenses: ["frameworks_base_obex_license"], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// See: http://go/android-license-faq +license { + name: "frameworks_base_obex_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "SPDX-license-identifier-BSD", + ], + license_text: [ + "NOTICE", + ], +} + java_sdk_library { name: "javax.obex", srcs: ["javax/**/*.java"], diff --git a/packages/AppPredictionLib/Android.bp b/packages/AppPredictionLib/Android.bp index e0f4dedc9368..5a68fdc9ae75 100644 --- a/packages/AppPredictionLib/Android.bp +++ b/packages/AppPredictionLib/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_library { name: "app_prediction", diff --git a/packages/BackupEncryption/Android.bp b/packages/BackupEncryption/Android.bp index 3a078d24e535..0244f289b40c 100644 --- a/packages/BackupEncryption/Android.bp +++ b/packages/BackupEncryption/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_app { name: "BackupEncryption", defaults: ["platform_app_defaults"], diff --git a/packages/BackupEncryption/test/robolectric-integration/Android.bp b/packages/BackupEncryption/test/robolectric-integration/Android.bp index 67365df4b28f..c842e425fd6f 100644 --- a/packages/BackupEncryption/test/robolectric-integration/Android.bp +++ b/packages/BackupEncryption/test/robolectric-integration/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_robolectric_test { name: "BackupEncryptionRoboIntegTests", srcs: [ diff --git a/packages/BackupEncryption/test/robolectric/Android.bp b/packages/BackupEncryption/test/robolectric/Android.bp index 2a36dcf0baba..7665d8f4e4c2 100644 --- a/packages/BackupEncryption/test/robolectric/Android.bp +++ b/packages/BackupEncryption/test/robolectric/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_robolectric_test { name: "BackupEncryptionRoboTests", srcs: [ diff --git a/packages/BackupEncryption/test/unittest/Android.bp b/packages/BackupEncryption/test/unittest/Android.bp index d7c510b57518..f005170884c8 100644 --- a/packages/BackupEncryption/test/unittest/Android.bp +++ b/packages/BackupEncryption/test/unittest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "BackupEncryptionUnitTests", srcs: ["src/**/*.java"], @@ -19,4 +28,4 @@ android_test { test_suites: ["device-tests"], instrumentation_for: "BackupEncryption", certificate: "platform", -}
\ No newline at end of file +} diff --git a/packages/BackupRestoreConfirmation/Android.bp b/packages/BackupRestoreConfirmation/Android.bp index 6fe039d48357..ad3f4c12dfb1 100644 --- a/packages/BackupRestoreConfirmation/Android.bp +++ b/packages/BackupRestoreConfirmation/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_app { name: "BackupRestoreConfirmation", defaults: ["platform_app_defaults"], diff --git a/packages/CarrierDefaultApp/Android.bp b/packages/CarrierDefaultApp/Android.bp index c1b0b2da2cb5..fc753da19394 100644 --- a/packages/CarrierDefaultApp/Android.bp +++ b/packages/CarrierDefaultApp/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "CarrierDefaultApp", srcs: ["src/**/*.java"], diff --git a/packages/CarrierDefaultApp/tests/unit/Android.bp b/packages/CarrierDefaultApp/tests/unit/Android.bp index 5655abb046b2..54c9016ee53a 100644 --- a/packages/CarrierDefaultApp/tests/unit/Android.bp +++ b/packages/CarrierDefaultApp/tests/unit/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "CarrierDefaultAppUnitTests", certificate: "platform", diff --git a/packages/CompanionDeviceManager/Android.bp b/packages/CompanionDeviceManager/Android.bp index 354d2c7a6228..4a526501cf96 100644 --- a/packages/CompanionDeviceManager/Android.bp +++ b/packages/CompanionDeviceManager/Android.bp @@ -12,6 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: [ + "frameworks_base_packages_CompanionDeviceManager_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_packages_CompanionDeviceManager_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + android_app { name: "CompanionDeviceManager", defaults: ["platform_app_defaults"], diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml index 91bad7b4aa91..f9795c601431 100644 --- a/packages/CompanionDeviceManager/AndroidManifest.xml +++ b/packages/CompanionDeviceManager/AndroidManifest.xml @@ -41,13 +41,13 @@ android:supportsRtl="true"> <service - android:name=".DeviceDiscoveryService" + android:name=".CompanionDeviceDiscoveryService" android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE" android:exported="true"> </service> <activity - android:name=".DeviceChooserActivity" + android:name=".CompanionDeviceActivity" android:theme="@style/ChooserActivity" android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE" android:exported="true"> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index c30d4bf322cf..f1a98adc0ab2 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE import static java.util.Objects.requireNonNull; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.companion.AssociationRequest; @@ -31,41 +32,52 @@ import android.companion.CompanionDeviceManager; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.content.res.TypedArray; import android.database.DataSetObserver; +import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.Html; import android.util.Log; +import android.util.SparseArray; +import android.util.TypedValue; import android.view.Gravity; import android.view.View; -import android.widget.AdapterView; +import android.view.ViewGroup; +import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; -import com.android.companiondevicemanager.DeviceDiscoveryService.DeviceFilterPair; +import com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DeviceFilterPair; import com.android.internal.util.Preconditions; -public class DeviceChooserActivity extends Activity { +public class CompanionDeviceActivity extends Activity { private static final boolean DEBUG = false; - private static final String LOG_TAG = "DeviceChooserActivity"; + private static final String LOG_TAG = CompanionDeviceActivity.class.getSimpleName(); + + static CompanionDeviceActivity sInstance; View mLoadingIndicator = null; ListView mDeviceListView; private View mPairButton; private View mCancelButton; + DevicesAdapter mDevicesAdapter; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (DEBUG) Log.i(LOG_TAG, "Started with intent " + getIntent()); + Log.i(LOG_TAG, "Starting UI for " + getService().mRequest); if (getService().mDevicesFound.isEmpty()) { Log.e(LOG_TAG, "About to show UI, but no devices to show"); } getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + sInstance = this; String deviceProfile = getRequest().getDeviceProfile(); String profilePrivacyDisclaimer = emptyIfNull(getRequest() @@ -96,17 +108,14 @@ public class DeviceChooserActivity extends Activity { profileName, getCallingAppName()), 0)); mDeviceListView = findViewById(R.id.device_list); - final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter; - mDeviceListView.setAdapter(adapter); - mDeviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) { - getService().mSelectedDevice = - (DeviceFilterPair) adapterView.getItemAtPosition(pos); - adapter.notifyDataSetChanged(); - } + mDevicesAdapter = new DevicesAdapter(); + mDeviceListView.setAdapter(mDevicesAdapter); + mDeviceListView.setOnItemClickListener((adapterView, view, pos, l) -> { + getService().mSelectedDevice = + (DeviceFilterPair) adapterView.getItemAtPosition(pos); + mDevicesAdapter.notifyDataSetChanged(); }); - adapter.registerDataSetObserver(new DataSetObserver() { + mDevicesAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { onSelectionUpdate(); @@ -133,6 +142,12 @@ public class DeviceChooserActivity extends Activity { mCancelButton.setOnClickListener(v -> cancel()); } + static void notifyDevicesChanged() { + if (sInstance != null && sInstance.mDevicesAdapter != null && !sInstance.isFinishing()) { + sInstance.mDevicesAdapter.notifyDataSetChanged(); + } + } + private AssociationRequest getRequest() { return getService().mRequest; } @@ -156,6 +171,7 @@ public class DeviceChooserActivity extends Activity { } private void cancel() { + Log.i(LOG_TAG, "cancel()"); getService().onCancel(); setResult(RESULT_CANCELED); finish(); @@ -170,6 +186,14 @@ public class DeviceChooserActivity extends Activity { } } + @Override + protected void onDestroy() { + super.onDestroy(); + if (sInstance == this) { + sInstance = null; + } + } + private CharSequence getCallingAppName() { try { final PackageManager packageManager = getPackageManager(); @@ -217,15 +241,91 @@ public class DeviceChooserActivity extends Activity { } } - private DeviceDiscoveryService getService() { - return DeviceDiscoveryService.sInstance; + private CompanionDeviceDiscoveryService getService() { + return CompanionDeviceDiscoveryService.sInstance; } - protected void onDeviceConfirmed(DeviceFilterPair selectedDevice) { + protected void onDeviceConfirmed(DeviceFilterPair selectedDevice) { + Log.i(LOG_TAG, "onDeviceConfirmed(selectedDevice = " + selectedDevice + ")"); getService().onDeviceSelected( getCallingPackage(), getDeviceMacAddress(selectedDevice.device)); setResult(RESULT_OK, new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device)); finish(); } + + class DevicesAdapter extends BaseAdapter { + private final Drawable mBluetoothIcon = icon(android.R.drawable.stat_sys_data_bluetooth); + private final Drawable mWifiIcon = icon(com.android.internal.R.drawable.ic_wifi_signal_3); + + private SparseArray<Integer> mColors = new SparseArray(); + + private Drawable icon(int drawableRes) { + Drawable icon = getResources().getDrawable(drawableRes, null); + icon.setTint(Color.DKGRAY); + return icon; + } + + @Override + public View getView( + int position, + @Nullable View convertView, + @NonNull ViewGroup parent) { + TextView view = convertView instanceof TextView + ? (TextView) convertView + : newView(); + bind(view, getItem(position)); + return view; + } + + private void bind(TextView textView, DeviceFilterPair device) { + textView.setText(device.getDisplayName()); + textView.setBackgroundColor( + device.equals(getService().mSelectedDevice) + ? getColor(android.R.attr.colorControlHighlight) + : Color.TRANSPARENT); + textView.setCompoundDrawablesWithIntrinsicBounds( + device.device instanceof android.net.wifi.ScanResult + ? mWifiIcon + : mBluetoothIcon, + null, null, null); + textView.getCompoundDrawables()[0].setTint(getColor(android.R.attr.colorForeground)); + } + + private TextView newView() { + final TextView textView = new TextView(CompanionDeviceActivity.this); + textView.setTextColor(getColor(android.R.attr.colorForeground)); + final int padding = CompanionDeviceActivity.getPadding(getResources()); + textView.setPadding(padding, padding, padding, padding); + textView.setCompoundDrawablePadding(padding); + return textView; + } + + private int getColor(int colorAttr) { + if (mColors.contains(colorAttr)) { + return mColors.get(colorAttr); + } + TypedValue typedValue = new TypedValue(); + TypedArray a = obtainStyledAttributes(typedValue.data, new int[] { colorAttr }); + int result = a.getColor(0, 0); + a.recycle(); + mColors.put(colorAttr, result); + return result; + } + + @Override + public int getCount() { + return getService().mDevicesFound.size(); + } + + @Override + public DeviceFilterPair getItem(int position) { + return getService().mDevicesFound.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + } } diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java index 67d4b41f164c..e620dfbafa08 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java @@ -50,9 +50,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.drawable.Drawable; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.IBinder; @@ -60,12 +57,6 @@ import android.os.Parcelable; import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; -import android.util.SparseArray; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.TextView; import com.android.internal.infra.AndroidFuture; import com.android.internal.util.ArrayUtils; @@ -76,14 +67,14 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -public class DeviceDiscoveryService extends Service { +public class CompanionDeviceDiscoveryService extends Service { private static final boolean DEBUG = false; - private static final String LOG_TAG = "DeviceDiscoveryService"; + private static final String LOG_TAG = CompanionDeviceDiscoveryService.class.getSimpleName(); private static final long SCAN_TIMEOUT = 20000; - static DeviceDiscoveryService sInstance; + static CompanionDeviceDiscoveryService sInstance; private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; @@ -102,12 +93,12 @@ public class DeviceDiscoveryService extends Service { AssociationRequest mRequest; List<DeviceFilterPair> mDevicesFound; DeviceFilterPair mSelectedDevice; - DevicesAdapter mDevicesAdapter; IFindDeviceCallback mFindCallback; AndroidFuture<Association> mServiceCallback; boolean mIsScanning = false; - @Nullable DeviceChooserActivity mActivity = null; + @Nullable + CompanionDeviceActivity mActivity = null; private final ICompanionDeviceDiscoveryService mBinder = new ICompanionDeviceDiscoveryService.Stub() { @@ -125,7 +116,8 @@ public class DeviceDiscoveryService extends Service { mFindCallback = findCallback; mServiceCallback = serviceCallback; Handler.getMain().sendMessage(obtainMessage( - DeviceDiscoveryService::startDiscovery, DeviceDiscoveryService.this, request)); + CompanionDeviceDiscoveryService::startDiscovery, + CompanionDeviceDiscoveryService.this, request)); } }; @@ -151,7 +143,6 @@ public class DeviceDiscoveryService extends Service { mWifiManager = getSystemService(WifiManager.class); mDevicesFound = new ArrayList<>(); - mDevicesAdapter = new DevicesAdapter(); sInstance = this; } @@ -165,7 +156,8 @@ public class DeviceDiscoveryService extends Service { mWifiFilters = CollectionUtils.filter(mFilters, WifiDeviceFilter.class); mBluetoothFilters = CollectionUtils.filter(mFilters, BluetoothDeviceFilter.class); mBLEFilters = CollectionUtils.filter(mFilters, BluetoothLeDeviceFilter.class); - mBLEScanFilters = CollectionUtils.map(mBLEFilters, BluetoothLeDeviceFilter::getScanFilter); + mBLEScanFilters + = CollectionUtils.map(mBLEFilters, BluetoothLeDeviceFilter::getScanFilter); reset(); } else if (DEBUG) Log.i(LOG_TAG, "startDiscovery: duplicate request: " + request); @@ -223,7 +215,7 @@ public class DeviceDiscoveryService extends Service { } mIsScanning = true; Handler.getMain().sendMessageDelayed( - obtainMessage(DeviceDiscoveryService::stopScan, this), + obtainMessage(CompanionDeviceDiscoveryService::stopScan, this), SCAN_TIMEOUT); } @@ -237,7 +229,7 @@ public class DeviceDiscoveryService extends Service { stopScan(); mDevicesFound.clear(); mSelectedDevice = null; - mDevicesAdapter.notifyDataSetChanged(); + CompanionDeviceActivity.notifyDevicesChanged(); } @Override @@ -252,7 +244,7 @@ public class DeviceDiscoveryService extends Service { if (!mIsScanning) return; mIsScanning = false; - DeviceChooserActivity activity = mActivity; + CompanionDeviceActivity activity = mActivity; if (activity != null) { if (activity.mDeviceListView != null) { activity.mDeviceListView.removeFooterView(activity.mLoadingIndicator); @@ -276,7 +268,7 @@ public class DeviceDiscoveryService extends Service { if (device == null) return; Handler.getMain().sendMessage(obtainMessage( - DeviceDiscoveryService::onDeviceFoundMainThread, this, device)); + CompanionDeviceDiscoveryService::onDeviceFoundMainThread, this, device)); } @MainThread @@ -292,7 +284,7 @@ public class DeviceDiscoveryService extends Service { onReadyToShowUI(); } mDevicesFound.add(device); - mDevicesAdapter.notifyDataSetChanged(); + CompanionDeviceActivity.notifyDevicesChanged(); } //TODO also, on timeout -> call onFailure @@ -300,7 +292,7 @@ public class DeviceDiscoveryService extends Service { try { mFindCallback.onSuccess(PendingIntent.getActivity( this, 0, - new Intent(this, DeviceChooserActivity.class), + new Intent(this, CompanionDeviceActivity.class), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE)); } catch (RemoteException e) { @@ -311,13 +303,13 @@ public class DeviceDiscoveryService extends Service { private void onDeviceLost(@Nullable DeviceFilterPair device) { Log.i(LOG_TAG, "Lost device " + device.getDisplayName()); Handler.getMain().sendMessage(obtainMessage( - DeviceDiscoveryService::onDeviceLostMainThread, this, device)); + CompanionDeviceDiscoveryService::onDeviceLostMainThread, this, device)); } @MainThread void onDeviceLostMainThread(@Nullable DeviceFilterPair device) { mDevicesFound.remove(device); - mDevicesAdapter.notifyDataSetChanged(); + CompanionDeviceActivity.notifyDevicesChanged(); } void onDeviceSelected(String callingPackage, String deviceAddress) { @@ -331,81 +323,6 @@ public class DeviceDiscoveryService extends Service { mServiceCallback.cancel(true); } - class DevicesAdapter extends BaseAdapter { - private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth); - private Drawable WIFI_ICON = icon(com.android.internal.R.drawable.ic_wifi_signal_3); - - private SparseArray<Integer> mColors = new SparseArray(); - - private Drawable icon(int drawableRes) { - Drawable icon = getResources().getDrawable(drawableRes, null); - icon.setTint(Color.DKGRAY); - return icon; - } - - @Override - public View getView( - int position, - @Nullable View convertView, - @NonNull ViewGroup parent) { - TextView view = convertView instanceof TextView - ? (TextView) convertView - : newView(); - bind(view, getItem(position)); - return view; - } - - private void bind(TextView textView, DeviceFilterPair device) { - textView.setText(device.getDisplayName()); - textView.setBackgroundColor( - device.equals(mSelectedDevice) - ? getColor(android.R.attr.colorControlHighlight) - : Color.TRANSPARENT); - textView.setCompoundDrawablesWithIntrinsicBounds( - device.device instanceof android.net.wifi.ScanResult - ? WIFI_ICON - : BLUETOOTH_ICON, - null, null, null); - textView.getCompoundDrawables()[0].setTint(getColor(android.R.attr.colorForeground)); - } - - private TextView newView() { - final TextView textView = new TextView(DeviceDiscoveryService.this); - textView.setTextColor(getColor(android.R.attr.colorForeground)); - final int padding = DeviceChooserActivity.getPadding(getResources()); - textView.setPadding(padding, padding, padding, padding); - textView.setCompoundDrawablePadding(padding); - return textView; - } - - private int getColor(int colorAttr) { - if (mColors.contains(colorAttr)) { - return mColors.get(colorAttr); - } - TypedValue typedValue = new TypedValue(); - TypedArray a = obtainStyledAttributes(typedValue.data, new int[] { colorAttr }); - int result = a.getColor(0, 0); - a.recycle(); - mColors.put(colorAttr, result); - return result; - } - - @Override - public int getCount() { - return mDevicesFound.size(); - } - - @Override - public DeviceFilterPair getItem(int position) { - return mDevicesFound.get(position); - } - - @Override - public long getItemId(int position) { - return position; - } - } - /** * A pair of device and a filter that matched this device if any. * diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp index 73e15116443f..86b85e8398ce 100644 --- a/packages/Connectivity/framework/Android.bp +++ b/packages/Connectivity/framework/Android.bp @@ -14,6 +14,15 @@ // 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-connectivity-internal-sources", srcs: [ @@ -48,4 +57,29 @@ filegroup { "//frameworks/base", "//packages/modules/Connectivity:__subpackages__", ], -}
\ No newline at end of file +} + +java_sdk_library { + name: "framework-connectivity", + api_only: true, + defaults: ["framework-module-defaults"], + // TODO: build against module API + platform_apis: true, + srcs: [ + ":framework-connectivity-sources", + ], + aidl: { + include_dirs: [ + // Include directories for parcelables that are part of the stable API, and need a + // one-line "parcelable X" .aidl declaration to be used in AIDL interfaces. + // TODO(b/180293679): remove these dependencies as they should not be necessary once + // the module builds against API (the parcelable declarations exist in framework.aidl) + "frameworks/base/core/java", // For framework parcelables + "frameworks/native/aidl/binder", // For PersistableBundle.aidl + ], + }, + libs: [ + "unsupportedappusage", + ], + permitted_packages: ["android.net", "com.android.connectivity.aidl"], +} diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt new file mode 100644 index 000000000000..31b8fc8ae53a --- /dev/null +++ b/packages/Connectivity/framework/api/current.txt @@ -0,0 +1,470 @@ +// Signature format: 2.0 +package android.net { + + public class CaptivePortal implements android.os.Parcelable { + method public int describeContents(); + method public void ignoreNetwork(); + method public void reportCaptivePortalDismissed(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR; + } + + public class ConnectivityDiagnosticsManager { + method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback); + method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback); + } + + public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback { + ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback(); + method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport); + method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport); + method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean); + } + + public static final class ConnectivityDiagnosticsManager.ConnectivityReport implements android.os.Parcelable { + ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle); + method public int describeContents(); + method @NonNull public android.os.PersistableBundle getAdditionalInfo(); + method @NonNull public android.net.LinkProperties getLinkProperties(); + method @NonNull public android.net.Network getNetwork(); + method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); + method public long getReportTimestamp(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR; + field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted"; + field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded"; + field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult"; + field public static final int NETWORK_PROBE_DNS = 4; // 0x4 + field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20 + field public static final int NETWORK_PROBE_HTTP = 8; // 0x8 + field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10 + field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40 + field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0 + field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2 + field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3 + field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1 + } + + public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable { + ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle); + method public int describeContents(); + method public int getDetectionMethod(); + method @NonNull public android.net.LinkProperties getLinkProperties(); + method @NonNull public android.net.Network getNetwork(); + method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); + method public long getReportTimestamp(); + method @NonNull public android.os.PersistableBundle getStallDetails(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR; + field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1 + field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2 + field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts"; + field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis"; + field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate"; + } + + public class ConnectivityManager { + method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); + method public boolean bindProcessToNetwork(@Nullable android.net.Network); + method @NonNull public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); + method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork(); + method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo(); + method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo(); + method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks(); + method @Deprecated public boolean getBackgroundDataSetting(); + method @Nullable public android.net.Network getBoundNetworkForProcess(); + method public int getConnectionOwnerUid(int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress); + method @Nullable public android.net.ProxyInfo getDefaultProxy(); + method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.LinkProperties getLinkProperties(@Nullable android.net.Network); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getMultipathPreference(@Nullable android.net.Network); + method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkCapabilities getNetworkCapabilities(@Nullable android.net.Network); + method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(int); + method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(@Nullable android.net.Network); + method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getNetworkPreference(); + method @Nullable public byte[] getNetworkWatchlistConfigHash(); + method @Deprecated @Nullable public static android.net.Network getProcessDefaultNetwork(); + method public int getRestrictBackgroundStatus(); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered(); + method public boolean isDefaultNetworkActive(); + method @Deprecated public static boolean isNetworkTypeValid(int); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent); + method public void releaseNetworkRequest(@NonNull android.app.PendingIntent); + method public void removeDefaultNetworkActiveListener(@NonNull android.net.ConnectivityManager.OnNetworkActiveListener); + method @Deprecated public void reportBadNetwork(@Nullable android.net.Network); + method public void reportNetworkConnectivity(@Nullable android.net.Network, boolean); + method public boolean requestBandwidthUpdate(@NonNull android.net.Network); + method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback); + method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); + method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int); + method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler, int); + method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent); + method @Deprecated public void setNetworkPreference(int); + method @Deprecated public static boolean setProcessDefaultNetwork(@Nullable android.net.Network); + method public void unregisterNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback); + method public void unregisterNetworkCallback(@NonNull android.app.PendingIntent); + field @Deprecated public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; + field public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL"; + field public static final String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED"; + field @Deprecated public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; + field @Deprecated public static final int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1 + field public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL"; + field public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL"; + field @Deprecated public static final String EXTRA_EXTRA_INFO = "extraInfo"; + field @Deprecated public static final String EXTRA_IS_FAILOVER = "isFailover"; + field public static final String EXTRA_NETWORK = "android.net.extra.NETWORK"; + field @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo"; + field public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST"; + field @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType"; + field public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity"; + field @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork"; + field public static final String EXTRA_REASON = "reason"; + field public static final int MULTIPATH_PREFERENCE_HANDOVER = 1; // 0x1 + field public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 4; // 0x4 + field public static final int MULTIPATH_PREFERENCE_RELIABILITY = 2; // 0x2 + field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1 + field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3 + field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2 + field @Deprecated public static final int TYPE_BLUETOOTH = 7; // 0x7 + field @Deprecated public static final int TYPE_DUMMY = 8; // 0x8 + field @Deprecated public static final int TYPE_ETHERNET = 9; // 0x9 + field @Deprecated public static final int TYPE_MOBILE = 0; // 0x0 + field @Deprecated public static final int TYPE_MOBILE_DUN = 4; // 0x4 + field @Deprecated public static final int TYPE_MOBILE_HIPRI = 5; // 0x5 + field @Deprecated public static final int TYPE_MOBILE_MMS = 2; // 0x2 + field @Deprecated public static final int TYPE_MOBILE_SUPL = 3; // 0x3 + field @Deprecated public static final int TYPE_VPN = 17; // 0x11 + field @Deprecated public static final int TYPE_WIFI = 1; // 0x1 + field @Deprecated public static final int TYPE_WIMAX = 6; // 0x6 + } + + public static class ConnectivityManager.NetworkCallback { + ctor public ConnectivityManager.NetworkCallback(); + method public void onAvailable(@NonNull android.net.Network); + method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean); + method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities); + method public void onLinkPropertiesChanged(@NonNull android.net.Network, @NonNull android.net.LinkProperties); + method public void onLosing(@NonNull android.net.Network, int); + method public void onLost(@NonNull android.net.Network); + method public void onUnavailable(); + } + + public static interface ConnectivityManager.OnNetworkActiveListener { + method public void onNetworkActive(); + } + + public class DhcpInfo implements android.os.Parcelable { + ctor public DhcpInfo(); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpInfo> CREATOR; + field public int dns1; + field public int dns2; + field public int gateway; + field public int ipAddress; + field public int leaseDuration; + field public int netmask; + field public int serverAddress; + } + + public final class DnsResolver { + method @NonNull public static android.net.DnsResolver getInstance(); + method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>); + method public void query(@Nullable android.net.Network, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>); + method public void rawQuery(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>); + method public void rawQuery(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>); + field public static final int CLASS_IN = 1; // 0x1 + field public static final int ERROR_PARSE = 0; // 0x0 + field public static final int ERROR_SYSTEM = 1; // 0x1 + field public static final int FLAG_EMPTY = 0; // 0x0 + field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4 + field public static final int FLAG_NO_CACHE_STORE = 2; // 0x2 + field public static final int FLAG_NO_RETRY = 1; // 0x1 + field public static final int TYPE_A = 1; // 0x1 + field public static final int TYPE_AAAA = 28; // 0x1c + } + + public static interface DnsResolver.Callback<T> { + method public void onAnswer(@NonNull T, int); + method public void onError(@NonNull android.net.DnsResolver.DnsException); + } + + public static class DnsResolver.DnsException extends java.lang.Exception { + field public final int code; + } + + public class InetAddresses { + method public static boolean isNumericAddress(@NonNull String); + method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String); + } + + public final class IpPrefix implements android.os.Parcelable { + method public boolean contains(@NonNull java.net.InetAddress); + method public int describeContents(); + method @NonNull public java.net.InetAddress getAddress(); + method @IntRange(from=0, to=128) public int getPrefixLength(); + method @NonNull public byte[] getRawAddress(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR; + } + + public class LinkAddress implements android.os.Parcelable { + method public int describeContents(); + method public java.net.InetAddress getAddress(); + method public int getFlags(); + method @IntRange(from=0, to=128) public int getPrefixLength(); + method public int getScope(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkAddress> CREATOR; + } + + public final class LinkProperties implements android.os.Parcelable { + ctor public LinkProperties(); + method public boolean addRoute(@NonNull android.net.RouteInfo); + method public void clear(); + method public int describeContents(); + method @Nullable public java.net.Inet4Address getDhcpServerAddress(); + method @NonNull public java.util.List<java.net.InetAddress> getDnsServers(); + method @Nullable public String getDomains(); + method @Nullable public android.net.ProxyInfo getHttpProxy(); + method @Nullable public String getInterfaceName(); + method @NonNull public java.util.List<android.net.LinkAddress> getLinkAddresses(); + method public int getMtu(); + method @Nullable public android.net.IpPrefix getNat64Prefix(); + method @Nullable public String getPrivateDnsServerName(); + method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(); + method public boolean isPrivateDnsActive(); + method public boolean isWakeOnLanSupported(); + method public void setDhcpServerAddress(@Nullable java.net.Inet4Address); + method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>); + method public void setDomains(@Nullable String); + method public void setHttpProxy(@Nullable android.net.ProxyInfo); + method public void setInterfaceName(@Nullable String); + method public void setLinkAddresses(@NonNull java.util.Collection<android.net.LinkAddress>); + method public void setMtu(int); + method public void setNat64Prefix(@Nullable android.net.IpPrefix); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR; + } + + public final class MacAddress implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public static android.net.MacAddress fromBytes(@NonNull byte[]); + method @NonNull public static android.net.MacAddress fromString(@NonNull String); + method public int getAddressType(); + method @Nullable public java.net.Inet6Address getLinkLocalIpv6FromEui48Mac(); + method public boolean isLocallyAssigned(); + method public boolean matches(@NonNull android.net.MacAddress, @NonNull android.net.MacAddress); + method @NonNull public byte[] toByteArray(); + method @NonNull public String toOuiString(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.net.MacAddress BROADCAST_ADDRESS; + field @NonNull public static final android.os.Parcelable.Creator<android.net.MacAddress> CREATOR; + field public static final int TYPE_BROADCAST = 3; // 0x3 + field public static final int TYPE_MULTICAST = 2; // 0x2 + field public static final int TYPE_UNICAST = 1; // 0x1 + } + + public class Network implements android.os.Parcelable { + method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException; + method public void bindSocket(java.net.Socket) throws java.io.IOException; + method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException; + method public int describeContents(); + method public static android.net.Network fromNetworkHandle(long); + method public java.net.InetAddress[] getAllByName(String) throws java.net.UnknownHostException; + method public java.net.InetAddress getByName(String) throws java.net.UnknownHostException; + method public long getNetworkHandle(); + method public javax.net.SocketFactory getSocketFactory(); + method public java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException; + method public java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException; + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.Network> CREATOR; + } + + public final class NetworkCapabilities implements android.os.Parcelable { + ctor public NetworkCapabilities(); + ctor public NetworkCapabilities(android.net.NetworkCapabilities); + method public int describeContents(); + method public int getLinkDownstreamBandwidthKbps(); + method public int getLinkUpstreamBandwidthKbps(); + method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier(); + method public int getOwnerUid(); + method public int getSignalStrength(); + method @Nullable public android.net.TransportInfo getTransportInfo(); + method public boolean hasCapability(int); + method public boolean hasTransport(int); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR; + field public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; // 0x11 + field public static final int NET_CAPABILITY_CBS = 5; // 0x5 + field public static final int NET_CAPABILITY_DUN = 2; // 0x2 + field public static final int NET_CAPABILITY_EIMS = 10; // 0xa + field public static final int NET_CAPABILITY_ENTERPRISE = 29; // 0x1d + field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13 + field public static final int NET_CAPABILITY_FOTA = 3; // 0x3 + field public static final int NET_CAPABILITY_IA = 7; // 0x7 + field public static final int NET_CAPABILITY_IMS = 4; // 0x4 + field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc + field public static final int NET_CAPABILITY_MCX = 23; // 0x17 + field public static final int NET_CAPABILITY_MMS = 0; // 0x0 + field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14 + field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb + field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd + field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12 + field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15 + field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf + field public static final int NET_CAPABILITY_RCS = 8; // 0x8 + field public static final int NET_CAPABILITY_SUPL = 1; // 0x1 + field public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; // 0x19 + field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe + field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10 + field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6 + field public static final int NET_CAPABILITY_XCAP = 9; // 0x9 + field public static final int SIGNAL_STRENGTH_UNSPECIFIED = -2147483648; // 0x80000000 + field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2 + field public static final int TRANSPORT_CELLULAR = 0; // 0x0 + field public static final int TRANSPORT_ETHERNET = 3; // 0x3 + field public static final int TRANSPORT_LOWPAN = 6; // 0x6 + field public static final int TRANSPORT_VPN = 4; // 0x4 + field public static final int TRANSPORT_WIFI = 1; // 0x1 + field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5 + } + + @Deprecated public class NetworkInfo implements android.os.Parcelable { + ctor @Deprecated public NetworkInfo(int, int, @Nullable String, @Nullable String); + method @Deprecated public int describeContents(); + method @Deprecated @NonNull public android.net.NetworkInfo.DetailedState getDetailedState(); + method @Deprecated public String getExtraInfo(); + method @Deprecated public String getReason(); + method @Deprecated public android.net.NetworkInfo.State getState(); + method @Deprecated public int getSubtype(); + method @Deprecated public String getSubtypeName(); + method @Deprecated public int getType(); + method @Deprecated public String getTypeName(); + method @Deprecated public boolean isAvailable(); + method @Deprecated public boolean isConnected(); + method @Deprecated public boolean isConnectedOrConnecting(); + method @Deprecated public boolean isFailover(); + method @Deprecated public boolean isRoaming(); + method @Deprecated public void setDetailedState(@NonNull android.net.NetworkInfo.DetailedState, @Nullable String, @Nullable String); + method @Deprecated public void writeToParcel(android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR; + } + + @Deprecated public enum NetworkInfo.DetailedState { + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState AUTHENTICATING; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState BLOCKED; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CAPTIVE_PORTAL_CHECK; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTED; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTING; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTED; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTING; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState FAILED; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState IDLE; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState OBTAINING_IPADDR; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SCANNING; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SUSPENDED; + enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK; + } + + @Deprecated public enum NetworkInfo.State { + enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTED; + enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTING; + enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTED; + enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTING; + enum_constant @Deprecated public static final android.net.NetworkInfo.State SUSPENDED; + enum_constant @Deprecated public static final android.net.NetworkInfo.State UNKNOWN; + } + + public class NetworkRequest implements android.os.Parcelable { + method public boolean canBeSatisfiedBy(@Nullable android.net.NetworkCapabilities); + method public int describeContents(); + method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier(); + method public boolean hasCapability(int); + method public boolean hasTransport(int); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR; + } + + public static class NetworkRequest.Builder { + ctor public NetworkRequest.Builder(); + method public android.net.NetworkRequest.Builder addCapability(int); + method public android.net.NetworkRequest.Builder addTransportType(int); + method public android.net.NetworkRequest build(); + method @NonNull public android.net.NetworkRequest.Builder clearCapabilities(); + method public android.net.NetworkRequest.Builder removeCapability(int); + method public android.net.NetworkRequest.Builder removeTransportType(int); + method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String); + method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier); + } + + public final class Proxy { + ctor public Proxy(); + method @Deprecated public static String getDefaultHost(); + method @Deprecated public static int getDefaultPort(); + method @Deprecated public static String getHost(android.content.Context); + method @Deprecated public static int getPort(android.content.Context); + field @Deprecated public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO"; + field public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE"; + } + + public class ProxyInfo implements android.os.Parcelable { + ctor public ProxyInfo(@Nullable android.net.ProxyInfo); + method public static android.net.ProxyInfo buildDirectProxy(String, int); + method public static android.net.ProxyInfo buildDirectProxy(String, int, java.util.List<java.lang.String>); + method public static android.net.ProxyInfo buildPacProxy(android.net.Uri); + method @NonNull public static android.net.ProxyInfo buildPacProxy(@NonNull android.net.Uri, int); + method public int describeContents(); + method public String[] getExclusionList(); + method public String getHost(); + method public android.net.Uri getPacFileUrl(); + method public int getPort(); + method public boolean isValid(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.ProxyInfo> CREATOR; + } + + public final class RouteInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.net.IpPrefix getDestination(); + method @Nullable public java.net.InetAddress getGateway(); + method @Nullable public String getInterface(); + method public boolean hasGateway(); + method public boolean isDefaultRoute(); + method public boolean matches(java.net.InetAddress); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.RouteInfo> CREATOR; + } + + public abstract class SocketKeepalive implements java.lang.AutoCloseable { + method public final void close(); + method public final void start(@IntRange(from=0xa, to=0xe10) int); + method public final void stop(); + field public static final int ERROR_HARDWARE_ERROR = -31; // 0xffffffe1 + field public static final int ERROR_INSUFFICIENT_RESOURCES = -32; // 0xffffffe0 + field public static final int ERROR_INVALID_INTERVAL = -24; // 0xffffffe8 + field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb + field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9 + field public static final int ERROR_INVALID_NETWORK = -20; // 0xffffffec + field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea + field public static final int ERROR_INVALID_SOCKET = -25; // 0xffffffe7 + field public static final int ERROR_SOCKET_NOT_IDLE = -26; // 0xffffffe6 + field public static final int ERROR_UNSUPPORTED = -30; // 0xffffffe2 + } + + public static class SocketKeepalive.Callback { + ctor public SocketKeepalive.Callback(); + method public void onDataReceived(); + method public void onError(int); + method public void onStarted(); + method public void onStopped(); + } + + public interface TransportInfo { + } + +} + diff --git a/packages/Connectivity/framework/api/lint-baseline.txt b/packages/Connectivity/framework/api/lint-baseline.txt new file mode 100644 index 000000000000..2f4004ab723d --- /dev/null +++ b/packages/Connectivity/framework/api/lint-baseline.txt @@ -0,0 +1,4 @@ +// Baseline format: 1.0 +VisiblySynchronized: android.net.NetworkInfo#toString(): + Internal locks must not be exposed (synchronizing on this or class is still + externally observable): method android.net.NetworkInfo.toString() diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt new file mode 100644 index 000000000000..3af855ec1e11 --- /dev/null +++ b/packages/Connectivity/framework/api/module-lib-current.txt @@ -0,0 +1,66 @@ +// Signature format: 2.0 +package android.net { + + public final class ConnectivityFrameworkInitializer { + method public static void registerServiceWrappers(); + } + + public class ConnectivityManager { + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); + } + + public final class NetworkAgentConfig implements android.os.Parcelable { + method @Nullable public String getSubscriberId(); + } + + public static final class NetworkAgentConfig.Builder { + method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String); + } + + public final class NetworkCapabilities implements android.os.Parcelable { + field public static final int TRANSPORT_TEST = 7; // 0x7 + } + + public final class Proxy { + method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo); + } + + public final class TcpRepairWindow { + ctor public TcpRepairWindow(int, int, int, int, int, int); + field public final int maxWindow; + field public final int rcvWnd; + field public final int rcvWndScale; + field public final int rcvWup; + field public final int sndWl1; + field public final int sndWnd; + } + + public final class TestNetworkInterface implements android.os.Parcelable { + ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String); + method public int describeContents(); + method @NonNull public android.os.ParcelFileDescriptor getFileDescriptor(); + method @NonNull public String getInterfaceName(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR; + } + + public class TestNetworkManager { + method @NonNull public android.net.TestNetworkInterface createTapInterface(); + method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>); + method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder); + method public void teardownTestNetwork(@NonNull android.net.Network); + field public static final String TEST_TAP_PREFIX = "testtap"; + } + + public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo { + ctor public VpnTransportInfo(int); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR; + field public final int type; + } + +} + diff --git a/packages/Connectivity/framework/api/module-lib-removed.txt b/packages/Connectivity/framework/api/module-lib-removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/packages/Connectivity/framework/api/module-lib-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/packages/Connectivity/framework/api/removed.txt b/packages/Connectivity/framework/api/removed.txt new file mode 100644 index 000000000000..303a1e6173ba --- /dev/null +++ b/packages/Connectivity/framework/api/removed.txt @@ -0,0 +1,11 @@ +// Signature format: 2.0 +package android.net { + + public class ConnectivityManager { + method @Deprecated public boolean requestRouteToHost(int, int); + method @Deprecated public int startUsingNetworkFeature(int, String); + method @Deprecated public int stopUsingNetworkFeature(int, String); + } + +} + diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt new file mode 100644 index 000000000000..41ebc5774f3d --- /dev/null +++ b/packages/Connectivity/framework/api/system-current.txt @@ -0,0 +1,407 @@ +// Signature format: 2.0 +package android.net { + + public class CaptivePortal implements android.os.Parcelable { + method public void logEvent(int, @NonNull String); + method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork(); + method public void useNetwork(); + field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64 + field public static final int APP_RETURN_DISMISSED = 0; // 0x0 + field public static final int APP_RETURN_UNWANTED = 1; // 0x1 + field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2 + } + + public final class CaptivePortalData implements android.os.Parcelable { + method public int describeContents(); + method public long getByteLimit(); + method public long getExpiryTimeMillis(); + method public long getRefreshTimeMillis(); + method @Nullable public android.net.Uri getUserPortalUrl(); + method public int getUserPortalUrlSource(); + method @Nullable public String getVenueFriendlyName(); + method @Nullable public android.net.Uri getVenueInfoUrl(); + method public int getVenueInfoUrlSource(); + method public boolean isCaptive(); + method public boolean isSessionExtendable(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0 + field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1 + field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR; + } + + public static class CaptivePortalData.Builder { + ctor public CaptivePortalData.Builder(); + ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData); + method @NonNull public android.net.CaptivePortalData build(); + method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long); + method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean); + method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long); + method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); + method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); + method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); + method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int); + method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String); + method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); + method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int); + } + + public class ConnectivityManager { + method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); + method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback); + method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl(); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider); + method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean); + method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi(); + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider); + method public void unregisterQosCallback(@NonNull android.net.QosCallback); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback); + field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; + field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; + field public static final int TETHERING_BLUETOOTH = 2; // 0x2 + field public static final int TETHERING_USB = 1; // 0x1 + field public static final int TETHERING_WIFI = 0; // 0x0 + field @Deprecated public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd + field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 + field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field public static final int TYPE_NONE = -1; // 0xffffffff + field @Deprecated public static final int TYPE_PROXY = 16; // 0x10 + field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd + } + + public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener { + method public void onComplete(); + } + + @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback { + ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback(); + method @Deprecated public void onTetheringFailed(); + method @Deprecated public void onTetheringStarted(); + } + + @Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener { + method @Deprecated public void onTetheringEntitlementResult(int); + } + + @Deprecated public abstract static class ConnectivityManager.OnTetheringEventCallback { + ctor @Deprecated public ConnectivityManager.OnTetheringEventCallback(); + method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network); + } + + public final class InvalidPacketException extends java.lang.Exception { + ctor public InvalidPacketException(int); + method public int getError(); + field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb + field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9 + field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea + } + + public final class IpConfiguration implements android.os.Parcelable { + ctor public IpConfiguration(); + ctor public IpConfiguration(@NonNull android.net.IpConfiguration); + method public int describeContents(); + method @Nullable public android.net.ProxyInfo getHttpProxy(); + method @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment(); + method @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings(); + method @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration(); + method public void setHttpProxy(@Nullable android.net.ProxyInfo); + method public void setIpAssignment(@NonNull android.net.IpConfiguration.IpAssignment); + method public void setProxySettings(@NonNull android.net.IpConfiguration.ProxySettings); + method public void setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.IpConfiguration> CREATOR; + } + + public enum IpConfiguration.IpAssignment { + enum_constant public static final android.net.IpConfiguration.IpAssignment DHCP; + enum_constant public static final android.net.IpConfiguration.IpAssignment STATIC; + enum_constant public static final android.net.IpConfiguration.IpAssignment UNASSIGNED; + } + + public enum IpConfiguration.ProxySettings { + enum_constant public static final android.net.IpConfiguration.ProxySettings NONE; + enum_constant public static final android.net.IpConfiguration.ProxySettings PAC; + enum_constant public static final android.net.IpConfiguration.ProxySettings STATIC; + enum_constant public static final android.net.IpConfiguration.ProxySettings UNASSIGNED; + } + + public final class IpPrefix implements android.os.Parcelable { + ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); + ctor public IpPrefix(@NonNull String); + } + + public class KeepalivePacketData { + ctor protected KeepalivePacketData(@NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull byte[]) throws android.net.InvalidPacketException; + method @NonNull public java.net.InetAddress getDstAddress(); + method public int getDstPort(); + method @NonNull public byte[] getPacket(); + method @NonNull public java.net.InetAddress getSrcAddress(); + method public int getSrcPort(); + } + + public class LinkAddress implements android.os.Parcelable { + ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int); + ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long); + ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); + ctor public LinkAddress(@NonNull String); + ctor public LinkAddress(@NonNull String, int, int); + method public long getDeprecationTime(); + method public long getExpirationTime(); + method public boolean isGlobalPreferred(); + method public boolean isIpv4(); + method public boolean isIpv6(); + method public boolean isSameAddressAs(@Nullable android.net.LinkAddress); + field public static final long LIFETIME_PERMANENT = 9223372036854775807L; // 0x7fffffffffffffffL + field public static final long LIFETIME_UNKNOWN = -1L; // 0xffffffffffffffffL + } + + public final class LinkProperties implements android.os.Parcelable { + ctor public LinkProperties(@Nullable android.net.LinkProperties); + ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean); + method public boolean addDnsServer(@NonNull java.net.InetAddress); + method public boolean addLinkAddress(@NonNull android.net.LinkAddress); + method public boolean addPcscfServer(@NonNull java.net.InetAddress); + method @NonNull public java.util.List<java.net.InetAddress> getAddresses(); + method @NonNull public java.util.List<java.lang.String> getAllInterfaceNames(); + method @NonNull public java.util.List<android.net.LinkAddress> getAllLinkAddresses(); + method @NonNull public java.util.List<android.net.RouteInfo> getAllRoutes(); + method @Nullable public android.net.Uri getCaptivePortalApiUrl(); + method @Nullable public android.net.CaptivePortalData getCaptivePortalData(); + method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers(); + method @Nullable public String getTcpBufferSizes(); + method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers(); + method public boolean hasGlobalIpv6Address(); + method public boolean hasIpv4Address(); + method public boolean hasIpv4DefaultRoute(); + method public boolean hasIpv4DnsServer(); + method public boolean hasIpv6DefaultRoute(); + method public boolean hasIpv6DnsServer(); + method public boolean isIpv4Provisioned(); + method public boolean isIpv6Provisioned(); + method public boolean isProvisioned(); + method public boolean isReachable(@NonNull java.net.InetAddress); + method public boolean removeDnsServer(@NonNull java.net.InetAddress); + method public boolean removeLinkAddress(@NonNull android.net.LinkAddress); + method public boolean removeRoute(@NonNull android.net.RouteInfo); + method public void setCaptivePortalApiUrl(@Nullable android.net.Uri); + method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData); + method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>); + method public void setPrivateDnsServerName(@Nullable String); + method public void setTcpBufferSizes(@Nullable String); + method public void setUsePrivateDns(boolean); + method public void setValidatedPrivateDnsServers(@NonNull java.util.Collection<java.net.InetAddress>); + } + + public final class NattKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable { + ctor public NattKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[]) throws android.net.InvalidPacketException; + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.NattKeepalivePacketData> CREATOR; + } + + public class Network implements android.os.Parcelable { + ctor public Network(@NonNull android.net.Network); + method public int getNetId(); + method @NonNull public android.net.Network getPrivateDnsBypassingCopy(); + } + + public abstract class NetworkAgent { + ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider); + method @Nullable public android.net.Network getNetwork(); + method public void markConnected(); + method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData); + method public void onAutomaticReconnectDisabled(); + method public void onNetworkUnwanted(); + method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter); + method public void onQosCallbackUnregistered(int); + method public void onRemoveKeepalivePacketFilter(int); + method public void onSaveAcceptUnvalidated(boolean); + method public void onSignalStrengthThresholdsUpdated(@NonNull int[]); + method public void onStartSocketKeepalive(int, @NonNull java.time.Duration, @NonNull android.net.KeepalivePacketData); + method public void onStopSocketKeepalive(int); + method public void onValidationStatus(int, @Nullable android.net.Uri); + method @NonNull public android.net.Network register(); + method public final void sendLinkProperties(@NonNull android.net.LinkProperties); + method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities); + method public final void sendNetworkScore(@IntRange(from=0, to=99) int); + method public final void sendQosCallbackError(int, int); + method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes); + method public final void sendQosSessionLost(int, int); + method public final void sendSocketKeepaliveEvent(int, int); + method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>); + method public void unregister(); + field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2 + field public static final int VALIDATION_STATUS_VALID = 1; // 0x1 + } + + public final class NetworkAgentConfig implements android.os.Parcelable { + method public int describeContents(); + method public int getLegacyType(); + method @NonNull public String getLegacyTypeName(); + method public boolean isExplicitlySelected(); + method public boolean isPartialConnectivityAcceptable(); + method public boolean isUnvalidatedConnectivityAcceptable(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR; + } + + public static final class NetworkAgentConfig.Builder { + ctor public NetworkAgentConfig.Builder(); + method @NonNull public android.net.NetworkAgentConfig build(); + method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean); + method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int); + method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String); + method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean); + method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean); + } + + public final class NetworkCapabilities implements android.os.Parcelable { + ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean); + method @NonNull public int[] getAdministratorUids(); + method @Nullable public String getSsid(); + method @NonNull public int[] getTransportTypes(); + method public boolean isPrivateDnsBroken(); + method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); + field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c + field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 + field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a + field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18 + field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b + } + + public static final class NetworkCapabilities.Builder { + ctor public NetworkCapabilities.Builder(); + ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities); + method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int); + method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int); + method @NonNull public android.net.NetworkCapabilities build(); + method @NonNull public android.net.NetworkCapabilities.Builder clearAll(); + method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int); + method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]); + method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int); + method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int); + method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int); + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String); + method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo); + } + + public class NetworkProvider { + ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String); + method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest); + method public int getProviderId(); + method public void onNetworkRequestWithdrawn(@NonNull android.net.NetworkRequest); + method public void onNetworkRequested(@NonNull android.net.NetworkRequest, @IntRange(from=0, to=99) int, int); + field public static final int ID_NONE = -1; // 0xffffffff + } + + public class NetworkRequest implements android.os.Parcelable { + method @Nullable public String getRequestorPackageName(); + method public int getRequestorUid(); + } + + public static class NetworkRequest.Builder { + method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int); + } + + public final class RouteInfo implements android.os.Parcelable { + ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int); + ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int); + method public int getMtu(); + method public int getType(); + field public static final int RTN_THROW = 9; // 0x9 + field public static final int RTN_UNICAST = 1; // 0x1 + field public static final int RTN_UNREACHABLE = 7; // 0x7 + } + + public abstract class SocketKeepalive implements java.lang.AutoCloseable { + field public static final int SUCCESS = 0; // 0x0 + } + + public final class StaticIpConfiguration implements android.os.Parcelable { + ctor public StaticIpConfiguration(); + ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); + method public void addDnsServer(@NonNull java.net.InetAddress); + method public void clear(); + method public int describeContents(); + method @NonNull public java.util.List<java.net.InetAddress> getDnsServers(); + method @Nullable public String getDomains(); + method @Nullable public java.net.InetAddress getGateway(); + method @Nullable public android.net.LinkAddress getIpAddress(); + method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR; + } + + public static final class StaticIpConfiguration.Builder { + ctor public StaticIpConfiguration.Builder(); + method @NonNull public android.net.StaticIpConfiguration build(); + method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>); + method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String); + method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress); + method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress); + } + + public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable { + ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException; + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR; + field public final int ipTos; + field public final int ipTtl; + field public final int tcpAck; + field public final int tcpSeq; + field public final int tcpWindow; + field public final int tcpWindowScale; + } + + public interface TransportInfo { + method public default boolean hasLocationSensitiveFields(); + method @NonNull public default android.net.TransportInfo makeCopy(boolean); + } + +} + +package android.net.apf { + + public final class ApfCapabilities implements android.os.Parcelable { + ctor public ApfCapabilities(int, int, int); + method public int describeContents(); + method public static boolean getApfDrop8023Frames(); + method @NonNull public static int[] getApfEtherTypeBlackList(); + method public boolean hasDataAccess(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR; + field public final int apfPacketFormat; + field public final int apfVersionSupported; + field public final int maximumApfProgramSize; + } + +} + +package android.net.util { + + public final class SocketUtils { + method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException; + method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException; + method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); + method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int); + method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]); + method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]); + } + +} + diff --git a/packages/Connectivity/framework/api/system-lint-baseline.txt b/packages/Connectivity/framework/api/system-lint-baseline.txt new file mode 100644 index 000000000000..9a97707763f1 --- /dev/null +++ b/packages/Connectivity/framework/api/system-lint-baseline.txt @@ -0,0 +1 @@ +// Baseline format: 1.0 diff --git a/packages/Connectivity/framework/api/system-removed.txt b/packages/Connectivity/framework/api/system-removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/packages/Connectivity/framework/api/system-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java index f4b46e9f11ed..eafda4d2d694 100644 --- a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java +++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java @@ -44,7 +44,7 @@ public final class CaptivePortalData implements Parcelable { private final boolean mCaptive; private final String mVenueFriendlyName; private final int mVenueInfoUrlSource; - private final int mTermsAndConditionsSource; + private final int mUserPortalUrlSource; /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -65,7 +65,7 @@ public final class CaptivePortalData implements Parcelable { private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl, boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive, - String venueFriendlyName, int venueInfoUrlSource, int termsAndConditionsSource) { + String venueFriendlyName, int venueInfoUrlSource, int userPortalUrlSource) { mRefreshTimeMillis = refreshTimeMillis; mUserPortalUrl = userPortalUrl; mVenueInfoUrl = venueInfoUrl; @@ -75,7 +75,7 @@ public final class CaptivePortalData implements Parcelable { mCaptive = captive; mVenueFriendlyName = venueFriendlyName; mVenueInfoUrlSource = venueInfoUrlSource; - mTermsAndConditionsSource = termsAndConditionsSource; + mUserPortalUrlSource = userPortalUrlSource; } private CaptivePortalData(Parcel p) { @@ -100,7 +100,7 @@ public final class CaptivePortalData implements Parcelable { dest.writeBoolean(mCaptive); dest.writeString(mVenueFriendlyName); dest.writeInt(mVenueInfoUrlSource); - dest.writeInt(mTermsAndConditionsSource); + dest.writeInt(mUserPortalUrlSource); } /** @@ -130,7 +130,7 @@ public final class CaptivePortalData implements Parcelable { public Builder(@Nullable CaptivePortalData data) { if (data == null) return; setRefreshTime(data.mRefreshTimeMillis) - .setUserPortalUrl(data.mUserPortalUrl, data.mTermsAndConditionsSource) + .setUserPortalUrl(data.mUserPortalUrl, data.mUserPortalUrlSource) .setVenueInfoUrl(data.mVenueInfoUrl, data.mVenueInfoUrlSource) .setSessionExtendable(data.mIsSessionExtendable) .setBytesRemaining(data.mByteLimit) @@ -314,7 +314,7 @@ public final class CaptivePortalData implements Parcelable { * @return The source that the user portal URL was obtained from */ public @CaptivePortalDataSource int getUserPortalUrlSource() { - return mTermsAndConditionsSource; + return mUserPortalUrlSource; } /** @@ -342,7 +342,7 @@ public final class CaptivePortalData implements Parcelable { public int hashCode() { return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl, mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName, - mVenueInfoUrlSource, mTermsAndConditionsSource); + mVenueInfoUrlSource, mUserPortalUrlSource); } @Override @@ -358,7 +358,7 @@ public final class CaptivePortalData implements Parcelable { && mCaptive == other.mCaptive && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName) && mVenueInfoUrlSource == other.mVenueInfoUrlSource - && mTermsAndConditionsSource == other.mTermsAndConditionsSource; + && mUserPortalUrlSource == other.mUserPortalUrlSource; } @Override @@ -373,7 +373,7 @@ public final class CaptivePortalData implements Parcelable { + ", captive: " + mCaptive + ", venueFriendlyName: " + mVenueFriendlyName + ", venueInfoUrlSource: " + mVenueInfoUrlSource - + ", termsAndConditionsSource: " + mTermsAndConditionsSource + + ", userPortalUrlSource: " + mUserPortalUrlSource + "}"; } } diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index d7c8291c6676..e7ab0a1c7ac8 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -49,8 +49,6 @@ import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.os.INetworkActivityListener; -import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; import android.os.Messenger; @@ -835,7 +833,6 @@ public class ConnectivityManager { private final Context mContext; - private INetworkManagementService mNMService; private INetworkPolicyManager mNPManager; private final TetheringManager mTetheringManager; @@ -1070,58 +1067,6 @@ public class ConnectivityManager { } /** - * Calls VpnManager#isAlwaysOnVpnPackageSupportedForUser. - * @deprecated TODO: remove when callers have migrated to VpnManager. - * @hide - */ - @Deprecated - public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) { - return getVpnManager().isAlwaysOnVpnPackageSupportedForUser(userId, vpnPackage); - } - - /** - * Calls VpnManager#setAlwaysOnVpnPackageForUser. - * @deprecated TODO: remove when callers have migrated to VpnManager. - * @hide - */ - @Deprecated - public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage, - boolean lockdownEnabled, @Nullable List<String> lockdownAllowlist) { - return getVpnManager().setAlwaysOnVpnPackageForUser(userId, vpnPackage, lockdownEnabled, - lockdownAllowlist); - } - - /** - * Calls VpnManager#getAlwaysOnVpnPackageForUser. - * @deprecated TODO: remove when callers have migrated to VpnManager. - * @hide - */ - @Deprecated - public String getAlwaysOnVpnPackageForUser(int userId) { - return getVpnManager().getAlwaysOnVpnPackageForUser(userId); - } - - /** - * Calls VpnManager#isVpnLockdownEnabled. - * @deprecated TODO: remove when callers have migrated to VpnManager. - * @hide - */ - @Deprecated - public boolean isVpnLockdownEnabled(int userId) { - return getVpnManager().isVpnLockdownEnabled(userId); - } - - /** - * Calls VpnManager#getVpnLockdownAllowlist. - * @deprecated TODO: remove when callers have migrated to VpnManager. - * @hide - */ - @Deprecated - public List<String> getVpnLockdownAllowlist(int userId) { - return getVpnManager().getVpnLockdownAllowlist(userId); - } - - /** * Adds or removes a requirement for given UID ranges to use the VPN. * * If set to {@code true}, informs the system that the UIDs in the specified ranges must not @@ -2211,17 +2156,6 @@ public class ConnectivityManager { void onNetworkActive(); } - private INetworkManagementService getNetworkManagementService() { - synchronized (this) { - if (mNMService != null) { - return mNMService; - } - IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - mNMService = INetworkManagementService.Stub.asInterface(b); - return mNMService; - } - } - private final ArrayMap<OnNetworkActiveListener, INetworkActivityListener> mNetworkActivityListeners = new ArrayMap<>(); @@ -2246,7 +2180,7 @@ public class ConnectivityManager { }; try { - getNetworkManagementService().registerNetworkActivityListener(rl); + mService.registerNetworkActivityListener(rl); mNetworkActivityListeners.put(l, rl); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -2263,7 +2197,7 @@ public class ConnectivityManager { INetworkActivityListener rl = mNetworkActivityListeners.get(l); Preconditions.checkArgument(rl != null, "Listener was not registered."); try { - getNetworkManagementService().unregisterNetworkActivityListener(rl); + mService.registerNetworkActivityListener(rl); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2279,7 +2213,7 @@ public class ConnectivityManager { */ public boolean isDefaultNetworkActive() { try { - return getNetworkManagementService().isNetworkActive(); + return mService.isDefaultNetworkActive(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3167,16 +3101,6 @@ public class ConnectivityManager { } /** - * Calls VpnManager#updateLockdownVpn. - * @deprecated TODO: remove when callers have migrated to VpnManager. - * @hide - */ - @Deprecated - public boolean updateLockdownVpn() { - return getVpnManager().updateLockdownVpn(); - } - - /** * Set sign in error notification to visible or invisible * * @hide @@ -4537,8 +4461,6 @@ public class ConnectivityManager { try { mService.factoryReset(); mTetheringManager.stopAllTethering(); - // TODO: Migrate callers to VpnManager#factoryReset. - getVpnManager().factoryReset(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -4832,15 +4754,6 @@ public class ConnectivityManager { return new TestNetworkManager(ITestNetworkManager.Stub.asInterface(tnBinder)); } - /** - * Temporary hack to shim calls from ConnectivityManager to VpnManager. We cannot store a - * private final mVpnManager because ConnectivityManager is initialized before VpnManager. - * @hide TODO: remove. - */ - public VpnManager getVpnManager() { - return mContext.getSystemService(VpnManager.class); - } - /** @hide */ public ConnectivityDiagnosticsManager createDiagnosticsManager() { return new ConnectivityDiagnosticsManager(mContext, mService); diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index 6391802f3330..160338d396af 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -21,6 +21,7 @@ import android.net.ConnectionInfo; import android.net.ConnectivityDiagnosticsManager; import android.net.IConnectivityDiagnosticsCallback; import android.net.IOnSetOemNetworkPreferenceListener; +import android.net.INetworkActivityListener; import android.net.IQosCallback; import android.net.ISocketKeepaliveCallback; import android.net.LinkProperties; @@ -36,7 +37,6 @@ import android.net.UidRange; import android.net.QosSocketInfo; import android.os.Bundle; import android.os.IBinder; -import android.os.INetworkActivityListener; import android.os.Messenger; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; diff --git a/core/java/android/os/INetworkActivityListener.aidl b/packages/Connectivity/framework/src/android/net/INetworkActivityListener.aidl index 24e6e55f1e57..79687dd3bf06 100644 --- a/core/java/android/os/INetworkActivityListener.aidl +++ b/packages/Connectivity/framework/src/android/net/INetworkActivityListener.aidl @@ -13,7 +13,7 @@ ** limitations under the License. */ -package android.os; +package android.net; /** * @hide diff --git a/packages/Connectivity/framework/src/android/net/IpPrefix.java b/packages/Connectivity/framework/src/android/net/IpPrefix.java index bcb65fab8571..d2ee7d13b05f 100644 --- a/packages/Connectivity/framework/src/android/net/IpPrefix.java +++ b/packages/Connectivity/framework/src/android/net/IpPrefix.java @@ -24,6 +24,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.Pair; +import com.android.net.module.util.NetUtils; + import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; @@ -59,7 +61,7 @@ public final class IpPrefix implements Parcelable { throw new IllegalArgumentException( "IpPrefix has " + address.length + " bytes which is neither 4 nor 16"); } - NetworkUtils.maskRawAddress(address, prefixLength); + NetUtils.maskRawAddress(address, prefixLength); } /** @@ -190,7 +192,7 @@ public final class IpPrefix implements Parcelable { if (addrBytes == null || addrBytes.length != this.address.length) { return false; } - NetworkUtils.maskRawAddress(addrBytes, prefixLength); + NetUtils.maskRawAddress(addrBytes, prefixLength); return Arrays.equals(this.address, addrBytes); } @@ -204,7 +206,7 @@ public final class IpPrefix implements Parcelable { public boolean containsPrefix(@NonNull IpPrefix otherPrefix) { if (otherPrefix.getPrefixLength() < prefixLength) return false; final byte[] otherAddress = otherPrefix.getRawAddress(); - NetworkUtils.maskRawAddress(otherAddress, prefixLength); + NetUtils.maskRawAddress(otherAddress, prefixLength); return Arrays.equals(otherAddress, address); } diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java index 26d14cbfaa95..cd76f409b093 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java @@ -205,6 +205,7 @@ public final class NetworkCapabilities implements Parcelable { NET_CAPABILITY_OEM_PRIVATE, NET_CAPABILITY_VEHICLE_INTERNAL, NET_CAPABILITY_NOT_VCN_MANAGED, + NET_CAPABILITY_ENTERPRISE, }) public @interface NetCapability { } @@ -415,8 +416,17 @@ public final class NetworkCapabilities implements Parcelable { @SystemApi public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; + /** + * Indicates that this network is intended for enterprise use. + * <p> + * 5G URSP rules may indicate that all data should use a connection dedicated for enterprise + * use. If the enterprise capability is requested, all enterprise traffic will be routed over + * the connection with this capability. + */ + public static final int NET_CAPABILITY_ENTERPRISE = 29; + private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; - private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED; + private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_ENTERPRISE; /** * Network capabilities that are expected to be mutable, i.e., can change while a particular @@ -474,7 +484,8 @@ public final class NetworkCapabilities implements Parcelable { | (1 << NET_CAPABILITY_MCX) | (1 << NET_CAPABILITY_RCS) | (1 << NET_CAPABILITY_VEHICLE_INTERNAL) - | (1 << NET_CAPABILITY_XCAP); + | (1 << NET_CAPABILITY_XCAP) + | (1 << NET_CAPABILITY_ENTERPRISE); /** * Capabilities that force network to be restricted. @@ -2028,8 +2039,9 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY"; case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED"; case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE"; - case NET_CAPABILITY_VEHICLE_INTERNAL: return "NET_CAPABILITY_VEHICLE_INTERNAL"; + case NET_CAPABILITY_VEHICLE_INTERNAL: return "VEHICLE_INTERNAL"; case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED"; + case NET_CAPABILITY_ENTERPRISE: return "ENTERPRISE"; default: return Integer.toString(capability); } } diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index 4e3085f4704d..b4a651c0607e 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -16,6 +16,22 @@ package android.net; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -30,6 +46,8 @@ import android.os.Process; import android.text.TextUtils; import android.util.proto.ProtoOutputStream; +import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.Set; @@ -154,8 +172,30 @@ public class NetworkRequest implements Parcelable { * needed in terms of {@link NetworkCapabilities} features */ public static class Builder { + /** + * Capabilities that are currently compatible with VCN networks. + */ + private static final List<Integer> VCN_SUPPORTED_CAPABILITIES = Arrays.asList( + NET_CAPABILITY_CAPTIVE_PORTAL, + NET_CAPABILITY_DUN, + NET_CAPABILITY_FOREGROUND, + NET_CAPABILITY_INTERNET, + NET_CAPABILITY_NOT_CONGESTED, + NET_CAPABILITY_NOT_METERED, + NET_CAPABILITY_NOT_RESTRICTED, + NET_CAPABILITY_NOT_ROAMING, + NET_CAPABILITY_NOT_SUSPENDED, + NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_PARTIAL_CONNECTIVITY, + NET_CAPABILITY_TEMPORARILY_NOT_METERED, + NET_CAPABILITY_TRUSTED, + NET_CAPABILITY_VALIDATED); + private final NetworkCapabilities mNetworkCapabilities; + // A boolean that represents the user modified NOT_VCN_MANAGED capability. + private boolean mModifiedNotVcnManaged = false; + /** * Default constructor for Builder. */ @@ -177,6 +217,7 @@ public class NetworkRequest implements Parcelable { // maybeMarkCapabilitiesRestricted() doesn't add back. final NetworkCapabilities nc = new NetworkCapabilities(mNetworkCapabilities); nc.maybeMarkCapabilitiesRestricted(); + deduceNotVcnManagedCapability(nc); return new NetworkRequest(nc, ConnectivityManager.TYPE_NONE, ConnectivityManager.REQUEST_ID_UNSET, Type.NONE); } @@ -193,6 +234,9 @@ public class NetworkRequest implements Parcelable { */ public Builder addCapability(@NetworkCapabilities.NetCapability int capability) { mNetworkCapabilities.addCapability(capability); + if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) { + mModifiedNotVcnManaged = true; + } return this; } @@ -204,6 +248,9 @@ public class NetworkRequest implements Parcelable { */ public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) { mNetworkCapabilities.removeCapability(capability); + if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) { + mModifiedNotVcnManaged = true; + } return this; } @@ -261,6 +308,9 @@ public class NetworkRequest implements Parcelable { @NonNull public Builder clearCapabilities() { mNetworkCapabilities.clearAll(); + // If the caller explicitly clear all capabilities, the NOT_VCN_MANAGED capabilities + // should not be add back later. + mModifiedNotVcnManaged = true; return this; } @@ -380,6 +430,25 @@ public class NetworkRequest implements Parcelable { mNetworkCapabilities.setSignalStrength(signalStrength); return this; } + + /** + * Deduce the NET_CAPABILITY_NOT_VCN_MANAGED capability from other capabilities + * and user intention, which includes: + * 1. For the requests that don't have anything besides + * {@link #VCN_SUPPORTED_CAPABILITIES}, add the NET_CAPABILITY_NOT_VCN_MANAGED to + * allow the callers automatically utilize VCN networks if available. + * 2. For the requests that explicitly add or remove NET_CAPABILITY_NOT_VCN_MANAGED, + * do not alter them to allow user fire request that suits their need. + * + * @hide + */ + private void deduceNotVcnManagedCapability(final NetworkCapabilities nc) { + if (mModifiedNotVcnManaged) return; + for (final int cap : nc.getCapabilities()) { + if (!VCN_SUPPORTED_CAPABILITIES.contains(cap)) return; + } + nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + } } // implement the Parcelable interface diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java index 8be4af7b1396..b5e8a614b8ea 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java +++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java @@ -29,7 +29,6 @@ import java.math.BigInteger; import java.net.Inet4Address; import java.net.InetAddress; import java.net.SocketException; -import java.net.UnknownHostException; import java.util.Locale; import java.util.TreeSet; @@ -92,7 +91,8 @@ public class NetworkUtils { * this socket will go directly to the underlying network, so its traffic will not be * forwarded through the VPN. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553, + publicAlternatives = "Use {@link android.net.VpnService#protect} instead.") public static native boolean protectFromVpn(FileDescriptor fd); /** @@ -232,46 +232,6 @@ public class NetworkUtils { } /** - * Masks a raw IP address byte array with the specified prefix length. - */ - public static void maskRawAddress(byte[] array, int prefixLength) { - if (prefixLength < 0 || prefixLength > array.length * 8) { - throw new RuntimeException("IP address with " + array.length + - " bytes has invalid prefix length " + prefixLength); - } - - int offset = prefixLength / 8; - int remainder = prefixLength % 8; - byte mask = (byte)(0xFF << (8 - remainder)); - - if (offset < array.length) array[offset] = (byte)(array[offset] & mask); - - offset++; - - for (; offset < array.length; offset++) { - array[offset] = 0; - } - } - - /** - * Get InetAddress masked with prefixLength. Will never return null. - * @param address the IP address to mask with - * @param prefixLength the prefixLength used to mask the IP - */ - public static InetAddress getNetworkPart(InetAddress address, int prefixLength) { - byte[] array = address.getAddress(); - maskRawAddress(array, prefixLength); - - InetAddress netPart = null; - try { - netPart = InetAddress.getByAddress(array); - } catch (UnknownHostException e) { - throw new RuntimeException("getNetworkPart error - " + e.toString()); - } - return netPart; - } - - /** * Returns the implicit netmask of an IPv4 address, as was the custom before 1993. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java index 0242ba08742c..340141b78aa5 100644 --- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java +++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 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. diff --git a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java index 85e3fa3048ed..43fffd733e91 100644 --- a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java +++ b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java @@ -40,6 +40,8 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.Arrays; import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; /** * A class to encapsulate management of the "Smart Networking" capability of @@ -73,6 +75,32 @@ public class MultinetworkPolicyTracker { private volatile int mMeteredMultipathPreference; private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + // Mainline module can't use internal HandlerExecutor, so add an identical executor here. + private static class HandlerExecutor implements Executor { + @NonNull + private final Handler mHandler; + + HandlerExecutor(@NonNull Handler handler) { + mHandler = handler; + } + @Override + public void execute(Runnable command) { + if (!mHandler.post(command)) { + throw new RejectedExecutionException(mHandler + " is shutting down"); + } + } + } + + @VisibleForTesting + protected class ActiveDataSubscriptionIdChangedListener extends PhoneStateListener + implements PhoneStateListener.ActiveDataSubscriptionIdChangedListener { + @Override + public void onActiveDataSubscriptionIdChanged(int subId) { + mActiveSubId = subId; + reevaluateInternal(); + } + } + public MultinetworkPolicyTracker(Context ctx, Handler handler) { this(ctx, handler, null); } @@ -93,14 +121,8 @@ public class MultinetworkPolicyTracker { } }; - ctx.getSystemService(TelephonyManager.class).listen( - new PhoneStateListener(handler.getLooper()) { - @Override - public void onActiveDataSubscriptionIdChanged(int subId) { - mActiveSubId = subId; - reevaluateInternal(); - } - }, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); + ctx.getSystemService(TelephonyManager.class).registerPhoneStateListener( + new HandlerExecutor(handler), new ActiveDataSubscriptionIdChangedListener()); updateAvoidBadWifi(); updateMeteredMultipathPreference(); diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp index ed1716fad8c0..f20b89fb842c 100644 --- a/packages/Connectivity/service/Android.bp +++ b/packages/Connectivity/service/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + cc_library_shared { name: "libservice-connectivity", // TODO: build against the NDK (sdk_version: "30" for example) diff --git a/packages/CtsShim/Android.bp b/packages/CtsShim/Android.bp index 49608b3856e0..31cd76079131 100644 --- a/packages/CtsShim/Android.bp +++ b/packages/CtsShim/Android.bp @@ -17,6 +17,15 @@ //########################################################## // Variant: Privileged app +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"], +} + android_app_import { name: "CtsShimPrivPrebuilt", diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp index 14a3376380df..0b3f9bb62a9b 100644 --- a/packages/CtsShim/build/Android.bp +++ b/packages/CtsShim/build/Android.bp @@ -17,6 +17,15 @@ //########################################################## // Variant: Privileged app upgrade +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"], +} + android_app { name: "CtsShimPrivUpgrade", // this needs to be a privileged application diff --git a/packages/CtsShim/build/jni/Android.bp b/packages/CtsShim/build/jni/Android.bp index 4a1973cc0b46..ba586dbb2d88 100644 --- a/packages/CtsShim/build/jni/Android.bp +++ b/packages/CtsShim/build/jni/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + cc_library_shared { name: "libshim_jni", srcs: ["Shim.c"], diff --git a/packages/DynamicSystemInstallationService/Android.bp b/packages/DynamicSystemInstallationService/Android.bp index a8cf5d605f17..ad86f4667f67 100644 --- a/packages/DynamicSystemInstallationService/Android.bp +++ b/packages/DynamicSystemInstallationService/Android.bp @@ -1,3 +1,22 @@ +package { + default_applicable_licenses: [ + "frameworks_base_packages_DynamicSystemInstallationService_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_packages_DynamicSystemInstallationService_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + android_app { name: "DynamicSystemInstallationService", defaults: ["platform_app_defaults"], diff --git a/packages/DynamicSystemInstallationService/res/values-eu/strings.xml b/packages/DynamicSystemInstallationService/res/values-eu/strings.xml index 6576edd386d6..b0d45e1d061a 100644 --- a/packages/DynamicSystemInstallationService/res/values-eu/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-eu/strings.xml @@ -5,7 +5,7 @@ <string name="notification_install_completed" msgid="6252047868415172643">"Prest dago sistema dinamikoa. Erabiltzen hasteko, berrabiarazi gailua."</string> <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalatzen"</string> <string name="notification_install_failed" msgid="4066039210317521404">"Ezin izan da instalatu"</string> - <string name="notification_image_validation_failed" msgid="2720357826403917016">"Ezin izan da balidatu irudia. Utzi bertan behera instalazioa."</string> + <string name="notification_image_validation_failed" msgid="2720357826403917016">"Ezin izan da baliozkotu irudia. Utzi bertan behera instalazioa."</string> <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Sistema dinamiko bat abian da. Berrabiarazi Android-en jatorrizko bertsioa erabiltzeko."</string> <string name="notification_action_cancel" msgid="5929299408545961077">"Utzi"</string> <string name="notification_action_discard" msgid="1817481003134947493">"Baztertu"</string> diff --git a/packages/DynamicSystemInstallationService/tests/Android.bp b/packages/DynamicSystemInstallationService/tests/Android.bp index 3bdf82966889..50b65b4067fd 100644 --- a/packages/DynamicSystemInstallationService/tests/Android.bp +++ b/packages/DynamicSystemInstallationService/tests/Android.bp @@ -1,3 +1,14 @@ +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_packages_DynamicSystemInstallationService_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: [ + "frameworks_base_packages_DynamicSystemInstallationService_license", + ], +} + android_test { name: "DynamicSystemInstallationServiceTests", diff --git a/packages/EasterEgg/Android.bp b/packages/EasterEgg/Android.bp index b858ab01ffd9..f8785f2b8e2c 100644 --- a/packages/EasterEgg/Android.bp +++ b/packages/EasterEgg/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_app { // the build system in pi-dev can't quite handle R.java in kt // so we will have a mix of java and kotlin files diff --git a/packages/EncryptedLocalTransport/Android.bp b/packages/EncryptedLocalTransport/Android.bp index 00e9c71c7654..09e563076c95 100644 --- a/packages/EncryptedLocalTransport/Android.bp +++ b/packages/EncryptedLocalTransport/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_app { name: "EncryptedLocalTransport", defaults: ["platform_app_defaults"], diff --git a/packages/ExtShared/Android.bp b/packages/ExtShared/Android.bp index 279ac9de9035..b1fd7f64292d 100644 --- a/packages/ExtShared/Android.bp +++ b/packages/ExtShared/Android.bp @@ -12,6 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_base_packages_ExtShared_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_packages_ExtShared_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + android_app { name: "ExtShared", defaults: ["platform_app_defaults"], diff --git a/packages/ExternalStorageProvider/Android.bp b/packages/ExternalStorageProvider/Android.bp index f1e629961e11..0bbdf7aae9a5 100644 --- a/packages/ExternalStorageProvider/Android.bp +++ b/packages/ExternalStorageProvider/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "ExternalStorageProvider", defaults: ["platform_app_defaults"], diff --git a/packages/ExternalStorageProvider/tests/Android.bp b/packages/ExternalStorageProvider/tests/Android.bp index 04cf01af073a..633f18610921 100644 --- a/packages/ExternalStorageProvider/tests/Android.bp +++ b/packages/ExternalStorageProvider/tests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "ExternalStorageProviderTests", diff --git a/packages/FakeOemFeatures/Android.bp b/packages/FakeOemFeatures/Android.bp index b63e3a136700..25e3a862f6c4 100644 --- a/packages/FakeOemFeatures/Android.bp +++ b/packages/FakeOemFeatures/Android.bp @@ -1,3 +1,22 @@ +package { + default_applicable_licenses: [ + "frameworks_base_packages_FakeOemFeatures_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_packages_FakeOemFeatures_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + android_app { name: "FakeOemFeatures", defaults: ["platform_app_defaults"], diff --git a/packages/FusedLocation/Android.bp b/packages/FusedLocation/Android.bp index ada463ac6b59..64b4c54e74ad 100644 --- a/packages/FusedLocation/Android.bp +++ b/packages/FusedLocation/Android.bp @@ -12,6 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: [ + "frameworks_base_packages_FusedLocation_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_packages_FusedLocation_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + android_app { name: "FusedLocation", defaults: ["platform_app_defaults"], diff --git a/packages/InputDevices/Android.bp b/packages/InputDevices/Android.bp index 5afbe729d960..477acc5e71a8 100644 --- a/packages/InputDevices/Android.bp +++ b/packages/InputDevices/Android.bp @@ -12,6 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: [ + "frameworks_base_packages_InputDevices_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_packages_InputDevices_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + android_app { name: "InputDevices", defaults: ["platform_app_defaults"], diff --git a/packages/InputDevices/OWNERS b/packages/InputDevices/OWNERS index 0313a40f7270..f0d6db88bcc5 100644 --- a/packages/InputDevices/OWNERS +++ b/packages/InputDevices/OWNERS @@ -1,2 +1 @@ -michaelwr@google.com -svv@google.com +include /services/core/java/com/android/server/input/OWNERS diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml index 0346d74ca7a7..513eba2ddc6d 100644 --- a/packages/InputDevices/res/values-eu/strings.xml +++ b/packages/InputDevices/res/values-eu/strings.xml @@ -42,7 +42,7 @@ <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greziarra"</string> <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Hebrearra"</string> <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Lituaniera"</string> - <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espainiera (Latinoamerika)"</string> + <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Espainiarra (Latinoamerika)"</string> <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Letoniera"</string> <string name="keyboard_layout_persian" msgid="3920643161015888527">"Persiarra"</string> <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Azerbaijandarra"</string> diff --git a/packages/LocalTransport/Android.bp b/packages/LocalTransport/Android.bp index 9a98a86002b8..d4fa1915a140 100644 --- a/packages/LocalTransport/Android.bp +++ b/packages/LocalTransport/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_app { name: "LocalTransport", defaults: ["platform_app_defaults"], diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java index c9d8a8198ca4..139c8e59a148 100644 --- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java +++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java @@ -162,6 +162,9 @@ public class LocalTransport extends BackupTransport { if (mParameters.isFakeEncryptionFlag()) { flags |= BackupAgent.FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED; } + if (mParameters.isDeviceTransfer()) { + flags |= BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER; + } return flags; } diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java index 8b4db92910f5..2946db3cdcf0 100644 --- a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java +++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java @@ -27,9 +27,11 @@ public class LocalTransportParameters extends KeyValueSettingObserver { private static final String SETTING = Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS; private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag"; private static final String KEY_NON_INCREMENTAL_ONLY = "non_incremental_only"; + private static final String KEY_IS_DEVICE_TRANSFER = "is_device_transfer"; private boolean mFakeEncryptionFlag; private boolean mIsNonIncrementalOnly; + private boolean mIsDeviceTransfer; public LocalTransportParameters(Handler handler, ContentResolver resolver) { super(handler, resolver, Settings.Secure.getUriFor(SETTING)); @@ -43,6 +45,10 @@ public class LocalTransportParameters extends KeyValueSettingObserver { return mIsNonIncrementalOnly; } + boolean isDeviceTransfer() { + return mIsDeviceTransfer; + } + public String getSettingValue(ContentResolver resolver) { return Settings.Secure.getString(resolver, SETTING); } @@ -50,5 +56,6 @@ public class LocalTransportParameters extends KeyValueSettingObserver { public void update(KeyValueListParser parser) { mFakeEncryptionFlag = parser.getBoolean(KEY_FAKE_ENCRYPTION_FLAG, false); mIsNonIncrementalOnly = parser.getBoolean(KEY_NON_INCREMENTAL_ONLY, false); + mIsDeviceTransfer = parser.getBoolean(KEY_IS_DEVICE_TRANSFER, false); } } diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp index 4d9c675de3ae..4106ac5e10b1 100644 --- a/packages/PackageInstaller/Android.bp +++ b/packages/PackageInstaller/Android.bp @@ -12,6 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: [ + "frameworks_base_packages_PackageInstaller_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_packages_PackageInstaller_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + android_app { name: "PackageInstaller", defaults: ["platform_app_defaults"], diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml index d411831eef3f..65e75cdba405 100644 --- a/packages/PackageInstaller/res/values-eu/strings.xml +++ b/packages/PackageInstaller/res/values-eu/strings.xml @@ -83,9 +83,9 @@ <string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak tableta honetan."</string> <string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telebista honetan."</string> <string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telefono honetan."</string> - <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string> - <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string> - <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string> + <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string> + <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string> + <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string> <string name="anonymous_source_continue" msgid="4375745439457209366">"Egin aurrera"</string> <string name="external_sources_settings" msgid="4046964413071713807">"Ezarpenak"</string> <string name="wear_app_channel" msgid="1960809674709107850">"Wear aplikazioak instalatzea/desinstalatzea"</string> diff --git a/packages/PrintRecommendationService/Android.bp b/packages/PrintRecommendationService/Android.bp index d368f3cb18f9..084dd99833c0 100644 --- a/packages/PrintRecommendationService/Android.bp +++ b/packages/PrintRecommendationService/Android.bp @@ -12,6 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: [ + "frameworks_base_packages_PrintRecommendationService_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_packages_PrintRecommendationService_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + android_app { name: "PrintRecommendationService", defaults: ["platform_app_defaults"], diff --git a/packages/PrintSpooler/Android.bp b/packages/PrintSpooler/Android.bp index d38fd020bb5f..772c69fe079b 100644 --- a/packages/PrintSpooler/Android.bp +++ b/packages/PrintSpooler/Android.bp @@ -12,6 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: [ + "frameworks_base_packages_PrintSpooler_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_packages_PrintSpooler_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + android_app { name: "PrintSpooler", defaults: ["platform_app_defaults"], diff --git a/packages/PrintSpooler/jni/Android.bp b/packages/PrintSpooler/jni/Android.bp index 789312e58863..44df70e89d54 100644 --- a/packages/PrintSpooler/jni/Android.bp +++ b/packages/PrintSpooler/jni/Android.bp @@ -1,3 +1,14 @@ +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_packages_PrintSpooler_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: [ + "frameworks_base_packages_PrintSpooler_license", + ], +} + cc_library_shared { name: "libprintspooler_jni", diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml index 719fc9219450..e69ca7664b53 100644 --- a/packages/PrintSpooler/res/values-fa/strings.xml +++ b/packages/PrintSpooler/res/values-fa/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/PrintSpooler/tests/outofprocess/Android.bp b/packages/PrintSpooler/tests/outofprocess/Android.bp index 0e028b04aaaf..69a1d7fa59e4 100644 --- a/packages/PrintSpooler/tests/outofprocess/Android.bp +++ b/packages/PrintSpooler/tests/outofprocess/Android.bp @@ -12,6 +12,17 @@ // 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_packages_PrintSpooler_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: [ + "frameworks_base_packages_PrintSpooler_license", + ], +} + android_test { name: "PrintSpoolerOutOfProcessTests", diff --git a/packages/SettingsLib/ActionBarShadow/Android.bp b/packages/SettingsLib/ActionBarShadow/Android.bp index d2848564abea..800ab671cedb 100644 --- a/packages/SettingsLib/ActionBarShadow/Android.bp +++ b/packages/SettingsLib/ActionBarShadow/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibActionBarShadow", diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp index cd3fb0cc2143..51c9c39cdf03 100644 --- a/packages/SettingsLib/ActionButtonsPreference/Android.bp +++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibActionButtonsPreference", diff --git a/packages/SettingsLib/AdaptiveIcon/Android.bp b/packages/SettingsLib/AdaptiveIcon/Android.bp index 7f4442deecd6..934aacfddfb9 100644 --- a/packages/SettingsLib/AdaptiveIcon/Android.bp +++ b/packages/SettingsLib/AdaptiveIcon/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibAdaptiveIcon", diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 0d4e7460c21f..f2a0e1cb83bd 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLib", @@ -57,6 +66,7 @@ java_defaults { "SettingsLibBannerMessagePreference", "SettingsLibFooterPreference", "SettingsLibUsageProgressBarPreference", + "SettingsLibCollapsingToolbarBaseActivity", ], } diff --git a/packages/SettingsLib/AppPreference/Android.bp b/packages/SettingsLib/AppPreference/Android.bp index b07176a064f9..1817a77981a9 100644 --- a/packages/SettingsLib/AppPreference/Android.bp +++ b/packages/SettingsLib/AppPreference/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibAppPreference", diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp index 095975afa13a..82e837bcd3ac 100644 --- a/packages/SettingsLib/BannerMessagePreference/Android.bp +++ b/packages/SettingsLib/BannerMessagePreference/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibBannerMessagePreference", diff --git a/packages/SettingsLib/BarChartPreference/Android.bp b/packages/SettingsLib/BarChartPreference/Android.bp index 477e8979b03b..ae2606661697 100644 --- a/packages/SettingsLib/BarChartPreference/Android.bp +++ b/packages/SettingsLib/BarChartPreference/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibBarChartPreference", diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp new file mode 100644 index 000000000000..ed49bf4d5385 --- /dev/null +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp @@ -0,0 +1,23 @@ +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"], +} + +android_library { + name: "SettingsLibCollapsingToolbarBaseActivity", + + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + + static_libs: [ + "androidx.annotation_annotation", + "androidx.core_core", + "com.google.android.material_material", + ], + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.xml new file mode 100644 index 000000000000..dabba6832134 --- /dev/null +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/AndroidManifest.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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.collapsingtoolbar"> + + <uses-sdk android:minSdkVersion="21" /> + +</manifest> diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml new file mode 100644 index 000000000000..e376930645ce --- /dev/null +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout/collapsing_toolbar_base_layout.xml @@ -0,0 +1,60 @@ +<?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. +--> +<androidx.coordinatorlayout.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/content_parent" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:transitionGroup="true"> + + <com.google.android.material.appbar.AppBarLayout + android:id="@+id/app_bar" + android:layout_width="match_parent" + android:layout_height="180dp" + android:theme="@style/Theme.CollapsingToolbar.Settings"> + + <com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout + android:id="@+id/collapsing_toolbar" + android:background="?android:attr/colorPrimary" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:maxLines="3" + app:contentScrim="?android:attr/colorPrimary" + app:collapsedTitleTextAppearance="@style/CollapsingToolbarTitle.Collapsed" + app:statusBarScrim="?android:attr/colorPrimary" + app:layout_scrollFlags="scroll|exitUntilCollapsed" + app:expandedTitleMarginStart="18dp" + app:expandedTitleMarginEnd="18dp" + app:toolbarId="@id/action_bar"> + + <Toolbar + android:id="@+id/action_bar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:theme="?android:attr/actionBarTheme" + app:layout_collapseMode="pin"/> + + </com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout> + </com.google.android.material.appbar.AppBarLayout> + + <FrameLayout + android:id="@+id/content_frame" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:layout_behavior="@string/appbar_scrolling_view_behavior"/> +</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml new file mode 100644 index 000000000000..e20775ef45fd --- /dev/null +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.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. +--> +<resources> + <style name="Theme.CollapsingToolbar.Settings" + parent="@style/Theme.MaterialComponents.DayNight"> + <item name="colorPrimary">@*android:color/primary_dark_device_default_settings</item> + <item name="colorAccent">@*android:color/accent_device_default_dark</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml new file mode 100644 index 000000000000..e1a64d446ea2 --- /dev/null +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml @@ -0,0 +1,30 @@ +<?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="CollapsingToolbarTitle.Collapsed" + parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"> + </style> + + <style name="CollapsingToolbarTitle" + parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"> + <item name="android:textSize">36sp</item> + </style> + + <style name="CollapsingToolbarTitle.MoreThanTwoLines"> + <item name="android:textSize">24sp</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml new file mode 100644 index 000000000000..de545b0711e3 --- /dev/null +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.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. +--> +<resources> + <style name="Theme.CollapsingToolbar.Settings" + parent="@style/Theme.MaterialComponents.DayNight"> + <item name="colorPrimary">@*android:color/primary_device_default_settings_light</item> + <item name="colorAccent">@*android:color/accent_device_default_light</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java new file mode 100644 index 000000000000..e75a97857456 --- /dev/null +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java @@ -0,0 +1,92 @@ +/* + * 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.settingslib.collapsingtoolbar; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.android.material.appbar.CollapsingToolbarLayout; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * A customized version of CollapsingToolbarLayout that can apply different font size based on + * the line count of its title. + */ +public class AdjustableToolbarLayout extends CollapsingToolbarLayout { + + private static final int TOOLBAR_MAX_LINE_NUMBER = 2; + + public AdjustableToolbarLayout(@NonNull Context context) { + this(context, null); + + } + + public AdjustableToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public AdjustableToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + initCollapsingToolbar(); + } + + private void initCollapsingToolbar() { + this.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + v.removeOnLayoutChangeListener(this); + final int count = getLineCount(); + if (count > TOOLBAR_MAX_LINE_NUMBER) { + setExpandedTitleTextAppearance(R.style.CollapsingToolbarTitle_MoreThanTwoLines); + } else { + setExpandedTitleTextAppearance(R.style.CollapsingToolbarTitle); + } + } + }); + } + + /** + * Returns a number of title line count for CollapsingToolbarLayout so that facilitates the + * determination to apply what kind of font size. Since the title of CollapsingToolbarLayout is + * drawn in a canvas and the text process is wrapped in a CollapsingTextHelper, the way we used + * here is to get the line count from the CollapsingTextHelper via Java Reflection. + */ + private int getLineCount() { + try { + final Field textHelperField = this.getClass().getDeclaredField("collapsingTextHelper"); + textHelperField.setAccessible(true); + final Object textHelperObj = textHelperField.get(this); + + final Field layoutField = textHelperObj.getClass().getDeclaredField("textLayout"); + layoutField.setAccessible(true); + final Object layoutObj = layoutField.get(textHelperObj); + + final Method method = layoutObj.getClass().getDeclaredMethod("getLineCount"); + return (int) method.invoke(layoutObj); + } catch (Exception e) { + return 0; + } + } +} diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java new file mode 100644 index 000000000000..637805fc81c3 --- /dev/null +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java @@ -0,0 +1,99 @@ +/* + * 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.settingslib.collapsingtoolbar; + +import android.app.ActionBar; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toolbar; + +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; + +import com.google.android.material.appbar.CollapsingToolbarLayout; + +/** + * A base Activity that has a collapsing toolbar layout is used for the activities intending to + * enable the collapsing toolbar function. + */ +public class CollapsingToolbarBaseActivity extends FragmentActivity { + + private CollapsingToolbarLayout mCollapsingToolbarLayout; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + super.setContentView(R.layout.collapsing_toolbar_base_layout); + mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar); + + final Toolbar toolbar = findViewById(R.id.action_bar); + setActionBar(toolbar); + + // Enable title and home button by default + final ActionBar actionBar = getActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + actionBar.setDisplayShowTitleEnabled(true); + } + } + + @Override + public void setContentView(int layoutResID) { + final ViewGroup parent = findViewById(R.id.content_frame); + if (parent != null) { + parent.removeAllViews(); + } + LayoutInflater.from(this).inflate(layoutResID, parent); + } + + @Override + public void setContentView(View view) { + ((ViewGroup) findViewById(R.id.content_frame)).addView(view); + } + + @Override + public void setContentView(View view, ViewGroup.LayoutParams params) { + ((ViewGroup) findViewById(R.id.content_frame)).addView(view, params); + } + + @Override + public void setTitle(CharSequence title) { + if (mCollapsingToolbarLayout != null) { + mCollapsingToolbarLayout.setTitle(title); + } + super.setTitle(title); + } + + @Override + public void setTitle(int titleId) { + if (mCollapsingToolbarLayout != null) { + mCollapsingToolbarLayout.setTitle(getText(titleId)); + } + super.setTitle(titleId); + } + + /** + * Returns an instance of collapsing toolbar. + */ + public CollapsingToolbarLayout getCollapsingToolbarLayout() { + return mCollapsingToolbarLayout; + } +} diff --git a/packages/SettingsLib/DisplayDensityUtils/Android.bp b/packages/SettingsLib/DisplayDensityUtils/Android.bp index 27d0cb5ad48c..b7bfb5fd8af5 100644 --- a/packages/SettingsLib/DisplayDensityUtils/Android.bp +++ b/packages/SettingsLib/DisplayDensityUtils/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibDisplayDensityUtils", diff --git a/packages/SettingsLib/EmergencyNumber/Android.bp b/packages/SettingsLib/EmergencyNumber/Android.bp index 3c41f7834d6c..25b4905c438f 100644 --- a/packages/SettingsLib/EmergencyNumber/Android.bp +++ b/packages/SettingsLib/EmergencyNumber/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibEmergencyNumber", diff --git a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java index 4d454941af7c..b0a9b95492a6 100644 --- a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java +++ b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java @@ -45,13 +45,22 @@ public class EmergencyNumberUtils { public static final Uri EMERGENCY_NUMBER_OVERRIDE_AUTHORITY = new Uri.Builder().scheme( ContentResolver.SCHEME_CONTENT) - .authority("com.android.emergency.numbers") + .authority("com.android.emergency.gesture") .build(); public static final String METHOD_NAME_GET_EMERGENCY_NUMBER_OVERRIDE = "GET_EMERGENCY_NUMBER_OVERRIDE"; public static final String METHOD_NAME_SET_EMERGENCY_NUMBER_OVERRIDE = "SET_EMERGENCY_NUMBER_OVERRIDE"; + public static final String METHOD_NAME_SET_EMERGENCY_GESTURE = "SET_EMERGENCY_GESTURE"; + public static final String METHOD_NAME_SET_EMERGENCY_SOUND = "SET_EMERGENCY_SOUND"; + public static final String METHOD_NAME_GET_EMERGENCY_GESTURE_ENABLED = "GET_EMERGENCY_GESTURE"; + public static final String METHOD_NAME_GET_EMERGENCY_GESTURE_SOUND_ENABLED = + "GET_EMERGENCY_SOUND"; public static final String EMERGENCY_GESTURE_CALL_NUMBER = "emergency_gesture_call_number"; + public static final String EMERGENCY_SETTING_VALUE = "emergency_setting_value"; + public static final int EMERGENCY_SETTING_ON = 1; + public static final int EMERGENCY_SETTING_OFF = 0; + @VisibleForTesting static final String FALL_BACK_NUMBER = "112"; @@ -103,6 +112,51 @@ public class EmergencyNumberUtils { METHOD_NAME_SET_EMERGENCY_NUMBER_OVERRIDE, null /* args */, bundle); } + /** + * Enable/disable the emergency gesture setting + */ + public void setEmergencyGestureEnabled(boolean enabled) { + final Bundle bundle = new Bundle(); + bundle.putInt(EMERGENCY_SETTING_VALUE, + enabled ? EMERGENCY_SETTING_ON : EMERGENCY_SETTING_OFF); + mContext.getContentResolver().call(EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, + METHOD_NAME_SET_EMERGENCY_GESTURE, null /* args */, bundle); + } + + /** + * Enable/disable the emergency gesture sound setting + */ + public void setEmergencySoundEnabled(boolean enabled) { + final Bundle bundle = new Bundle(); + bundle.putInt(EMERGENCY_SETTING_VALUE, + enabled ? EMERGENCY_SETTING_ON : EMERGENCY_SETTING_OFF); + mContext.getContentResolver().call(EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, + METHOD_NAME_SET_EMERGENCY_SOUND, null /* args */, bundle); + } + + /** + * Whether or not emergency gesture is enabled. + */ + public boolean getEmergencyGestureEnabled() { + final Bundle bundle = mContext.getContentResolver().call( + EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, + METHOD_NAME_GET_EMERGENCY_GESTURE_ENABLED, null /* args */, null /* bundle */); + return bundle == null ? true : bundle.getInt(EMERGENCY_SETTING_VALUE, EMERGENCY_SETTING_ON) + == EMERGENCY_SETTING_ON; + } + + /** + * Whether or not emergency gesture sound is enabled. + */ + public boolean getEmergencyGestureSoundEnabled() { + final Bundle bundle = mContext.getContentResolver().call( + EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, + METHOD_NAME_GET_EMERGENCY_GESTURE_SOUND_ENABLED, null /* args */, + null /* bundle */); + return bundle == null ? true : bundle.getInt(EMERGENCY_SETTING_VALUE, EMERGENCY_SETTING_OFF) + == EMERGENCY_SETTING_ON; + } + private String getEmergencyNumberOverride() { final Bundle bundle = mContext.getContentResolver().call( EMERGENCY_NUMBER_OVERRIDE_AUTHORITY, diff --git a/packages/SettingsLib/EntityHeaderWidgets/Android.bp b/packages/SettingsLib/EntityHeaderWidgets/Android.bp index 280848a0d7c4..bd83cdce0078 100644 --- a/packages/SettingsLib/EntityHeaderWidgets/Android.bp +++ b/packages/SettingsLib/EntityHeaderWidgets/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibEntityHeaderWidgets", diff --git a/packages/SettingsLib/FooterPreference/Android.bp b/packages/SettingsLib/FooterPreference/Android.bp index 1af967fef717..11f39e7bb210 100644 --- a/packages/SettingsLib/FooterPreference/Android.bp +++ b/packages/SettingsLib/FooterPreference/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibFooterPreference", diff --git a/packages/SettingsLib/HelpUtils/Android.bp b/packages/SettingsLib/HelpUtils/Android.bp index 285131d19725..5826047b9f52 100644 --- a/packages/SettingsLib/HelpUtils/Android.bp +++ b/packages/SettingsLib/HelpUtils/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibHelpUtils", diff --git a/packages/SettingsLib/LayoutPreference/Android.bp b/packages/SettingsLib/LayoutPreference/Android.bp index a1f9a768c76c..8a4e53d80a7c 100644 --- a/packages/SettingsLib/LayoutPreference/Android.bp +++ b/packages/SettingsLib/LayoutPreference/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibLayoutPreference", diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp index 1dc18f5cc4d2..1feec21e24e4 100644 --- a/packages/SettingsLib/MainSwitchPreference/Android.bp +++ b/packages/SettingsLib/MainSwitchPreference/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibMainSwitchPreference", diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml index 52779bcabf00..85c01c5732ca 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/main_switch_bar.xml @@ -19,6 +19,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" + android:background="?android:attr/colorBackground" android:orientation="vertical"> <LinearLayout diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java index 74b65780ffc2..1c9298ed6085 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java @@ -17,6 +17,7 @@ package com.android.settingslib.widget; import android.content.Context; +import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; @@ -29,6 +30,7 @@ import android.widget.Switch; import android.widget.TextView; import androidx.annotation.VisibleForTesting; +import androidx.core.content.res.TypedArrayUtils; import com.android.settingslib.RestrictedLockUtils; @@ -88,6 +90,17 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec }); setChecked(mSwitch.isChecked()); + + if (attrs != null) { + final TypedArray a = context.obtainStyledAttributes(attrs, + androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/, + 0 /*defStyleRes*/); + final CharSequence title = TypedArrayUtils.getText(a, + androidx.preference.R.styleable.Preference_title, + androidx.preference.R.styleable.Preference_android_title); + setTitle(title); + a.recycle(); + } } @Override @@ -126,7 +139,7 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec /** * Set the title text */ - public void setTitle(String text) { + public void setTitle(CharSequence text) { if (mTextView != null) { mTextView.setText(text); } diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java index 274bf8df2222..35afec38dd3d 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java @@ -18,7 +18,6 @@ package com.android.settingslib.widget; import android.content.Context; import android.content.res.TypedArray; -import android.text.TextUtils; import android.util.AttributeSet; import androidx.core.content.res.TypedArrayUtils; @@ -40,7 +39,7 @@ public class MainSwitchPreference extends TwoStatePreference { private final List<OnMainSwitchChangeListener> mSwitchChangeListeners = new ArrayList<>(); private MainSwitchBar mMainSwitchBar; - private String mTitle; + private CharSequence mTitle; private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin; @@ -81,24 +80,28 @@ public class MainSwitchPreference extends TwoStatePreference { setLayoutResource(R.layout.main_switch_layout); if (attrs != null) { - TypedArray a = context.obtainStyledAttributes(attrs, + final TypedArray a = context.obtainStyledAttributes(attrs, androidx.preference.R.styleable.Preference, 0 /*defStyleAttr*/, 0 /*defStyleRes*/); final CharSequence title = TypedArrayUtils.getText(a, androidx.preference.R.styleable.Preference_title, androidx.preference.R.styleable.Preference_android_title); - if (!TextUtils.isEmpty(title)) { - setTitle(title.toString()); - } + setTitle(title); a.recycle(); } } - /** - * Set the preference title text - */ - public void setTitle(String text) { - mTitle = text; + @Override + public void setChecked(boolean checked) { + super.setChecked(checked); + if (mMainSwitchBar != null) { + mMainSwitchBar.setChecked(checked); + } + } + + @Override + public void setTitle(CharSequence title) { + mTitle = title; if (mMainSwitchBar != null) { mMainSwitchBar.setTitle(mTitle); } diff --git a/packages/SettingsLib/ProgressBar/Android.bp b/packages/SettingsLib/ProgressBar/Android.bp index eae21d8858dc..b5bc8f77045b 100644 --- a/packages/SettingsLib/ProgressBar/Android.bp +++ b/packages/SettingsLib/ProgressBar/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibProgressBar", @@ -6,4 +15,4 @@ android_library { sdk_version: "system_current", min_sdk_version: "21", -}
\ No newline at end of file +} diff --git a/packages/SettingsLib/RadioButtonPreference/Android.bp b/packages/SettingsLib/RadioButtonPreference/Android.bp index 136d6daad99a..b309c01e0b7b 100644 --- a/packages/SettingsLib/RadioButtonPreference/Android.bp +++ b/packages/SettingsLib/RadioButtonPreference/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibRadioButtonPreference", diff --git a/packages/SettingsLib/RestrictedLockUtils/Android.bp b/packages/SettingsLib/RestrictedLockUtils/Android.bp index b2f088257bb9..c0623edabefe 100644 --- a/packages/SettingsLib/RestrictedLockUtils/Android.bp +++ b/packages/SettingsLib/RestrictedLockUtils/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibRestrictedLockUtils", @@ -10,4 +19,4 @@ android_library { sdk_version: "system_current", min_sdk_version: "21", -}
\ No newline at end of file +} diff --git a/packages/SettingsLib/SchedulesProvider/Android.bp b/packages/SettingsLib/SchedulesProvider/Android.bp index ef592527ba92..c4373bb2dc1f 100644 --- a/packages/SettingsLib/SchedulesProvider/Android.bp +++ b/packages/SettingsLib/SchedulesProvider/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibSchedulesProvider", diff --git a/packages/SettingsLib/SearchProvider/Android.bp b/packages/SettingsLib/SearchProvider/Android.bp index 5254dde71553..f96011ad5d09 100644 --- a/packages/SettingsLib/SearchProvider/Android.bp +++ b/packages/SettingsLib/SearchProvider/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibSearchProvider", diff --git a/packages/SettingsLib/SearchWidget/Android.bp b/packages/SettingsLib/SearchWidget/Android.bp index 7541ca456138..b7367b4a10a7 100644 --- a/packages/SettingsLib/SearchWidget/Android.bp +++ b/packages/SettingsLib/SearchWidget/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibSearchWidget", diff --git a/packages/SettingsLib/SettingsSpinner/Android.bp b/packages/SettingsLib/SettingsSpinner/Android.bp index ca23616ca588..c5b2fe66c315 100644 --- a/packages/SettingsLib/SettingsSpinner/Android.bp +++ b/packages/SettingsLib/SettingsSpinner/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibSettingsSpinner", diff --git a/packages/SettingsLib/SettingsTheme/Android.bp b/packages/SettingsLib/SettingsTheme/Android.bp index 6579fd9b4238..bda863a71453 100644 --- a/packages/SettingsLib/SettingsTheme/Android.bp +++ b/packages/SettingsLib/SettingsTheme/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibSettingsTheme", diff --git a/packages/SettingsLib/Tile/Android.bp b/packages/SettingsLib/Tile/Android.bp index bf16ef317fd8..cc570ccbb506 100644 --- a/packages/SettingsLib/Tile/Android.bp +++ b/packages/SettingsLib/Tile/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibTile", diff --git a/packages/SettingsLib/TopIntroPreference/Android.bp b/packages/SettingsLib/TopIntroPreference/Android.bp index 03becbd23226..957728120c4d 100644 --- a/packages/SettingsLib/TopIntroPreference/Android.bp +++ b/packages/SettingsLib/TopIntroPreference/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibTopIntroPreference", diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml index 19fa91f72f0d..0287b1f064d0 100644 --- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml +++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml @@ -20,27 +20,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="?android:attr/listPreferredItemHeight" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingStart="20dp" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:background="?android:attr/selectableItemBackground" android:clipToPadding="false"> - <LinearLayout - android:id="@+id/icon_frame" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:minWidth="56dp" - android:gravity="start|top" - android:orientation="horizontal" - android:paddingEnd="12dp" - android:paddingTop="16dp" - android:paddingBottom="4dp"> - <ImageView - android:id="@android:id/icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - </LinearLayout> - <TextView android:id="@android:id/title" android:layout_width="wrap_content" @@ -50,5 +34,5 @@ android:clickable="false" android:longClickable="false" android:maxLines="10" - android:textColor="?android:attr/textColorSecondary"/> + android:textAppearance="@style/TextAppearance.TopIntroText"/> </LinearLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/TopIntroPreference/res/values/styles.xml b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml new file mode 100644 index 000000000000..e7eb9f495310 --- /dev/null +++ b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml @@ -0,0 +1,24 @@ +<?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="TextAppearance.TopIntroText" + parent="@*android:style/TextAppearance.DeviceDefault"> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:textSize">14sp</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> + </style> +</resources> diff --git a/packages/SettingsLib/UsageProgressBarPreference/Android.bp b/packages/SettingsLib/UsageProgressBarPreference/Android.bp index 3331550d0535..ad6e7ab9f564 100644 --- a/packages/SettingsLib/UsageProgressBarPreference/Android.bp +++ b/packages/SettingsLib/UsageProgressBarPreference/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibUsageProgressBarPreference", diff --git a/packages/SettingsLib/Utils/Android.bp b/packages/SettingsLib/Utils/Android.bp index c5f0ee6c939c..1cf42ff1c566 100644 --- a/packages/SettingsLib/Utils/Android.bp +++ b/packages/SettingsLib/Utils/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLibUtils", diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 577fb6611448..eafc6147fae8 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Ignoreer kodewisselingverstekke"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Aktiveer kodewisseling"</string> <string name="transcode_default" msgid="3784803084573509491">"Aanvaar dat programme moderne formate steun"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Wys kodewisselingkennisgewings"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Lopende dienste"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Sien en beheer dienste wat tans aktief is"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Datasein vol."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet is ontkoppel."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Geen oproepe nie."</string> </resources> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index c554e2ed8a8e..547d0af6200d 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"የትራንስኮዲንግ ነባሪዎችን ሻር"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ትራንስኮዲንግን ያንቁ"</string> <string name="transcode_default" msgid="3784803084573509491">"መተግበሪያዎች ዘመናዊ ቅርጸቶችን እንደሚደግፉ አድርገው ይቁጠሩ"</string> + <string name="transcode_notification" msgid="5560515979793436168">"ትራንስኮዲንግ ማሳወቂያዎችን አሳይ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"አሂድ አገልግሎቶች"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"በአሁኑጊዜ እየሄዱ ያሉ አገልግሎቶችን ተቆጣጠር እና እይ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"የWebView ትግበራ"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"የውሂብ አመልካች ሙሉ ነው።"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ኤተርኔት ተነቅሏል።"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ኢተርኔት።"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"መደወል የለም።"</string> </resources> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index b23cebd19e7a..c6a1d18f9c1e 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"إلغاء الإعدادات التلقائية لتحويل الترميز"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"تفعيل تحويل الترميز"</string> <string name="transcode_default" msgid="3784803084573509491">"افتراض أن التطبيق يتوافق مع التنسيقات الحديثة"</string> + <string name="transcode_notification" msgid="5560515979793436168">"إظهار إشعارات تحويل الترميز"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"الخدمات قيد التشغيل"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"عرض الخدمات قيد التشغيل في الوقت الحالي والتحكم فيها"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"تطبيق WebView"</string> @@ -596,6 +597,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"إشارة البيانات كاملة."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"تم قطع اتصال Ethernet."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"إيثرنت"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"لا يتم الاتصال."</string> </resources> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 2380b2fa21ac..e9deb4641e46 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ট্ৰেন্সক’ডিং ডিফ’ল্ট অ’ভাৰৰাইড কৰক"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ট্ৰেন্সক’ডিং সক্ষম কৰক"</string> <string name="transcode_default" msgid="3784803084573509491">"এপে আধুনিক ফৰ্মেট সমৰ্থন কৰে বুলি ধৰি লওক"</string> + <string name="transcode_notification" msgid="5560515979793436168">"ট্ৰান্সক\'ডিঙৰ জাননী দেখুৱাওক"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"চলিত সেৱা"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"বৰ্তমান চলি থকা সেৱাসমূহ চাওক আৰু নিয়ন্ত্ৰণ কৰক"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ৱেবভিউ প্ৰয়োগ"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"ডেটা ছিগনেল পূৰা আছে।"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ইথাৰনেট সংযোগ বিচ্ছিন্ন হৈছে।"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ইথাৰনেট।"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"কল কৰা নহয়"</string> </resources> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index f70bb84c55a4..d04409cd5e56 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Yenidən kodlaşdırma defoltlarını əvəzləyin"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Yenidən kodlaşdırmanı aktiv edin"</string> <string name="transcode_default" msgid="3784803084573509491">"Tətbiqlərin müasir formatları dəstəklədiyini qəbul edin"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Kod dəyişmə bildirişlərini göstərin"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"İşləyən xidmətlər"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Hazırda prosesdə olan xidmətləri görüntüləyin və onlara nəzarət edin"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView icrası"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Data siqnalı tamdır."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet bağlantısı kəsilib."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Zəng yoxdur."</string> </resources> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 749f864f8aef..ecd70a285bd0 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Zameni podrazumevana podešavanja transkodiranja"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Omogući transkodiranje"</string> <string name="transcode_default" msgid="3784803084573509491">"Podrazumevaj da aplikacije podržavaju moderne formate"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Prikazuj obaveštenja o transkodiranju"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Pokrenute usluge"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Prikaz i kontrola trenutno pokrenutih usluga"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Primena WebView-a"</string> @@ -593,6 +594,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Signal za podatke je najjači."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Veza sa eternetom je prekinuta."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Eternet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Bez pozivanja."</string> </resources> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 6b2740d7d7d7..6a3b961d1c5c 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Перавызначыць стандартныя налады перакадзіравання"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Уключыць перакадзіраванне"</string> <string name="transcode_default" msgid="3784803084573509491">"Лічыца, што праграмы падтрымліваюць сучасныя фарматы"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Паказваць апавяшчэнні пра перакадзіраванне"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Запушчаныя службы"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Прагляд запушчаных службаў i кіраванне iмi"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Рэалізацыя WebView"</string> @@ -594,6 +595,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Поўны сігнал перадачы дадзеных."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet адлучаны."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Ніякіх выклікаў."</string> </resources> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 1a24b9e52b36..d2bf70565be9 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Отмяна на стандартните настройки за прекодирането"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Активиране на прекодирането"</string> <string name="transcode_default" msgid="3784803084573509491">"Предполагане, че приложенията поддържат съвременни формати"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Показване на известията за прекодиране"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Изпълнявани услуги"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Преглед и контрол върху изпълняващите се понастоящем услуги"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Внедряване на WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Сигналът за данни е пълен."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Връзката с Ethernet е прекратена."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Без обаждания."</string> </resources> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 1c9f84f68962..3ccf0e426b77 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ট্রান্সকোডিং ডিফল্ট সেটিংস ওভাররাইড করুন"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ট্রান্সকোডিং চালু করুন"</string> <string name="transcode_default" msgid="3784803084573509491">"অ্যাপ মর্ডার্ন ফর্ম্যাটে কাজ করবে বলে ধরে নিন"</string> + <string name="transcode_notification" msgid="5560515979793436168">"ট্রান্সকোডিং বিজ্ঞপ্তি দেখুন"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"এখন চলছে যে পরিষেবাগুলি"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"বর্তমান চলমান পরিষেবাগুলি দেখুন এবং নিয়ন্ত্রণ করুন"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ওয়েবভিউ প্রয়োগ"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"পূর্ণ ডেটার সংকেত রয়েছে৷"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ইথারনেটের সংযোগ বিচ্ছিন্ন হয়েছে৷"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ইথারনেট।"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"কল করবেন না।"</string> </resources> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index f70142187a36..f01eef340abc 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Zaobiđi zadane postavke transkodiranja"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Omogući transkodiranje"</string> <string name="transcode_default" msgid="3784803084573509491">"Pretpostavi da aplikacije podržavaju moderne formate"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Prikaži obavještenja o transkodiranju"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Pokrenute usluge"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Prikaz i kontrola trenutno pokrenutih usluga"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Postavljanje WebViewa"</string> @@ -593,6 +594,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Signal za prijenos podataka pun."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Veza sa Ethernetom je prekinuta."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Nema pozivanja."</string> </resources> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 78ef10969b72..7069999fc8e7 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Substitueix els valors predeterminats de la transcodificació"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Activa la transcodificació"</string> <string name="transcode_default" msgid="3784803084573509491">"Assumeix que les aplicacions són compatibles amb formats moderns"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Mostra les notificacions de transcodificació"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Serveis en execució"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualitza i controla els serveis en execució"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementació de WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Senyal de dades: complet."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"S\'ha desconnectat l\'Ethernet."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Sense trucades."</string> </resources> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 8ca9800562a0..c677feffc384 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Přepsat výchozí nastavení překódování"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Povolit překódování"</string> <string name="transcode_default" msgid="3784803084573509491">"Předpokládat, že aplikace podporují moderní formáty"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Zobrazit oznámení o překódování"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Spuštěné služby"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Umožňuje zobrazit a ovládat aktuálně spuštěné služby"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementace WebView"</string> @@ -594,6 +595,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Plný signál datové sítě."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Síť ethernet je odpojena."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Bez volání."</string> </resources> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 5d55a12b4d6c..84247a622740 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Tilsidesæt standardindstillingerne for omkodning"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Aktivér omkodning"</string> <string name="transcode_default" msgid="3784803084573509491">"Gå ud fra, at apps understøtter moderne formater"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Vis notifikationer for omkodning"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Kørende tjenester"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Vis og administrer kørende tjenester"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Datasignal fuldt."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet er ikke tilsluttet."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Opkald er deaktiveret."</string> </resources> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 1fbb391741b5..4cc79d32b797 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Standardeinstellungen für Transcodierung überschreiben"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Transcodierung aktivieren"</string> <string name="transcode_default" msgid="3784803084573509491">"Voraussetzen, dass Apps moderne Formate unterstützen"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Benachrichtigungen zur Transcodierung anzeigen"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktive Dienste"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Momentan ausgeführte Dienste anzeigen und steuern"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-Implementierung"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Volle Datensignalstärke"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet nicht verbunden"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Keine Anrufe."</string> </resources> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 32bcf8792067..30705a1f6f62 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Παράκαμψη προεπιλογών διακωδικοποίησης"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Ενεργοποίηση διακωδικοποίησης"</string> <string name="transcode_default" msgid="3784803084573509491">"Να θεωρείται ότι οι εφαρμογές χρησιμοποιούν σύγχρονες μορφές"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Εμφάνιση ειδοποιήσεων διακωδικοποίησης"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Υπηρεσίες που εκτελούνται"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Προβολή και έλεγχος των εφαρμογών που εκτελούνται αυτή τη στιγμή"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Υλοποίηση WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Πλήρες σήμα δεδομένων."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Το Ethernet αποσυνδέθηκε."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Χωρίς κλήσεις."</string> </resources> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 62419538309b..b3d80abfc0a2 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Override transcoding defaults"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Enable transcoding"</string> <string name="transcode_default" msgid="3784803084573509491">"Assume apps support modern formats"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Show transcoding notifications"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 198fee4b58af..ecf97ad07c85 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Override transcoding defaults"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Enable transcoding"</string> <string name="transcode_default" msgid="3784803084573509491">"Assume apps support modern formats"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Show transcoding notifications"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 62419538309b..b3d80abfc0a2 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Override transcoding defaults"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Enable transcoding"</string> <string name="transcode_default" msgid="3784803084573509491">"Assume apps support modern formats"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Show transcoding notifications"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 62419538309b..b3d80abfc0a2 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Override transcoding defaults"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Enable transcoding"</string> <string name="transcode_default" msgid="3784803084573509491">"Assume apps support modern formats"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Show transcoding notifications"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 27cd8b34889e..c3a3f3f484fb 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Override transcoding defaults"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Enable transcoding"</string> <string name="transcode_default" msgid="3784803084573509491">"Assume apps support modern formats"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Show transcoding notifications"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Running services"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"View and control currently running services"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView implementation"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 0f765764dbfd..eb2a23b45e70 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Anular los valores predeterminados de transcodificación"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Habilitar la transcodificación"</string> <string name="transcode_default" msgid="3784803084573509491">"Suponer que las apps admiten formatos modernos"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Mostrar notificaciones de transcodificación"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"En ejecución"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver y controlar servicios actuales en ejecución"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementación de WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Señal de datos completa"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet desconectada"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Sin llamadas."</string> </resources> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index abd209ccc717..fe9984b701db 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Anular valores predeterminados de transcodificación"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Habilitar transcodificación"</string> <string name="transcode_default" msgid="3784803084573509491">"Considerar que las aplicaciones admiten formatos modernos"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Mostrar notificaciones de transcodificación"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Servicios en ejecución"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver y controlar los servicios en ejecución"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementación de WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Señal de datos al máximo"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Conexión Ethernet desconectada."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Sin llamadas."</string> </resources> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 2e9f3b8948a2..c70700ddc24c 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Alista transkodeerimise vaikeseaded"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Luba transkodeerimine"</string> <string name="transcode_default" msgid="3784803084573509491">"Oleta, et rakendused toetavad kaasaegseid vorminguid"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Kuva transkodeerimise märguanded"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Käitatud teenused"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Praegu käitatud teenuste vaatamine ja juhtimine"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView\' rakendamine"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Andmesignaal on tugev."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Etherneti-ühendus on katkestatud."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Helistamine pole võimalik."</string> </resources> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index ecb4f7573b66..09c964aa9dd1 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Ez erabili transkodetzearen balio lehenetsiak"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Gaitu transkodetzea"</string> <string name="transcode_default" msgid="3784803084573509491">"Arduratu aplikazioek formatu modernoak onartzeaz"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Erakutsi transkodetze-jakinarazpenak"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Abian diren zerbitzuak"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ikusi eta kontrolatu une honetan abian diren zerbitzuak"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView inplementazioa"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Datu-seinale osoa."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet bidezko konexioa eten da."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Deirik ez."</string> </resources> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 56ade54d9212..22cd6ae68de2 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ملغی کردن پیشفرضهای تراتبدیل"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"فعال کردن تراتبدیل"</string> <string name="transcode_default" msgid="3784803084573509491">"فرض شود برنامهها از قالبهای مدرن پشتیبانی میکنند"</string> + <string name="transcode_notification" msgid="5560515979793436168">"نمایش اعلانهای تراتبدیل"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"سرویسهای در حال اجرا"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"مشاهده و کنترل سرویسهای در حال اجرای فعلی"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"اجرای وبنما"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"قدرت سیگنال داده کامل است."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"اترنت قطع شد."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"اترنت."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"تماس گرفته نشود."</string> </resources> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index e26ecb705c9f..22dde690c3cf 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Ohita transkoodauksen oletukset"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Salli transkoodaus"</string> <string name="transcode_default" msgid="3784803084573509491">"Oleta, että sovellukset tukevat nykyaikaisia formaatteja"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Näytä transkoodausilmoituksia"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Käynnissä olevat palvelut"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Tarkastele ja hallitse käynnissä olevia palveluita."</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-käyttöönotto"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Vahva kuuluvuus."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet on irrotettu."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Ei puheluita."</string> </resources> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index d61ebc099934..d189f72aad65 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Remplacer les valeurs par défaut de transcodage"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Activer le transcodage"</string> <string name="transcode_default" msgid="3784803084573509491">"Présumer que les applications prennent en charge les formats modernes"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Afficher les notifications de transcodage"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Services en cours d\'exécution"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Afficher et contrôler les services en cours d\'exécution"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Mise en œuvre WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Signal excellent"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet déconnecté."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Aucun appel."</string> </resources> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 64c06fae175d..49d7faa63a3b 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Ignorer les paramètres de transcodage par défaut"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Activer le transcodage"</string> <string name="transcode_default" msgid="3784803084573509491">"Supposer que les applications sont compatibles avec les formats modernes"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Afficher les notifications de transcodage"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Services en cours d\'exécution"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Afficher et contrôler les services en cours d\'exécution"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Mise en œuvre WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Signal excellent"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet déconnecté"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Pas d\'appels."</string> </resources> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index cf63dd7176b2..0922c3dca3af 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Anular valores predeterminados de transcodificación"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Activar transcodificación"</string> <string name="transcode_default" msgid="3784803084573509491">"Considerar que as aplicacións admiten formatos modernos"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Mostrar notificacións de transcodificación"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Servizos en uso"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Comproba e controla os servizos actualmente en uso"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementación de WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Sinal de datos: completo"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Desconectouse a Ethernet."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Sen chamadas."</string> </resources> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 427ce56296fe..10c5cc87c6da 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ફૉર્મેટ બદલવાની પ્રક્રિયાના ડિફૉલ્ટ સેટિંગ ઓવરરાઇડ કરો"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ફૉર્મેટ બદલવાની પ્રક્રિયા ચાલુ કરો"</string> <string name="transcode_default" msgid="3784803084573509491">"ધારો કે ઍપ આધુનિક ફૉર્મેટ પર કામ કરે છે"</string> + <string name="transcode_notification" msgid="5560515979793436168">"ફૉર્મેટ બદલવાની પ્રક્રિયાના નોટિફિકેશન બતાવો"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ચાલુ સેવાઓ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"હાલમાં ચાલતી સેવાઓ જુઓ અને નિયંત્રિત કરો"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView અમલીકરણ"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"ડેટા સિગ્નલ પૂર્ણ."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ઇથરનેટ ડિસ્કનેક્ટ થયું."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ઇથરનેટ."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"કોઈ કૉલિંગ નહીં."</string> </resources> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 159d2dbe7439..ea25406b1541 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ट्रांसकोडिंग की डिफ़ॉल्ट सेटिंग बदलें"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ट्रांसकोडिंग चालू करें"</string> <string name="transcode_default" msgid="3784803084573509491">"मानकर चलें कि ऐप्लिकेशन, नए फ़ॉर्मैट के साथ काम करेंगे"</string> + <string name="transcode_notification" msgid="5560515979793436168">"ट्रांसकोडिंग की सूचनाएं दिखाएं"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"चल रही सेवाएं"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"इस समय चल रही सेवाओं को देखें और नियंत्रित करें"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"वेबव्यू लागू करें"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"डेटा सिग्नल पूरा."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ईथरनेट डिस्कनेक्ट किया गया."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ईथरनेट."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"वॉइस कॉल की सुविधा उपलब्ध नहीं है."</string> </resources> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 5e33ef3d0689..27a7a6e7891a 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Nadjačaj zadane postavke konvertiranja"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Omogući konvertiranje"</string> <string name="transcode_default" msgid="3784803084573509491">"Pretpostavi da aplikacije podržavaju moderne formate"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Prikaži obavijesti o konvertiranju"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Pokrenute usluge"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Pregledajte i kontrolirajte pokrenute usluge"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementacija WebViewa"</string> @@ -593,6 +594,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Podatkovni signal pun."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Prekinuta je veza s ethernetom."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Bez poziva."</string> </resources> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 28975be7b6fb..70a48b638506 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Az átkódolás alapértelmezett beállításainak felülbírálása"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Átkódolás engedélyezése"</string> <string name="transcode_default" msgid="3784803084573509491">"Annak feltételezése, hogy az alkalmazások támogatják a modern formátumokat"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Átkódolási értesítések megjelenítése"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Futó szolgáltatások"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"A jelenleg futó szolgáltatások megtekintése és vezérlése"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-megvalósítás"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Adatjel teljes."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet leválasztva."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Nem kezdeményezhet hanghívást."</string> </resources> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index a2075265b58c..c39e966d880a 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Չեղարկել վերակոդավորման կանխադրված կարգավորումները"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Միացնել վերակոդավորումը"</string> <string name="transcode_default" msgid="3784803084573509491">"Ենթադրել, որ հավելվածներն աջակցում են ժամանակակից ձևաչափեր"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Ցուցադրել անդրկոդավորման ծանուցումներ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Աշխատող ծառայություններ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Դիտել և վերահսկել ընթացիկ աշխատող ծառայությունները"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ծառայություն"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Տվյալների ազդանշանը լրիվ է:"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet-ը անջատված է:"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet։"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Զանգել հնարավոր չէ։"</string> </resources> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index c4d752d25cf3..037c1b3b3e76 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Ganti default transcoding"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Aktifkan transcoding"</string> <string name="transcode_default" msgid="3784803084573509491">"Asumsikan aplikasi mendukung format modern"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Tampilkan notifikasi transcoding"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Layanan yang sedang berjalan"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Melihat dan mengontrol layanan yang sedang berjalan"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Penerapan WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Sinyal data penuh."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet terputus."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Tidak ada panggilan."</string> </resources> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 56b0a81b7c81..3f8783053ba3 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Hnekkja sjálfgefinni umkóðun"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Kveikja á umkóðun"</string> <string name="transcode_default" msgid="3784803084573509491">"Gera ráð fyrir að forrit styðji nútímasnið"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Sýna umkóðunartilkynningar"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Þjónustur í gangi"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Skoða og stjórna þjónustum í gangi"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Innleiðing WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Fullur sendistyrkur gagnatengingar."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet aftengt."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Engin símtöl."</string> </resources> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index f045d273ce0d..80ab57b66548 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Sostituisci impostazioni predefinite transcodifica"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Attiva transcodifica"</string> <string name="transcode_default" msgid="3784803084573509491">"Presupponi che le app supportino i formati moderni"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Mostra notifiche relative alla transcodifica"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Servizi in esecuzione"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualizza e controlla i servizi attualmente in esecuzione"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementazione di WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Massimo segnale dati."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Connessione Ethernet annullata."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Chiamate non disponibili."</string> </resources> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index ba3a46786663..4355f31e3ed1 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ביטול ברירות המחדל של המרת קידוד"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"הפעלת המרת קידוד"</string> <string name="transcode_default" msgid="3784803084573509491">"הנחת העבודה היא שאפליקציות תומכות בפורמטים מודרניים"</string> + <string name="transcode_notification" msgid="5560515979793436168">"הצגת התראות לגבי המרת קידוד"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"שירותים פועלים"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"הצגת השירותים הפועלים כעת ושליטה בהם"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"יישום WebView"</string> @@ -594,6 +595,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"אות הנתונים מלא."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"אתרנט מנותק."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"אתרנט."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"אין שיחות."</string> </resources> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 6f398b6840b0..ea4a683dd283 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"デフォルトのコード変換をオーバーライド"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"コード変換を有効にする"</string> <string name="transcode_default" msgid="3784803084573509491">"アプリによる最新形式のサポートを想定"</string> + <string name="transcode_notification" msgid="5560515979793436168">"コード変換に関する通知の表示"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"実行中のサービス"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"現在実行中のサービスを表示して制御する"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView の実装"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"データ信号:フル"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"イーサネット接続を解除しました。"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"イーサネット。"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"通話なし。"</string> </resources> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 5bf1dd03f558..5798a46e7c99 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ტრანსკოდირების ნაგულისხმევების უგულებელყოფა"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ტრანსკოდირების ჩართვა"</string> <string name="transcode_default" msgid="3784803084573509491">"დაშვება, რომ აპებს აქვთ თანამედროვე ფორმატების მხარდაჭერა"</string> + <string name="transcode_notification" msgid="5560515979793436168">"ტრანსკოდირების შეტყობინებების ჩვენება"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"მიმდინარე სერვისები"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ამჟამად მოქმედი სერვისების ნახვა და მართვა"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView რეალიზაცია"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"მონაცემთა გადაცემის საიმედო სიგნალი."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet კავშირი შეწყვეტილია."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"ზარების გარეშე."</string> </resources> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index c7c38378db6e..5ce4e3a8f1c8 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Қайта қодтаудың әдепкі параметрлерін қайта анықтау"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Қайта кодтауды қосу"</string> <string name="transcode_default" msgid="3784803084573509491">"Қолданбалар қазіргі заманғы форматтарды қолдайды делік"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Қайта кодтау хабарландыруларын көрсету"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Қосылып тұрған қызметтер"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Қазір істеп тұрған қызметтерді көру және басқару"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView қызметі"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Дерекқор сигналы толы."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet ажыратылған."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Қоңырау шалу мүмкін емес."</string> </resources> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 99a73e14daee..e6f2cb03439e 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"លុបពីលើលំនាំដើមនៃការបំប្លែងកូដ"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"បើកការបំប្លែងកូដ"</string> <string name="transcode_default" msgid="3784803084573509491">"សន្មតថាកម្មវិធីអាចប្រើទម្រង់ទំនើបបាន"</string> + <string name="transcode_notification" msgid="5560515979793436168">"បង្ហាញការជូនដំណឹងអំពីការបំប្លែងកូដ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"សេវាកម្មកំពុងដំណើរការ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"មើល និងគ្រប់គ្រងសេវាកម្មកំពុងដំណើរការបច្ចុប្បន្ន"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ការអនុវត្ត WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"សញ្ញាទិន្នន័យពេញ។"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"បានផ្តាច់អ៊ីសឺរណិត។"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"អ៊ីសឺរណិត។"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"គ្មានការហៅទូរសព្ទទេ។"</string> </resources> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 54a42a8b0090..561e6bed887d 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -404,6 +404,8 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ಟ್ರಾನ್ಸ್ಕೋಡಿಂಗ್ ಡೀಫಾಲ್ಟ್ಗಳನ್ನು ಅತಿಕ್ರಮಿಸಿ"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ಟ್ರಾನ್ಸ್ಕೋಡಿಂಗ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string> <string name="transcode_default" msgid="3784803084573509491">"ಆ್ಯಪ್ಗಳು ಆಧುನಿಕ ಫಾರ್ಮ್ಯಾಟ್ಗಳನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ ಎಂದು ಊಹಿಸಿ"</string> + <!-- no translation found for transcode_notification (5560515979793436168) --> + <skip /> <string name="runningservices_settings_title" msgid="6460099290493086515">"ರನ್ ಆಗುತ್ತಿರುವ ಸೇವೆಗಳು"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ಈಗ ರನ್ ಆಗುತ್ತಿರುವ ಸೇವೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿಯಂತ್ರಿಸಿ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ಹೊಂದಿಸಿ"</string> @@ -592,6 +594,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"ಡೇಟಾ ಸಂಕೇತ ತುಂಬಿದೆ."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ಇಥರ್ನೆಟ್ ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ಇಥರ್ನೆಟ್."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"ಕರೆ ಮಾಡಲಾಗುವುದಿಲ್ಲ."</string> </resources> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 9e9fea9e57ad..cd3b8b842c66 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"트랜스코딩 기본값 재정의"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"트랜스코딩 사용"</string> <string name="transcode_default" msgid="3784803084573509491">"앱이 최신 형식을 지원하는 것으로 가정"</string> + <string name="transcode_notification" msgid="5560515979793436168">"트랜스코딩 알림 표시"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"실행 중인 서비스"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"현재 실행 중인 서비스 보기 및 제어"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 구현"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"데이터 신호가 강합니다."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"이더넷에서 연결 해제되었습니다."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"이더넷에 연결되었습니다."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"통화 모드가 없습니다."</string> </resources> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 2aec5cb42199..1990353f80b8 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Демейки жүргүзүлгөн транскоддоону өзгөртүп коюу"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Транскоддоо жүргүзүүнү иштетүү"</string> <string name="transcode_default" msgid="3784803084573509491">"Колдонмолордо заманбап форматтар колдоого алынат"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Транскоддоо билдирмелерин көрсөтүү"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Иштеп жаткан кызматтар"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Учурда иштеп жаткан кызматтарды көрүп, көзөмөлдөп турасыз"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView кызматы"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Мобилдик интернеттин сигналы толук."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet ажырады."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Чалуу жок."</string> </resources> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index e199e1f7db83..4997ea1a82c6 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ຍົກເລີກຄ່າເລີ່ມຕົ້ນການປ່ຽນຮູບແບບລະຫັດ"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ເປີດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດ"</string> <string name="transcode_default" msgid="3784803084573509491">"ສົມມຸດວ່າແອັບຮອງຮັບຮູບແບບສະໄໝໃໝ່"</string> + <string name="transcode_notification" msgid="5560515979793436168">"ສະແດງການແຈ້ງເຕືອນການປ່ຽນຮູບແບບລະຫັດ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ບໍລິການທີ່ເຮັດວຽກຢູ່"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ເບິ່ງ ແລະຈັດການບໍລິການທີ່ກຳລັງເຮັດວຽກຢູ່ໃນປັດຈຸບັນ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ການຈັດຕັ້ງປະຕິບັດ WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"ສັນຍານຂໍ້ມູນເຕັມ."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ອີເທີເນັດຕັດເຊື່ອມຕໍ່ແລ້ວ."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ອີເທີເນັດ."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"ບໍ່ສາມາດໂທສຽງໄດ້."</string> </resources> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index a25c59f5eb26..ec3cb0d94a51 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Perkodavimo numatytųjų nustatymų nepaisymas"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Perkodavimo įgalinimas"</string> <string name="transcode_default" msgid="3784803084573509491">"Manoma, kad programos palaiko modernius formatus"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Rodyti perkodavimo pranešimus"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Vykdomos paslaugos"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Žiūrėti ir valdyti dabar vykdomas paslaugas"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"„WebView“ diegimas"</string> @@ -594,6 +595,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Stiprus duomenų signalas."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Atsijungta nuo eterneto."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Eternetas."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Nekviečiama."</string> </resources> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index f360c906d95a..ff9f0a1649f9 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Ignorēt pārkodēšanas noklusējuma iestatījumus"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Iespējot pārkodēšanu"</string> <string name="transcode_default" msgid="3784803084573509491">"Pieņemt, ka lietotnēs tiek atbalstīti moderni formāti"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Rādīt paziņojumus par pārkodēšanu"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktīvie pakalpojumi"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Pašreiz darbojošos pakalpojumu skatīšana un vadība"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ieviešana"</string> @@ -593,6 +594,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Pilna piekļuve datu signālam."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Pārtraukts savienojums ar tīklu Ethernet."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Tīkls Ethernet"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Zvanīšana nav pieejama."</string> </resources> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index a32d00b00928..507f65c641b2 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Отфрли стандардни вредности за транскодирање"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Овозможи транскодирање"</string> <string name="transcode_default" msgid="3784803084573509491">"Претпостави дека апликациите поддржуваат модерни формати"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Прикажувај известувања за транскодирање"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Активни услуги"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Погледнете и контролирајте услуги што се моментално активни"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Воведување WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Сигналот за податоци е исполнет."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Етернетот е исклучен."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Етернет."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Без повици."</string> </resources> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 902507df89c8..8a09801502e1 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ട്രാൻസ്കോഡ് ചെയ്യൽ ഡിഫോൾട്ടുകൾ അസാധുവാക്കുക"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ട്രാൻസ്കോഡ് ചെയ്യൽ പ്രവർത്തനക്ഷമമാക്കുക"</string> <string name="transcode_default" msgid="3784803084573509491">"ആപ്പുകൾ ആധുനിക ഫോർമാറ്റുകളെ പിന്തുണയ്ക്കുമെന്ന് കരുതുക"</string> + <string name="transcode_notification" msgid="5560515979793436168">"ട്രാൻസ്കോഡ് ചെയ്യൽ അറിയിപ്പുകൾ കാണിക്കുക"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"നിലവിൽ പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ കാണുക, നിയന്ത്രിക്കുക"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView നടപ്പാക്കൽ"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"ഡാറ്റ സിഗ്നൽ പൂർണ്ണമാണ്."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ഇതർനെറ്റ് വിച്ഛേദിച്ചു."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ഇതർനെറ്റ്."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"വോയ്സ് കോൾ ലഭ്യമല്ല."</string> </resources> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 83009b7c03a1..27ca8f59739d 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Хөрвүүлгийн өгөгдмөлийг дарах"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Хөрвүүлгийг идэвхжүүлэх"</string> <string name="transcode_default" msgid="3784803084573509491">"Аппыг орчин үеийн форматыг дэмждэг гэж үздэг"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Хөрвүүлгийн мэдэгдэл харуулах"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Ажиллаж байгаа үйлчилгээнүүд"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Одоо ажиллаж байгаа үйлчилгээнүүдийг харах болон хянах"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView хэрэгжилт"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Дата дохио дүүрэн."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet саллаа."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Этернэт."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Дуудлага байхгүй."</string> </resources> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 359f52565934..b6c546072473 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -404,6 +404,8 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ट्रान्सकोडिंग डीफॉल्ट ओव्हरराइड करा"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ट्रान्सकोडिंग सुरू करा"</string> <string name="transcode_default" msgid="3784803084573509491">"असे गृहीत धरा की, ॲप्स आधुनिक फॉरमॅटना सपोर्ट करतात"</string> + <!-- no translation found for transcode_notification (5560515979793436168) --> + <skip /> <string name="runningservices_settings_title" msgid="6460099290493086515">"सुरू सेवा"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"सध्या सुरू असलेल्या सेवा पहा आणि नियंत्रित करा"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"वेबदृश्य अंमलबजावणी"</string> @@ -592,6 +594,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"डेटा सिग्नल पूर्ण."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"इथरनेट डिस्कनेक्ट केले."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"इथरनेट."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"कॉलिंग उपलब्ध नाही."</string> </resources> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index eba89daf02ed..53a4398d1b90 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Batalkan transpengekodan lalai"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Dayakan transpengekodan"</string> <string name="transcode_default" msgid="3784803084573509491">"Mengambil alih sokongan apl format moden"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Tunjukkan pemberitahuan transpengekodan"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Perkhidmatan dijalankan"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Lihat dan kawal perkhidmatan yang sedang dijalankan"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Pelaksanaan WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Isyarat data penuh."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet diputuskan sambungan."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Tiada panggilan."</string> </resources> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index e982aa93d28e..0fef5abf782b 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -307,7 +307,7 @@ <string name="adb_keys_warning_message" msgid="2968555274488101220">"သင် ယခင်က ခွင့်ပြုခဲ့သော ကွန်ပျူတာအားလုံးမှ ယူအက်စ်ဘီ အမှားစစ်ခွင့်ကို ရုတ်သိမ်းမည်လား ?"</string> <string name="dev_settings_warning_title" msgid="8251234890169074553">"တည်ဆောက်ပြုပြင်ရန်ဆက်တင်များကို အသုံးပြုခွင့်ပေးမည်လား?"</string> <string name="dev_settings_warning_message" msgid="37741686486073668">"ဤဆက်တင်းများကို တည်ဆောက်ပြုပြင်ရာတွင် သုံးရန်အတွက်သာ ရည်ရွယ်သည်။ ၎င်းတို့သည် သင်၏စက်နှင့် အပလီကေးရှင်းများကို ရပ်စေခြင်း သို့ လုပ်ဆောင်ချက်မမှန်ကန်ခြင်းများ ဖြစ်ပေါ်စေနိုင်သည်။"</string> - <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB ဖြင့် အက်ပ်များကို အတည်ပြုစိစစ်ရန်"</string> + <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB ဖြင့် အက်ပ်များစိစစ်ရန်"</string> <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT မှတစ်ဆင့် ထည့်သွင်းသော အက်ပ်များ အန္တရာယ်ဖြစ်နိုင်ခြင်း ရှိမရှိ စစ်ဆေးသည်။"</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"အမည်မရှိသော (MAC လိပ်စာများသာပါသော) ဘလူးတုသ်စက်ပစ္စည်းများကို ပြသပါမည်"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"ချိတ်ဆက်ထားသည့် ကိရိယာတွင် လက်မခံနိုင်လောက်အောင် ဆူညံ သို့မဟုတ် ထိန်းညှိမရနိုင်သော အသံပိုင်းပြဿနာ ရှိခဲ့လျှင် ဘလူးတုသ် ပကတိ အသံနှုန်းကို ပိတ်ပါ။"</string> @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"အမျိုးအစားပြောင်းခြင်း၏ မူရင်းဆက်တင်များကို အစားထိုးရန်"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"အမျိုးအစားပြောင်းခြင်းကို ဖွင့်ရန်"</string> <string name="transcode_default" msgid="3784803084573509491">"ဤအက်ပ်များက ဖော်မက်အသစ်များကို ပံ့ပိုးသည်"</string> + <string name="transcode_notification" msgid="5560515979793436168">"အမျိုးအစားပြောင်းခြင်း အကြောင်းကြားချက်များကို ပြရန်"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"အလုပ်လုပ်နေသောဝန်ဆောင်မှုများ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"လက်ရှိ ဝန်ဆောင်မှုများကို ကြည့်ရှု ထိန်းသိမ်းသည်"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView အကောင်အထည်ဖော်မှု"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"ဒေတာထုတ်လွှင့်မှုအပြည့်ဖမ်းမိခြင်း"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet နှင့်ချိတ်ဆက်မှုပြတ်တောက်"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"အီသာနက်။"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"ခေါ်ဆိုမှု မရှိပါ။"</string> </resources> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index abbb5e7c64ee..0ca8d51645e8 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Overstyr omkodingsstandarder"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Slå på omkoding"</string> <string name="transcode_default" msgid="3784803084573509491">"Anta at apper støtter moderne formater"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Vis omkodingsvarsler"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktive tjenester"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Se og kontrollér tjenester som kjører"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Datasignal er fullt."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet er frakoblet."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Ingen ringing."</string> </resources> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index d7a32bf3a038..75b4537f6a8f 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -404,6 +404,8 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ट्रान्सकोडिङसम्बन्धी पूर्वनिर्धारित सेटिङ परिवर्तन गर्नुहोस्"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ट्रान्सकोडिङ अन गर्नुहोस्"</string> <string name="transcode_default" msgid="3784803084573509491">"एपहरूमा आधुनिक फर्म्याट प्रयोग गर्न मिल्छ भनी मान्नुहोस्"</string> + <!-- no translation found for transcode_notification (5560515979793436168) --> + <skip /> <string name="runningservices_settings_title" msgid="6460099290493086515">"चलिरहेका सेवाहरू"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"हाल चालु भइरहेका सेवाहरू हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView कार्यान्वयन"</string> @@ -592,6 +594,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"डेटा संकेत पूर्ण।"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"इथरनेट विच्छेद भयो।"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"इथरनेट।"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"कल गर्ने सुविधा उपलब्ध छैन।"</string> </resources> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 7e739bca9b94..71d6acc6a22b 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Standaardwaarden voor transcodering overschrijven"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Transcodering inschakelen"</string> <string name="transcode_default" msgid="3784803084573509491">"Aannemen dat apps moderne indelingen ondersteunen"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Transcoderingsmeldingen laten zien"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Actieve services"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Services die momenteel actief zijn, weergeven en beheren"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementatie"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Gegevenssignaal is op volle sterkte."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernetverbinding verbroken."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Geen gesprekken."</string> </resources> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 1e842b7f7ce0..1d6e34f7af23 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ଟ୍ରାନ୍ସକୋଡିଂ ଡିଫଲ୍ଟଗୁଡ଼ିକୁ ଓଭରରାଇଡ୍ କରନ୍ତୁ"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ଟ୍ରାନ୍ସକୋଡିଂକୁ ସକ୍ଷମ କରନ୍ତୁ"</string> <string name="transcode_default" msgid="3784803084573509491">"ଧରିନିଅନ୍ତୁ ଆପଗୁଡ଼ିକ ଆଧୁନିକ ଫର୍ମାଟଗୁଡ଼ିକୁ ସମର୍ଥନ କରେ"</string> + <string name="transcode_notification" msgid="5560515979793436168">"ଟ୍ରାନ୍ସକୋଡିଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ଏବେ ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ ଓ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ୱେବ୍ଭ୍ୟୁ ପ୍ରୟୋଗ"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"ଡାଟା ସିଗ୍ନାଲ୍ ପୂର୍ଣ୍ଣ ଅଛି।"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ଇଥରନେଟ୍ ବିଚ୍ଛିନ୍ନ ହୋଇଛି।"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ଇଥରନେଟ୍।"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"କୌଣସି କଲିଂ ନାହିଁ।"</string> </resources> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index d035fc0b5c9c..9d91aae0a982 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -404,6 +404,8 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਦੀਆਂ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੈਟਿੰਗਾਂ ਨੂੰ ਓਵਰਰਾਈਡ ਕਰੋ"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਚਾਲੂ ਕਰੋ"</string> <string name="transcode_default" msgid="3784803084573509491">"ਮੰਨ ਲਓ ਕਿ ਐਪਾਂ ਆਧੁਨਿਕ ਫਾਰਮੈਟਾਂ ਦਾ ਸਮਰਥਨ ਕਰਦੀਆਂ ਹਨ"</string> + <!-- no translation found for transcode_notification (5560515979793436168) --> + <skip /> <string name="runningservices_settings_title" msgid="6460099290493086515">"ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ਇਸ ਵੇਲੇ ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ ਦੇਖੋ ਅਤੇ ਇਹਨਾਂ ਨੂੰ ਕੰਟਰੋਲ ਕਰੋ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ਅਮਲ"</string> @@ -592,6 +594,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">" ਡਾਟਾ ਸਿਗਨਲ ਪੂਰਾ।"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ਈਥਰਨੈੱਟ ਡਿਸਕਨੈਕਟ ਹੋ ਗਿਆ।"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ਈਥਰਨੈੱਟ।"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"ਕਾਲਿੰਗ ਸੇਵਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string> </resources> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 8a732cb7710b..f48b05f01ddc 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Zastąp ustawienia domyślne transkodowania"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Włącz transkodowanie"</string> <string name="transcode_default" msgid="3784803084573509491">"Zakładaj, że aplikacje obsługują nowoczesne formaty"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Pokaż powiadomienia transkodowania"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Uruchomione usługi"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Wyświetl obecnie uruchomione usługi i nimi zarządzaj"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementacja WebView"</string> @@ -594,6 +595,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Dane: pełna moc sygnału."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Rozłączono z siecią Ethernet."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Brak połączenia."</string> </resources> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index a63b9b5660da..b9c919129623 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Substituir os padrões de transcodificação"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Ativar transcodificação"</string> <string name="transcode_default" msgid="3784803084573509491">"Considerar que os apps são compatíveis com formatos modernos"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Mostrar notificações de transcodificação"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Serviços em execução"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualizar e controlar os serviços em execução no momento"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementação do WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Sinal de dados cheio."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet desconectada."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Sem chamadas."</string> </resources> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index ab23059e013c..57e38c078375 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Substituir as predefinições da transcodificação"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Ativar a transcodificação"</string> <string name="transcode_default" msgid="3784803084573509491">"Assumir que as apps suportam formatos modernos"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Mostrar notificações de transcodificação"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Serviços em execução"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ver e controlar os serviços actualmente em execução"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementação WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Sinal de dados completo."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet desligada."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Sem chamadas."</string> </resources> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index a63b9b5660da..b9c919129623 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Substituir os padrões de transcodificação"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Ativar transcodificação"</string> <string name="transcode_default" msgid="3784803084573509491">"Considerar que os apps são compatíveis com formatos modernos"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Mostrar notificações de transcodificação"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Serviços em execução"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visualizar e controlar os serviços em execução no momento"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementação do WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Sinal de dados cheio."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet desconectada."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Sem chamadas."</string> </resources> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 98edb32e2865..c2da73c5679c 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Modificați setările prestabilite de transcodare"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Activați transcodarea"</string> <string name="transcode_default" msgid="3784803084573509491">"Presupuneți că aplicațiile acceptă formatele moderne"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Vedeți notificările privind transcodarea"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Servicii în curs de funcționare"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Vedeți și controlați serviciile care funcționează în prezent"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementare WebView"</string> @@ -593,6 +594,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Semnal pentru date: complet."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet deconectat."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Apelarea nu este disponibilă."</string> </resources> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 972dc205fea5..efcce7556910 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Переопределять настройки транскодирования по умолчанию"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Включить перекодирование"</string> <string name="transcode_default" msgid="3784803084573509491">"Считать, что приложения поддерживают современные форматы кодирования"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Показывать уведомления о перекодировании"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Работающие службы"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Просмотр и управление работающими службами"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Сервис WebView"</string> @@ -594,6 +595,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Надежный сигнал передачи данных."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Устройство отключено от Ethernet."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Совершение вызовов невозможно."</string> </resources> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 0dee8e1c8af9..590e0e4eded1 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ට්රාන්ස්කෝඩින් පෙරනිමි ප්රතික්ෂේප කරන්න"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ට්රාන්ස්කෝඩින් සබල කරන්න"</string> <string name="transcode_default" msgid="3784803084573509491">"යෙදුම් නවීන ආකෘති සඳහා සහාය දක්වයි යැයි උපකල්පනය කරමු"</string> + <string name="transcode_notification" msgid="5560515979793436168">"ට්රාන්ස්කෝඩින් දැනුම්දීම් පෙන්වන්න"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ධාවනය වන සේවා"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"දැනට ධාවනය වන සේවා බලන්න සහ පාලනය කරන්න"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ක්රියාත්මක කිරීම"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"දත්ත සංඥාව පිරී ඇත."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ඊතර්නෙට් විසන්ධි කරන ලදී."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ඊතර්නෙට්."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"ඇමතුම් නැත."</string> </resources> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 7d49ca3b91cd..c817ed0e200c 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Prepísať predvolené nastavenia prekódovania"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Povoliť prekódovanie"</string> <string name="transcode_default" msgid="3784803084573509491">"Prepdokladať, že aplikácie podporujú moderné formáty"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Zobraziť upozornenia prekódovania"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Spustené služby"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Zobrazovať a riadiť aktuálne spustené služby"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Implementácia WebView"</string> @@ -594,6 +595,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Plný signál dátovej siete."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Sieť ethernet je odpojená"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Žiadne volanie."</string> </resources> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 48e5dcc2284b..464b235e90da 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Preglasi privzete nastavitve prekodiranja"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Omogoči prekodiranje"</string> <string name="transcode_default" msgid="3784803084573509491">"Aplikacije naj bi podpirale sodobne oblike zapisov"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Prikaz obvestil o prekodiranju"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Zagnane storitve"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Preglejte in nadzorujte storitve, ki so trenutno zagnane"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Izvedba spletnega pogleda"</string> @@ -594,6 +595,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Podatkovni signal poln."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernetna povezava je prekinjena."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Klicanje ni mogoče."</string> </resources> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 0bc905e688a1..a2a63e165a96 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Anulo parazgjedhjet e transkodimit"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Aktivizo transkodimin"</string> <string name="transcode_default" msgid="3784803084573509491">"Supozo se aplikacionet i mbështetin formatet moderne"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Shfaq njoftimet e transkodimit"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Shërbimet në ekzekutim"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Shiko dhe kontrollo shërbimet që po ekzekutohen aktualisht"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Zbatimi i WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Sinjali i të dhënave është i plotë."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Lidhja e eternetit u shkëput."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Eternet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Telefonatat nuk ofrohen"</string> </resources> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 6d19af89f47b..2119303410e0 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Замени подразумевана подешавања транскодирања"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Омогући транскодирање"</string> <string name="transcode_default" msgid="3784803084573509491">"Подразумевај да апликације подржавају модерне формате"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Приказуј обавештења о транскодирању"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Покренуте услуге"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Приказ и контрола тренутно покренутих услуга"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Примена WebView-а"</string> @@ -593,6 +594,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Сигнал за податке је најјачи."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Веза са етернетом је прекинута."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Етернет."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Без позивања."</string> </resources> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 944c423b35ac..c7a21692fea6 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Åsidosätta standardinställningar för omkodning"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Aktivera omkodning"</string> <string name="transcode_default" msgid="3784803084573509491">"Anta att appar har stöd för moderna format"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Visa aviseringar för omkodning"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktiva tjänster"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Visa och styr aktiva tjänster"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-implementering"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Datasignalen är full."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet har kopplats från."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Inga anrop."</string> </resources> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 442f54be37bb..60014f437c4b 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Batilisha chaguomsingi za kubadilisha miundo ya faili"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Ruhusu ubadilishaji wa miundo ya faili"</string> <string name="transcode_default" msgid="3784803084573509491">"Chukulia kuwa programu zinatumia miundo ya kisasa"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Onyesha arifa za kubadilisha muundo wa faili"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Huduma zinazoendeshwa"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Onyesha na udhibiti huduma zinazoendeshwa kwa sasa"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Utekelezaji wa WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Ishara ya data imejaa."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethaneti imeondolewa."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethaneti."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Huwezi kupiga wala kupokea simu."</string> </resources> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 561a122430e7..82cce1275b10 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"இயல்புநிலை குறிமாற்றங்களை மீறிச் செயல்படு"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"குறிமாற்றத்தை இயக்கு"</string> <string name="transcode_default" msgid="3784803084573509491">"ஆப்ஸ் மாடர்ன் வடிவங்களை ஆதரிக்கும்படி அமை"</string> + <string name="transcode_notification" msgid="5560515979793436168">"குறிமாற்ற அறிவிப்புகளைக் காட்டு"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"இயங்கும் சேவைகள்"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"தற்போது இயக்கத்தில் இருக்கும் சேவைகளைப் பார்த்து கட்டுப்படுத்து"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView செயல்படுத்தல்"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"தரவு சிக்னல் முழுமையாக உள்ளது."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ஈத்தர்நெட் துண்டிக்கப்பட்டது."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ஈதர்நெட்."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"அழைப்பை மேற்கொள்ள முடியவில்லை."</string> </resources> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 18d7f281db62..3b04eb402585 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ట్రాన్స్కోడింగ్ ఆటోమేటిక్ సెట్టింగ్లను ఓవర్రైడ్ చేయండి"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ట్రాన్స్కోడింగ్ను ఎనేబుల్ చేయండి"</string> <string name="transcode_default" msgid="3784803084573509491">"యాప్లు ఆధునిక ఫార్మాట్లకు సపోర్ట్ చేస్తాయని అనుకోండి"</string> + <string name="transcode_notification" msgid="5560515979793436168">"ట్రాన్స్కోడింగ్ నోటిఫికేషన్లను చూపండి"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"అమలులో ఉన్న సేవలు"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ప్రస్తుతం అమలులో ఉన్న సేవలను వీక్షించండి మరియు నియంత్రించండి"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"వెబ్ వీక్షణ అమలు"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"డేటా సిగ్నల్ సంపూర్ణంగా ఉంది."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ఈథర్నెట్ డిస్కనెక్ట్ చేయబడింది."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ఈథర్నెట్."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"కాలింగ్ మోడ్ ఆఫ్లో ఉంది."</string> </resources> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index ceab26cabd9a..a9703213813e 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ลบล้างค่าเริ่มต้นของการแปลง"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"เปิดใช้การแปลง"</string> <string name="transcode_default" msgid="3784803084573509491">"ถือว่าแอปรองรับรูปแบบสมัยใหม่"</string> + <string name="transcode_notification" msgid="5560515979793436168">"แสดงการแจ้งเตือนการแปลง"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"บริการที่ทำงานอยู่"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ดูและควบคุมบริการที่ทำงานอยู่"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"การใช้งาน WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"สัญญาณข้อมูลเต็ม"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ยกเลิกการเชื่อมต่ออีเทอร์เน็ตแล้ว"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"อีเทอร์เน็ต"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"ไม่มีการโทร"</string> </resources> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 671fa149219f..bedd00511118 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"I-override ang mga default ng pagta-transcode"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"I-enable ang pagta-transcode"</string> <string name="transcode_default" msgid="3784803084573509491">"Ipagpalagay na sinusuportahan ng mga app ang mga modernong format"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Ipakita ang mga notification sa pag-transcode"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Mga tumatakbong serbisyo"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Tingnan at kontrolin ang mga kasalukuyang tumatakbong serbisyo"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Pagpapatupad sa WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Puno ang signal ng data."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Nadiskonekta ang Ethernet."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Hindi makakatawag."</string> </resources> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 98d89f3e43d5..2b2b44e0f9dc 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Kod dönüştürme varsayılanlarını geçersiz kıl"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Kod dönüştürmeyi etkinleştir"</string> <string name="transcode_default" msgid="3784803084573509491">"Uygulamaların modern biçimleri desteklediğini varsay"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Kod dönüştürme bildirimlerini göster"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Çalışan hizmetler"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Şu anda çalışan hizmetleri görüntüle ve denetle"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Web Görünümü kullanımı"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Veri sinyali tam."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet bağlantısı kesildi."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Çağrı yok."</string> </resources> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 4e739acb53a7..9c5e2aa858aa 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Замінити стандартні налаштування перекодування"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Увімкнути перекодування"</string> <string name="transcode_default" msgid="3784803084573509491">"Вважати, що додатки підтримують сучасні формати"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Показувати сповіщення про перекодування"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Запущені сервіси"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Переглянути й налаштувати запущені сервіси"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Застосування WebView"</string> @@ -594,6 +595,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Максимальний сигнал даних."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Ethernet відключено."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Виклики недоступні."</string> </resources> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index c7dee9513759..ee06623240de 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"ٹرانسکوڈنگ ڈیفالٹس کو اوور رائیڈ کریں"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"ٹرانسکوڈنگ فعال کریں"</string> <string name="transcode_default" msgid="3784803084573509491">"فرض کریں کہ ایپس جدید فارمیٹس کو سپورٹ کرتی ہیں"</string> + <string name="transcode_notification" msgid="5560515979793436168">"ٹرانسکوڈنگ اطلاعات دکھائیں"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"چل رہی سروسز"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"فی الحال چل رہی سروسز دیکھیں اور انہیں کنٹرول کریں"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView کا نفاذ"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"ڈیٹا سگنل بھرا ہوا ہے۔"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ایتھرنیٹ منقطع ہے۔"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ایتھرنیٹ۔"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"کوئی کالنگ نہیں ہے۔"</string> </resources> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 433096b64f08..29c0f91c4e60 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Transkripsiya parametrlarini almashtirish"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Transkripsiyasini yoqish"</string> <string name="transcode_default" msgid="3784803084573509491">"Ilovalarda zamonaviy kodlash formatlari ishlaydi deb hisoblash"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Transkripsiya bildirishnomalarini chiqarish"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Ishlab turgan ilovalar"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Ishlab turgan ilovalarni ko‘rish va boshqarish"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ta’minotchisi"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Internet signali butun."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Qurilma Ethernet tarmog‘idan uzildi."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Chaqiruv imkonsiz."</string> </resources> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index a14462bf7efe..39beecd0a87a 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Ghi đè tùy chọn chuyển mã mặc định"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Bật tính năng chuyển mã"</string> <string name="transcode_default" msgid="3784803084573509491">"Giả định rằng các ứng dụng hỗ trợ định dạng hiện đại"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Hiển thị thông báo chuyển mã"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Các dịch vụ đang chạy"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Xem và kiểm soát các dịch vụ đang chạy"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Triển khai WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Tín hiệu dữ liệu đầy đủ."</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"Đã ngắt kết nối Ethernet."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Không thể gọi điện."</string> </resources> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index f6bea916c3fa..010a4dc9ea6c 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"覆盖转码默认设置"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"启用转码"</string> <string name="transcode_default" msgid="3784803084573509491">"假设应用支持现代格式"</string> + <string name="transcode_notification" msgid="5560515979793436168">"显示转码通知"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"正在运行的服务"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"查看和控制当前正在运行的服务"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 实现"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"数据信号满格。"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"以太网已断开连接。"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"以太网。"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"不启用通话。"</string> </resources> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 3cf15a9f3995..1e06a91859e2 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"覆寫轉碼預設設定"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"啟用轉碼功能"</string> <string name="transcode_default" msgid="3784803084573509491">"假設應用程式支援新型格式"</string> + <string name="transcode_notification" msgid="5560515979793436168">"顯示轉碼通知"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"執行中的服務"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"查看並控制目前正在執行中的服務"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 設置"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"數據網絡訊號滿格。"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"以太網連接中斷。"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"以太網絡。"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"不啟用通話。"</string> </resources> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 7cf02978efd8..2c077e7991be 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"覆寫轉碼預設設定"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"啟用轉碼"</string> <string name="transcode_default" msgid="3784803084573509491">"假設應用程式支援新格式"</string> + <string name="transcode_notification" msgid="5560515979793436168">"顯示轉碼通知"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"正在運作的服務"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"查看並管理目前正在執行的服務"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 實作"</string> @@ -434,9 +435,9 @@ <skip /> <string name="power_discharge_by_enhanced" msgid="563438403581662942">"根據你的使用情形,目前電量為 <xliff:g id="LEVEL">%2$s</xliff:g>,預估可持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"根據你的使用情形,預估可持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_discharge_by" msgid="4113180890060388350">"目前電量 <xliff:g id="LEVEL">%2$s</xliff:g>,預估還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string> + <string name="power_discharge_by" msgid="4113180890060388350">"目前電量 <xliff:g id="LEVEL">%2$s</xliff:g>,預估可用到<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"預估電力大約可使用到<xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_discharge_by_only_short" msgid="5883041507426914446">"還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string> + <string name="power_discharge_by_only_short" msgid="5883041507426914446">"可用到<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"電池電力可能於<xliff:g id="TIME">%1$s</xliff:g> 前耗盡"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"數據網路訊號滿格。"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"未連上乙太網路。"</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"乙太網路。"</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"不顯示在螢幕上。"</string> </resources> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index d950d58b5bf7..87cf75b0beaf 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -404,6 +404,7 @@ <string name="transcode_user_control" msgid="6176368544817731314">"Khipha okuzenzakalelayo kokudlulisela ikhodi"</string> <string name="transcode_enable_all" msgid="2411165920039166710">"Nika amandla ukudlulisela ikhodi"</string> <string name="transcode_default" msgid="3784803084573509491">"Kuthathe njengokungathi izinhlelo zokusebenza zisekela amafomethi esimanje"</string> + <string name="transcode_notification" msgid="5560515979793436168">"Bonisa izaziso zokudlulisela ikhodi"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Amasevisi asebenzayo"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Buka futhi ulawule amasevisi asebenzayo okwamanje"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"Ukufakwa ke-WebView"</string> @@ -592,6 +593,5 @@ <string name="accessibility_data_signal_full" msgid="1808301899314382337">"Igcwele i-signal yedatha"</string> <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"I-Ethernet inqanyuliwe."</string> <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"I-Ethernet."</string> - <!-- no translation found for accessibility_no_calling (3540827068323895748) --> - <skip /> + <string name="accessibility_no_calling" msgid="3540827068323895748">"Akukho ukwenza ikholi"</string> </resources> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 3866151982a4..79fbcc376b3c 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1007,6 +1007,9 @@ <!-- Settings item title to select the default behavior for transcoding if an encodig is not supported by an app. [CHAR LIMIT=85] --> <string name="transcode_default">Assume apps support modern formats</string> + <!-- Settings item title to select whether to show transcoding notifications. [CHAR LIMIT=85] --> + <string name="transcode_notification">Show transcoding notifications</string> + <!-- Services settings screen, setting option name for the user to go to the screen to view running services --> <string name="runningservices_settings_title">Running services</string> <!-- Services settings screen, setting option summary for the user to go to the screen to view running services --> diff --git a/packages/SettingsLib/search/Android.bp b/packages/SettingsLib/search/Android.bp index d398aa5c44ac..45746d9848df 100644 --- a/packages/SettingsLib/search/Android.bp +++ b/packages/SettingsLib/search/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_library { name: "SettingsLib-search", srcs: ["src/**/*.java"], diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 4c80b91f300d..5e2d21b2e188 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -851,11 +851,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> if (BluetoothUuid.containsAnyUuid(uuids, PbapServerProfile.PBAB_CLIENT_UUIDS)) { // The pairing dialog now warns of phone-book access for paired devices. // No separate prompt is displayed after pairing. + final BluetoothClass bluetoothClass = mDevice.getBluetoothClass(); if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) { - if (mDevice.getBluetoothClass().getDeviceClass() - == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE || - mDevice.getBluetoothClass().getDeviceClass() - == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET) { + if (bluetoothClass != null && (bluetoothClass.getDeviceClass() + == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE + || bluetoothClass.getDeviceClass() + == BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) { EventLog.writeEvent(0x534e4554, "138529441", -1, ""); } mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED); diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java index 81ca9eaf8e36..228de039fc1b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java +++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java @@ -163,8 +163,12 @@ public class RecentLocationAccesses { long locationAccessFinishTime = 0L; // Earliest time for a location access to end and still be shown in list. long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS; + // Compute the most recent access time from all op entries. for (AppOpsManager.OpEntry entry : entries) { - locationAccessFinishTime = entry.getLastAccessTime(TRUSTED_STATE_FLAGS); + long lastAccessTime = entry.getLastAccessTime(TRUSTED_STATE_FLAGS); + if (lastAccessTime > locationAccessFinishTime) { + locationAccessFinishTime = lastAccessTime; + } } // Bail out if the entry is out of date. if (locationAccessFinishTime < recentLocationCutoffTime) { diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 32419f49d8c9..1f3e0e9fe038 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -205,7 +205,6 @@ public class LocalMediaManager implements BluetoothCallback { void dispatchDeviceListUpdate() { final List<MediaDevice> mediaDevices = new ArrayList<>(mMediaDevices); - Collections.sort(mediaDevices, COMPARATOR); for (DeviceCallback callback : getCallbacks()) { callback.onDeviceListUpdate(mediaDevices); } @@ -472,6 +471,7 @@ public class LocalMediaManager implements BluetoothCallback { synchronized (mMediaDevicesLock) { mMediaDevices.clear(); mMediaDevices.addAll(devices); + Collections.sort(devices, COMPARATOR); // Add disconnected bluetooth devices only when phone output device is available. for (MediaDevice device : devices) { final int type = device.getDeviceType(); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java index 647fd2acf7c8..552fa11a42b7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputConstants.java @@ -39,8 +39,7 @@ public class MediaOutputConstants { /** * A string extra specifying a media package name. */ - public static final String EXTRA_PACKAGE_NAME = - "com.android.settings.panel.extra.PACKAGE_NAME"; + public static final String EXTRA_PACKAGE_NAME = "package_name"; /** * An intent action to launch media output dialog. diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp index 92e32d96cdf3..64563be78fbb 100644 --- a/packages/SettingsLib/tests/integ/Android.bp +++ b/packages/SettingsLib/tests/integ/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "SettingsLibTests", defaults: [ diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp index 3756c3bf7704..dc661c234116 100644 --- a/packages/SettingsLib/tests/robotests/Android.bp +++ b/packages/SettingsLib/tests/robotests/Android.bp @@ -16,6 +16,15 @@ // SettingsLib Shell app just for Robolectric test target. # //########################################################### +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"], +} + android_app { name: "SettingsLibShell", defaults: ["SettingsLibDefaults"], diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index 53ff1a10b6ff..53a99ab8cbe4 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -27,12 +27,14 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.media.AudioManager; import com.android.settingslib.R; +import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import org.junit.Before; import org.junit.Test; @@ -41,8 +43,11 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowBluetoothAdapter.class}) public class CachedBluetoothDeviceTest { private static final String DEVICE_NAME = "TestName"; private static final String DEVICE_ALIAS = "TestAlias"; @@ -72,12 +77,14 @@ public class CachedBluetoothDeviceTest { private AudioManager mAudioManager; private Context mContext; private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; mAudioManager = mContext.getSystemService(AudioManager.class); + mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS); when(mHfpProfile.isProfileReady()).thenReturn(true); when(mA2dpProfile.isProfileReady()).thenReturn(true); @@ -937,4 +944,17 @@ public class CachedBluetoothDeviceTest { assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( mContext.getString(R.string.profile_connect_timeout_subtext)); } + + @Test + public void onUuidChanged_bluetoothClassIsNull_shouldNotCrash() { + mShadowBluetoothAdapter.setUuids(PbapServerProfile.PBAB_CLIENT_UUIDS); + when(mDevice.getUuids()).thenReturn(PbapServerProfile.PBAB_CLIENT_UUIDS); + when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); + when(mDevice.getPhonebookAccessPermission()).thenReturn(BluetoothDevice.ACCESS_UNKNOWN); + when(mDevice.getBluetoothClass()).thenReturn(null); + + mCachedDevice.onUuidChanged(); + + // Should not crash + } } diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java index b265d46058be..3b7fbc73522f 100644 --- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java +++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java @@ -24,6 +24,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.os.ParcelUuid; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @@ -36,6 +37,7 @@ public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBlueto private List<Integer> mSupportedProfiles; private List<BluetoothDevice> mMostRecentlyConnectedDevices; private BluetoothProfile.ServiceListener mServiceListener; + private ParcelUuid[] mParcelUuids; @Implementation protected boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, @@ -87,4 +89,13 @@ public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBlueto } return true; } + + @Implementation + protected ParcelUuid[] getUuids() { + return mParcelUuids; + } + + public void setUuids(ParcelUuid[] uuids) { + mParcelUuids = uuids; + } } diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index 2e5347869554..f490c87aa4ec 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -1,3 +1,22 @@ +package { + default_applicable_licenses: [ + "frameworks_base_packages_SettingsProvider_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_packages_SettingsProvider_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + android_app { name: "SettingsProvider", defaults: ["platform_app_defaults"], diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index 66165b6d1ff2..ad6a5312f156 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -147,5 +147,10 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.NOTIFICATION_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR); + VALIDATORS.put( + Global.ONE_HANDED_KEYGUARD_SIDE, + new InclusiveIntegerRangeValidator( + /* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT, + /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT)); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 1ce738ef065c..a0b952882162 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -551,9 +551,6 @@ class SettingsProtoDumpUtil { Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, GlobalSettingsProto.Development.FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS); dumpSetting(s, p, - Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, - GlobalSettingsProto.Development.ENABLE_SIZECOMPAT_FREEFORM); - dumpSetting(s, p, Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, GlobalSettingsProto.Development.ENABLE_NON_RESIZABLE_MULTI_WINDOW); p.end(developmentToken); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index a1fd7eefaf9a..e427981b87d7 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -4780,17 +4780,23 @@ public class SettingsProvider extends ContentProvider { } if (currentVersion == 192) { - // Version 192: set the default value for magnification capabilities. If - // magnification is enabled by the user, set it to full-screen, and set a value - // to show a prompt when using the magnification first time after upgrading. + // Version 192: set the default value for magnification capabilities. + // If the device supports magnification area and magnification is enabled + // by the user, set it to full-screen, and set a value to show a prompt + // when using the magnification first time after upgrading. final SettingsState secureSettings = getSecureSettingsLocked(userId); final Setting magnificationCapabilities = secureSettings.getSettingLocked( Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY); + final boolean supportMagnificationArea = getContext().getResources().getBoolean( + com.android.internal.R.bool.config_magnification_area); + final int capability = supportMagnificationArea + ? R.integer.def_accessibility_magnification_capabilities + : Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + final String supportShowPrompt = supportMagnificationArea ? "1" : "0"; if (magnificationCapabilities.isNull()) { secureSettings.insertSettingLocked( Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY, - String.valueOf(getContext().getResources().getInteger( - R.integer.def_accessibility_magnification_capabilities)), + String.valueOf(getContext().getResources().getInteger(capability)), null, true, SettingsState.SYSTEM_PACKAGE_NAME); if (isMagnificationSettingsOn(secureSettings)) { @@ -4800,7 +4806,8 @@ public class SettingsProvider extends ContentProvider { null, false /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME); secureSettings.insertSettingLocked( - Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, "1", + Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, + supportShowPrompt, null, false /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME); } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 6719f179ef86..4dc6d1475c4a 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -227,7 +227,6 @@ public class SettingsBackupTest { Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, Settings.Global.DEVELOPMENT_FORCE_RTL, - Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_SV, @@ -283,6 +282,7 @@ public class SettingsBackupTest { Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS, Settings.Global.EUICC_SWITCH_SLOT_TIMEOUT_MILLIS, Settings.Global.FANCY_IME_ANIMATIONS, + Settings.Global.ONE_HANDED_KEYGUARD_SIDE, Settings.Global.FORCE_ALLOW_ON_EXTERNAL, Settings.Global.FORCED_APP_STANDBY_ENABLED, Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, @@ -298,7 +298,6 @@ public class SettingsBackupTest { Settings.Global.GNSS_SATELLITE_BLOCKLIST, Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS, Settings.Global.HDMI_CEC_SWITCH_ENABLED, - Settings.Global.HDMI_CEC_VERSION, Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, Settings.Global.HDMI_CONTROL_ENABLED, diff --git a/packages/SharedStorageBackup/Android.bp b/packages/SharedStorageBackup/Android.bp index d02f4800fc83..21516fade1ab 100644 --- a/packages/SharedStorageBackup/Android.bp +++ b/packages/SharedStorageBackup/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_app { name: "SharedStorageBackup", defaults: ["platform_app_defaults"], diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp index 546642db881a..c87916fa3b95 100644 --- a/packages/Shell/Android.bp +++ b/packages/Shell/Android.bp @@ -1,3 +1,12 @@ +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"], +} + // used both for the android_app and android_library shell_srcs = ["src/**/*.java",":dumpstate_aidl"] shell_static_libs = ["androidx.legacy_legacy-support-v4"] diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index a15ceb6d8811..71e09106368b 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -84,8 +84,7 @@ <uses-permission android:name="android.permission.READ_INPUT_STATE" /> <uses-permission android:name="android.permission.SET_ORIENTATION" /> <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> - <!-- TODO(b/152310230): remove once APIs are confirmed to be sufficient --> - <uses-permission android:name="com.android.permission.USE_INSTALLER_V2" /> + <uses-permission android:name="com.android.permission.USE_SYSTEM_DATA_LOADERS" /> <uses-permission android:name="android.permission.MOVE_PACKAGE" /> <uses-permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> @@ -124,6 +123,8 @@ <uses-permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP" /> <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" /> <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" /> + <uses-permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" /> + <uses-permission android:name="android.permission.CLEAR_FREEZE_PERIOD" /> <uses-permission android:name="android.permission.QUERY_USERS" /> <uses-permission android:name="android.permission.MODIFY_QUIET_MODE" /> <uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/> @@ -397,6 +398,7 @@ <!-- Permission required for CTS to test sensor privacy behavior --> <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" /> + <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" /> <!-- Permission needed for CTS test - CallLogTest --> <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" /> diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS index 34901f5830c4..6d738f8d4d43 100644 --- a/packages/Shell/OWNERS +++ b/packages/Shell/OWNERS @@ -9,3 +9,4 @@ yamasani@google.com toddke@google.com cbrubaker@google.com omakoto@google.com +michaelwr@google.com diff --git a/packages/Shell/res/values-ky/strings.xml b/packages/Shell/res/values-ky/strings.xml index 3567ac276e63..d73ee2fdb4e9 100644 --- a/packages/Shell/res/values-ky/strings.xml +++ b/packages/Shell/res/values-ky/strings.xml @@ -25,7 +25,7 @@ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Мүчүлүштүктөр жөнүндө кабар жакында телефонго чыгат"</string> <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Мүчүлүштүк тууралуу кабарды жөнөтүү үчүн таптап коюңуз"</string> <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Мүчүлүштүк тууралуу билдирүүңүздү бөлүшүү үчүн таптап коюңуз"</string> - <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Мүчүлүштүк тууралуу кабарды скриншотсуз жөнөтүү үчүн солго серпиңиз же скриншот даяр болгуча күтүңүз"</string> + <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Мүчүлүштүк тууралуу кабарды скриншотсуз жөнөтүү үчүн солго сүрүңүз же скриншот даяр болгуча күтүңүз"</string> <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Мүчүлүштүк тууралуу билдирүүңүздү скриншотсуз бөлүшүү үчүн таптап коюңуз же скриншот даяр болгуча күтө туруңуз"</string> <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Мүчүлүштүк тууралуу билдирүүңүздү скриншотсуз бөлүшүү үчүн таптап коюңуз же скриншот даяр болгуча күтө туруңуз"</string> <string name="bugreport_confirm" msgid="5917407234515812495">"Мүчүлүштүктөр тууралуу билдирүүлөрдө тутумдун ар кандай таржымалдарынан алынган дайындар, ошондой эле купуя маалымат камтылышы мүмкүн (мисалы, жайгашкан жер сыяктуу). Мындай билдирүүлөрдү бир гана ишеничтүү адамдар жана колдонмолор менен бөлүшүңүз."</string> diff --git a/packages/Shell/tests/Android.bp b/packages/Shell/tests/Android.bp index 8536c4fbb5a3..70e8c10a961d 100644 --- a/packages/Shell/tests/Android.bp +++ b/packages/Shell/tests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "ShellTests", srcs: ["src/**/*.java"], diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp index 9c0d78c41e5e..a9fe3ad1a596 100644 --- a/packages/SimAppDialog/Android.bp +++ b/packages/SimAppDialog/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "SimAppDialog", defaults: ["platform_app_defaults"], diff --git a/packages/SoundPicker/Android.bp b/packages/SoundPicker/Android.bp index 56e7cd162cdf..2c89d6dbce56 100644 --- a/packages/SoundPicker/Android.bp +++ b/packages/SoundPicker/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "SoundPicker", defaults: ["platform_app_defaults"], diff --git a/packages/StatementService/Android.bp b/packages/StatementService/Android.bp index 2664a0316f5c..32defc822733 100644 --- a/packages/StatementService/Android.bp +++ b/packages/StatementService/Android.bp @@ -11,6 +11,15 @@ // WITHOUT WARRANTIES 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"], +} + android_app { name: "StatementService", defaults: ["platform_app_defaults"], diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index bf5198eadb9c..b6fd286d3ad1 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -14,6 +14,23 @@ // limitations under the License. // +package { + default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_packages_SystemUI_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + java_library { name: "SystemUI-proto", diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 2faca8dbdcbf..b6d942a29d49 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -87,6 +87,7 @@ <uses-permission android:name="android.permission.MASTER_CLEAR" /> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" /> + <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" /> <!-- ActivityManager --> <uses-permission android:name="android.permission.REAL_GET_TASKS" /> @@ -334,6 +335,12 @@ </intent-filter> </receiver> + <activity android:name=".screenshot.LongScreenshotActivity" + android:theme="@android:style/Theme.DeviceDefault.NoActionBar" + android:process=":screenshot" + android:exported="false" + android:finishOnTaskLaunch="true" /> + <activity android:name=".screenrecord.ScreenRecordDialog" android:theme="@style/ScreenRecord" android:showForAllUsers="true" diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp index ab4f800d2586..9e67e4becc8e 100644 --- a/packages/SystemUI/plugin/Android.bp +++ b/packages/SystemUI/plugin/Android.bp @@ -12,6 +12,15 @@ // 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_packages_SystemUI_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], +} + java_library { name: "SystemUIPluginLib", diff --git a/packages/SystemUI/plugin/ExamplePlugin/Android.bp b/packages/SystemUI/plugin/ExamplePlugin/Android.bp index c6c80f3780a3..3f0fdedb57da 100644 --- a/packages/SystemUI/plugin/ExamplePlugin/Android.bp +++ b/packages/SystemUI/plugin/ExamplePlugin/Android.bp @@ -1,3 +1,12 @@ +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_packages_SystemUI_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], +} + android_app { name: "ExamplePlugin", diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java index bcd28a6ab124..53f7e44bc25a 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java @@ -50,6 +50,4 @@ public abstract class QSTileView extends LinearLayout { public abstract void onStateChanged(State state); public abstract int getDetailY(); - - public void setShowLabels(boolean show) {} } diff --git a/packages/SystemUI/plugin_core/Android.bp b/packages/SystemUI/plugin_core/Android.bp index 581fef721acc..34d31d9955fc 100644 --- a/packages/SystemUI/plugin_core/Android.bp +++ b/packages/SystemUI/plugin_core/Android.bp @@ -12,6 +12,15 @@ // 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_packages_SystemUI_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], +} + java_library { sdk_version: "current", name: "PluginCoreLib", diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index f3ec39d2b1fb..b6a41c20a607 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -64,7 +64,7 @@ android:id="@+id/new_lockscreen_clock_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentEnd="true" + android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:visibility="gone"> <com.android.keyguard.AnimatableClockView @@ -73,7 +73,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:gravity="center_horizontal" - android:textSize="100dp" + android:textSize="60dp" android:fontFamily="@font/clock" android:typeface="monospace" android:elegantTextHeight="false" diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 2a5784a1e6e9..115a156b65e7 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -47,26 +47,25 @@ <dimen name="eca_overlap">-10dip</dimen> <!-- Slice header --> - <dimen name="widget_title_font_size">24dp</dimen> - <dimen name="header_subtitle_padding">12dp</dimen> + <dimen name="widget_title_font_size">20dp</dimen> + <dimen name="widget_title_line_height">24dp</dimen> <dimen name="header_icon_size">16dp</dimen> <!-- Slice subtitle --> - <dimen name="widget_label_font_size">18dp</dimen> + <dimen name="widget_label_font_size">16dp</dimen> + <dimen name="widget_label_line_height">20dp</dimen> <!-- Clock without header --> <dimen name="widget_big_font_size">54dp</dimen> <dimen name="bottom_text_spacing_digital">0dp</dimen> <dimen name="title_clock_padding">4dp</dimen> <!-- Clock with header --> <dimen name="widget_small_font_size">@dimen/widget_title_font_size</dimen> - <dimen name="widget_vertical_padding">17dp</dimen> + <dimen name="widget_vertical_padding">5dp</dimen> <dimen name="widget_vertical_padding_with_header">25dp</dimen> <dimen name="widget_vertical_padding_clock">12dp</dimen> <!-- Subtitle paddings --> <dimen name="widget_horizontal_padding">8dp</dimen> - <dimen name="widget_icon_size">20dp</dimen> + <dimen name="widget_icon_size">18dp</dimen> <dimen name="widget_icon_padding">8dp</dimen> - <dimen name="subtitle_clock_padding">0dp</dimen> - <dimen name="header_row_font_size">14dp</dimen> <!-- Notification shelf padding when dark --> <dimen name="widget_bottom_separator_padding">-6dp</dimen> diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index 2391803957e5..8f42cbe31646 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -120,6 +120,7 @@ <style name="TextAppearance.Keyguard"> <item name="android:textSize">@dimen/widget_title_font_size</item> + <item name="android:lineHeight">@dimen/widget_title_line_height</item> <item name="android:gravity">center</item> <item name="android:ellipsize">end</item> <item name="android:maxLines">2</item> @@ -133,6 +134,7 @@ <item name="android:layout_height">wrap_content</item> <item name="android:lines">1</item> <item name="android:textSize">@dimen/widget_label_font_size</item> + <item name="android:lineHeight">@dimen/widget_label_line_height</item> </style> <style name="TextAppearance.Keyguard.BottomArea"> diff --git a/packages/SystemUI/res-product/values-az/strings.xml b/packages/SystemUI/res-product/values-az/strings.xml index ee86ae23d48f..c0668dbcea07 100644 --- a/packages/SystemUI/res-product/values-az/strings.xml +++ b/packages/SystemUI/res-product/values-az/strings.xml @@ -38,8 +38,8 @@ <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Telefonun kilidini açmaq üçün <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış cəhd etmisiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> uğursuz cəhddən sonra iş profili silinəcək və bütün profil datası ləğv ediləcək."</string> <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Planşetin kilidini açmaq üçün <xliff:g id="NUMBER">%d</xliff:g> dəfə yanlış cəhd etmisiniz. İş profili silinəcək və bütün data ləğv ediləcək."</string> <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Telefonun kilidini açmaq üçün <xliff:g id="NUMBER">%d</xliff:g> dəfə yanlış cəhd etmisiniz. İş profili silinəcək və bütün data ləğv ediləcək."</string> - <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Kilid açma modelini <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış çəkmisiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> uğursuz cəhddən sonra planşet kilidini e-poçt hesabınızla açmaq tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə sonra yenidən cəhd edin."</string> - <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Kilid açma modelini artıq <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış çəkmisiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> uğursuz cəhddən sonra telefon kilidini e-poçt hesabınızla açmaq tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə sonra yenidən cəhd edin."</string> + <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Kilid açma modelini <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış çəkmisiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> uğursuz cəhddən sonra planşet kilidini e-poçt hesabınızla açmaq tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə sonra cəhd edin."</string> + <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Kilid açma modelini artıq <xliff:g id="NUMBER_0">%1$d</xliff:g> dəfə yanlış çəkmisiniz. Daha <xliff:g id="NUMBER_1">%2$d</xliff:g> uğursuz cəhddən sonra telefon kilidini e-poçt hesabınızla açmaq tələb olunacaq.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> saniyə sonra cəhd edin."</string> <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Daha çox seçim üçün telefonu kiliddən çıxarın"</string> <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Daha çox seçim üçün planşeti kiliddən çıxarın"</string> <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Daha çox seçim üçün cihazı kiliddən çıxarın"</string> diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml index d097472471b0..8efe0539207a 100644 --- a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml +++ b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml @@ -41,8 +41,10 @@ </item> <item android:id="@android:id/progress" android:gravity="center_vertical|fill_horizontal"> - <com.android.systemui.util.RoundedCornerProgressDrawable + <clip android:drawable="@drawable/brightness_progress_full_drawable" + android:clipOrientation="horizontal" + android:gravity="left" /> </item> </layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml index 41140a7a8c85..5bc2773dc657 100644 --- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml +++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml @@ -26,10 +26,10 @@ </item> <item android:id="@+id/slider_icon" - android:gravity="center_vertical|right" + android:gravity="center_vertical|left" android:height="@dimen/rounded_slider_icon_size" android:width="@dimen/rounded_slider_icon_size" - android:right="@dimen/rounded_slider_icon_inset"> + android:left="@dimen/rounded_slider_icon_inset"> <com.android.systemui.util.AlphaTintDrawableWrapper android:drawable="@drawable/ic_brightness" android:tint="?android:attr/colorBackground" diff --git a/packages/SystemUI/res/drawable/control_no_favorites_background.xml b/packages/SystemUI/res/drawable/control_no_favorites_background.xml index d895dd0c85c7..2165b12e5697 100644 --- a/packages/SystemUI/res/drawable/control_no_favorites_background.xml +++ b/packages/SystemUI/res/drawable/control_no_favorites_background.xml @@ -26,12 +26,4 @@ <corners android:radius="@dimen/control_corner_radius" /> </shape> </item> - <item> - <shape> - <stroke - android:width="1dp" - android:color="#4DFFFFFF" /> - <corners android:radius="@dimen/control_corner_radius"/> - </shape> - </item> </ripple> diff --git a/packages/SystemUI/res/drawable/controls_dialog_bg.xml b/packages/SystemUI/res/drawable/controls_dialog_bg.xml index cb4686dd04a7..1ccb176b8689 100644 --- a/packages/SystemUI/res/drawable/controls_dialog_bg.xml +++ b/packages/SystemUI/res/drawable/controls_dialog_bg.xml @@ -16,6 +16,6 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> - <solid android:color="?android:attr/colorBackgroundFloating" /> + <solid android:color="?android:attr/colorBackground" /> <corners android:radius="@dimen/notification_corner_radius" /> </shape> diff --git a/packages/SystemUI/res/drawable/horizontal_ellipsis.xml b/packages/SystemUI/res/drawable/horizontal_ellipsis.xml new file mode 100644 index 000000000000..1800857a826c --- /dev/null +++ b/packages/SystemUI/res/drawable/horizontal_ellipsis.xml @@ -0,0 +1,27 @@ +<?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. +--> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?android:attr/colorBackgroundFloating" > + + <path + android:pathData="M6 10c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zm12 0c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zm-6 0c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2z" + android:fillColor="@android:color/white" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml index 9f66581d8053..96e01934e670 100644 --- a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml +++ b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml @@ -36,8 +36,4 @@ </vector> </item> - <item> - <ripple android:color="@color/GM2_grey_600"></ripple> - </item> - </layer-list> diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml index 659b02048c73..368c8dfb5023 100644 --- a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml +++ b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml @@ -50,28 +50,7 @@ android:strokeWidth="1" android:pathData="M 12.5 13.92 L 15.59 17 L 17 15.59 L 13.91 12.5 L 16.5 12.5 L 16.5 10.5 L 10.5 10.5 L 10.5 16.5 L 12.5 16.5 Z" /> </group> - <group - android:translateX="22.000000" - android:translateY="2.000000"> - <group - android:translateX="3.000000" - android:translateY="3.000000"> - <group - android:translateX="-3.000000" - android:translateY="-3.000000"> - <path - android:fillColor="#80868B" - android:fillType="evenOdd" - android:strokeWidth="1" - android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" /> - </group> - </group> - </group> </vector> </item> - <item> - <ripple android:color="@color/GM2_grey_600"></ripple> - </item> - </layer-list> diff --git a/packages/SystemUI/res/drawable/ic_phone_missed.xml b/packages/SystemUI/res/drawable/ic_phone_missed.xml new file mode 100644 index 000000000000..72e67d4a2ed0 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_phone_missed.xml @@ -0,0 +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M6.5,5.5L12,11l7,-7 -1,-1 -6,6 -4.5,-4.5L11,4.5L11,3L5,3v6h1.5L6.5,5.5zM23.71,16.67C20.66,13.78 16.54,12 12,12 7.46,12 3.34,13.78 0.29,16.67c-0.18,0.18 -0.29,0.43 -0.29,0.71s0.11,0.53 0.29,0.71l2.48,2.48c0.18,0.18 0.43,0.29 0.71,0.29 0.27,0 0.52,-0.11 0.7,-0.28 0.79,-0.74 1.69,-1.36 2.66,-1.85 0.33,-0.16 0.56,-0.5 0.56,-0.9v-3.1c1.45,-0.48 3,-0.73 4.6,-0.73 1.6,0 3.15,0.25 4.6,0.72v3.1c0,0.39 0.23,0.74 0.56,0.9 0.98,0.49 1.87,1.12 2.67,1.85 0.18,0.18 0.43,0.28 0.7,0.28 0.28,0 0.53,-0.11 0.71,-0.29l2.48,-2.48c0.18,-0.18 0.29,-0.43 0.29,-0.71s-0.12,-0.52 -0.3,-0.7z" /> +</vector> diff --git a/packages/SystemUI/res/drawable/people_space_content_background.xml b/packages/SystemUI/res/drawable/people_space_content_background.xml index 32314d29277e..30519aeddb45 100644 --- a/packages/SystemUI/res/drawable/people_space_content_background.xml +++ b/packages/SystemUI/res/drawable/people_space_content_background.xml @@ -15,6 +15,6 @@ ~ limitations under the License. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" > - <solid android:color="?android:attr/colorControlHighlight" /> + <solid android:color="?android:attr/colorBackground" /> <corners android:radius="@dimen/people_space_image_radius" /> </shape> diff --git a/packages/SystemUI/res/layout/qs_tile_label_divider.xml b/packages/SystemUI/res/drawable/volume_drawer_bg.xml index 150a5b8bfef1..f0e22926d07a 100644 --- a/packages/SystemUI/res/layout/qs_tile_label_divider.xml +++ b/packages/SystemUI/res/drawable/volume_drawer_bg.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 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,5 +14,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - -<View />
\ No newline at end of file +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:paddingMode="stack" > + <size android:width="@dimen/volume_ringer_drawer_item_size" /> + <solid android:color="?android:attr/colorBackgroundFloating" /> + <corners android:radius="@dimen/volume_ringer_drawer_item_size_half" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml new file mode 100644 index 000000000000..5e7cb12d1c5f --- /dev/null +++ b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml @@ -0,0 +1,24 @@ +<?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. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:paddingMode="stack" > + <size + android:height="@dimen/volume_ringer_drawer_item_size" + android:width="@dimen/volume_ringer_drawer_item_size" /> + <solid android:color="?android:attr/colorAccent" /> + <corners android:radius="@dimen/volume_ringer_drawer_item_size_half" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar.xml b/packages/SystemUI/res/drawable/volume_row_seekbar.xml new file mode 100644 index 000000000000..b0e0ed5079e6 --- /dev/null +++ b/packages/SystemUI/res/drawable/volume_row_seekbar.xml @@ -0,0 +1,56 @@ +<?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. + --> + +<!-- SeekBar drawable for volume rows. This contains a background layer (with a solid round rect, + and a bottom-aligned icon) and a progress layer (with an accent-colored round rect and icon) + that moves up and down with the progress value. --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" + android:paddingMode="stack" > + <item android:id="@android:id/background" + android:gravity="center_vertical|fill_horizontal"> + <layer-list> + <item android:id="@+id/volume_seekbar_background_solid"> + <shape> + <size android:height="@dimen/volume_dialog_panel_width" /> + <solid android:color="?android:attr/colorBackgroundFloating" /> + <corners android:radius="@dimen/volume_dialog_panel_width_half" /> + </shape> + </item> + <item + android:id="@+id/volume_seekbar_background_icon" + android:gravity="center_vertical|left" + android:height="@dimen/rounded_slider_icon_size" + android:width="@dimen/rounded_slider_icon_size" + android:left="@dimen/rounded_slider_icon_inset"> + <rotate + android:fromDegrees="-270" + android:toDegrees="-270"> + <!-- A placeholder drawable is required here - it'll be replaced in code. --> + <com.android.systemui.util.AlphaTintDrawableWrapper + android:drawable="@drawable/ic_volume_media" + android:tint="?android:attr/colorAccent" /> + </rotate> + </item> + </layer-list> + </item> + <item android:id="@android:id/progress" + android:gravity="center_vertical|fill_horizontal"> + <com.android.systemui.util.RoundedCornerProgressDrawable + android:drawable="@drawable/volume_row_seekbar_progress" + /> + </item> +</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml b/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml new file mode 100644 index 000000000000..ef202360b1ce --- /dev/null +++ b/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml @@ -0,0 +1,44 @@ +<?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. + --> + +<!-- Progress drawable for volume row SeekBars. This is the accent-colored round rect that moves up + and down as the progress value changes. --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" + android:autoMirrored="true"> + <item android:id="@+id/volume_seekbar_progress_solid"> + <shape> + <size android:height="@dimen/volume_dialog_panel_width" /> + <solid android:color="?android:attr/colorAccent" /> + <corners android:radius="@dimen/volume_dialog_panel_width_half"/> + </shape> + </item> + <item + android:id="@+id/volume_seekbar_progress_icon" + android:gravity="center_vertical|right" + android:height="@dimen/rounded_slider_icon_size" + android:width="@dimen/rounded_slider_icon_size" + android:right="@dimen/rounded_slider_icon_inset"> + <rotate + android:fromDegrees="-270" + android:toDegrees="-270"> + <!-- A placeholder drawable is required here - it'll be replaced in code. --> + <com.android.systemui.util.AlphaTintDrawableWrapper + android:drawable="@drawable/ic_volume_media" + android:tint="?android:attr/colorBackgroundFloating" /> + </rotate> + </item> +</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml index c420117073c5..237dc02e5d8c 100644 --- a/packages/SystemUI/res/layout-land/volume_dialog.xml +++ b/packages/SystemUI/res/layout-land/volume_dialog.xml @@ -32,105 +32,124 @@ android:gravity="right" android:layout_gravity="right" android:background="@android:color/transparent" - android:paddingRight="@dimen/volume_dialog_panel_transparent_padding_right" - android:paddingTop="@dimen/volume_dialog_panel_transparent_padding" - android:paddingBottom="@dimen/volume_dialog_panel_transparent_padding" + android:paddingRight="@dimen/volume_dialog_stream_padding" android:paddingLeft="@dimen/volume_dialog_panel_transparent_padding" android:clipToPadding="false"> - <FrameLayout - android:id="@+id/ringer" - android:layout_width="@dimen/volume_dialog_ringer_size" - android:layout_height="@dimen/volume_dialog_ringer_size" - android:layout_marginBottom="@dimen/volume_dialog_spacer" - android:gravity="right" - android:layout_gravity="right" - android:translationZ="@dimen/volume_dialog_elevation" - android:clipToPadding="false" - android:background="@drawable/rounded_bg_full"> - <com.android.keyguard.AlphaOptimizedImageButton - android:id="@+id/ringer_icon" - style="@style/VolumeButtons" - android:background="@drawable/rounded_ripple" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scaleType="fitCenter" - android:padding="@dimen/volume_dialog_ringer_icon_padding" - android:tint="@color/accent_tint_color_selector" - android:layout_gravity="center" - android:soundEffectsEnabled="false" /> - - <include layout="@layout/volume_dnd_icon" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginRight="@dimen/volume_dialog_stream_padding" - android:layout_marginTop="6dp"/> - </FrameLayout> - + <!-- + Container for a) the ringer drawer and the caption button next to b) the volume rows. + --> <LinearLayout - android:id="@+id/main" android:layout_width="wrap_content" - android:minWidth="@dimen/volume_dialog_panel_width" android:layout_height="wrap_content" - android:layout_marginTop="68dp" - android:gravity="right" - android:layout_gravity="right" - android:orientation="vertical" - android:translationZ="@dimen/volume_dialog_elevation" + android:orientation="horizontal" android:clipChildren="false" - android:clipToPadding="false" - android:background="@drawable/rounded_bg_full" > - <LinearLayout - android:id="@+id/volume_dialog_rows" + android:clipToPadding="false"> + + <!-- The ringer drawer and the caption button. --> + <FrameLayout android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:minWidth="@dimen/volume_dialog_panel_width" - android:gravity="center" - android:orientation="horizontal" + android:layout_height="match_parent" android:paddingRight="@dimen/volume_dialog_stream_padding" - android:paddingLeft="@dimen/volume_dialog_stream_padding"> - <!-- volume rows added and removed here! :-) --> - </LinearLayout> + android:clipChildren="false" + android:clipToPadding="false" + android:orientation="vertical"> + + <include layout="@layout/volume_ringer_drawer" + android:layout_gravity="top|right"/> + + <FrameLayout + android:id="@+id/odi_captions" + android:layout_width="@dimen/volume_dialog_caption_size" + android:layout_height="@dimen/volume_dialog_caption_size" + android:gravity="center" + android:layout_gravity="bottom|right" + android:layout_marginBottom="@dimen/volume_dialog_tap_target_size" + android:clipToPadding="false"> + + <com.android.systemui.volume.CaptionsToggleImageButton + android:id="@+id/odi_captions_icon" + android:src="@drawable/ic_volume_odi_captions_disabled" + style="@style/VolumeButtons" + android:background="@drawable/rounded_ripple" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:tint="@color/caption_tint_color_selector" + android:layout_gravity="center" + android:soundEffectsEnabled="false" + sysui:optedOut="false"/> + + </FrameLayout> + + </FrameLayout> + <FrameLayout - android:id="@+id/settings_container" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/rounded_bg_bottom_background"> + android:visibility="gone" + android:id="@+id/ringer" + android:layout_width="@dimen/volume_dialog_ringer_size" + android:layout_height="@dimen/volume_dialog_ringer_size" + android:layout_marginBottom="@dimen/volume_dialog_spacer" + android:gravity="right" + android:layout_gravity="right" + android:translationZ="@dimen/volume_dialog_elevation" + android:clipToPadding="false" + android:background="@drawable/rounded_bg_full"> <com.android.keyguard.AlphaOptimizedImageButton - android:id="@+id/settings" - android:src="@drawable/ic_tune_black_16dp" - android:layout_width="@dimen/volume_dialog_tap_target_size" - android:layout_height="@dimen/volume_dialog_tap_target_size" + android:id="@+id/ringer_icon" + style="@style/VolumeButtons" + android:background="@drawable/rounded_ripple" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="fitCenter" + android:padding="@dimen/volume_dialog_ringer_icon_padding" + android:tint="@color/accent_tint_color_selector" android:layout_gravity="center" - android:contentDescription="@string/accessibility_volume_settings" - android:background="@drawable/ripple_drawable_20dp" - android:tint="?android:attr/textColorSecondary" android:soundEffectsEnabled="false" /> + + <include layout="@layout/volume_dnd_icon" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginRight="@dimen/volume_dialog_stream_padding" + android:layout_marginTop="6dp"/> </FrameLayout> - </LinearLayout> - <FrameLayout - android:id="@+id/odi_captions" - android:layout_width="@dimen/volume_dialog_caption_size" - android:layout_height="@dimen/volume_dialog_caption_size" - android:layout_marginRight="68dp" - android:gravity="right" - android:layout_gravity="right" - android:clipToPadding="false" - android:translationZ="@dimen/volume_dialog_elevation" - android:background="@drawable/rounded_bg_full"> - <com.android.systemui.volume.CaptionsToggleImageButton - android:id="@+id/odi_captions_icon" - android:src="@drawable/ic_volume_odi_captions_disabled" - style="@style/VolumeButtons" - android:background="@drawable/rounded_ripple" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:tint="@color/caption_tint_color_selector" - android:layout_gravity="center" - android:soundEffectsEnabled="false" - sysui:optedOut="false"/> - </FrameLayout> + <LinearLayout + android:id="@+id/main" + android:layout_width="wrap_content" + android:minWidth="@dimen/volume_dialog_panel_width" + android:layout_height="wrap_content" + android:gravity="right" + android:layout_gravity="right" + android:orientation="vertical" + android:clipChildren="false" + android:clipToPadding="false"> + <LinearLayout + android:id="@+id/volume_dialog_rows" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minWidth="@dimen/volume_dialog_panel_width" + android:gravity="center" + android:orientation="horizontal"> + <!-- volume rows added and removed here! :-) --> + </LinearLayout> + <FrameLayout + android:id="@+id/settings_container" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <com.android.keyguard.AlphaOptimizedImageButton + android:id="@+id/settings" + android:src="@drawable/horizontal_ellipsis" + android:layout_width="@dimen/volume_dialog_tap_target_size" + android:layout_height="@dimen/volume_dialog_tap_target_size" + android:layout_gravity="center" + android:contentDescription="@string/accessibility_volume_settings" + android:background="@drawable/ripple_drawable_20dp" + android:tint="?android:attr/colorBackgroundFloating" + android:soundEffectsEnabled="false" /> + </FrameLayout> + </LinearLayout> + + </LinearLayout> <ViewStub android:id="@+id/odi_captions_tooltip_stub" diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml index 73beefc9da83..d996cee4b39e 100644 --- a/packages/SystemUI/res/layout/media_output_dialog.xml +++ b/packages/SystemUI/res/layout/media_output_dialog.xml @@ -32,7 +32,8 @@ android:id="@+id/header_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingEnd="@dimen/media_output_dialog_header_icon_padding"/> + android:paddingEnd="@dimen/media_output_dialog_header_icon_padding" + android:importantForAccessibility="no"/> <LinearLayout android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/navigation_layout.xml b/packages/SystemUI/res/layout/navigation_layout.xml index 0e576fbe2245..64c7422c27ed 100644 --- a/packages/SystemUI/res/layout/navigation_layout.xml +++ b/packages/SystemUI/res/layout/navigation_layout.xml @@ -30,7 +30,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" - android:clipToPadding="false"> + android:clipToPadding="false" + systemui:isVertical="false"> <LinearLayout android:id="@+id/ends_group" diff --git a/packages/SystemUI/res/layout/navigation_layout_vertical.xml b/packages/SystemUI/res/layout/navigation_layout_vertical.xml index 4b6770042632..42e93249e95f 100644 --- a/packages/SystemUI/res/layout/navigation_layout_vertical.xml +++ b/packages/SystemUI/res/layout/navigation_layout_vertical.xml @@ -30,7 +30,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" - android:clipToPadding="false"> + android:clipToPadding="false" + systemui:isVertical="true"> <com.android.systemui.navigationbar.buttons.ReverseLinearLayout android:id="@+id/ends_group" diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml deleted file mode 100644 index 2b33e17a5fbd..000000000000 --- a/packages/SystemUI/res/layout/pip_menu_activity.xml +++ /dev/null @@ -1,93 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/background" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <!-- Menu layout --> - <FrameLayout - android:id="@+id/menu_container" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:forceHasOverlappingRendering="false" - android:accessibilityTraversalAfter="@id/dismiss"> - - <!-- The margins for this container is calculated in the code depending on whether the - actions_container is visible. --> - <FrameLayout - android:id="@+id/expand_container" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <ImageButton - android:id="@+id/expand_button" - android:layout_width="60dp" - android:layout_height="60dp" - android:layout_gravity="center" - android:contentDescription="@string/pip_phone_expand" - android:padding="10dp" - android:src="@drawable/pip_expand" - android:background="?android:selectableItemBackgroundBorderless" /> - </FrameLayout> - - <FrameLayout - android:id="@+id/actions_container" - android:layout_width="match_parent" - android:layout_height="@dimen/pip_action_size" - android:layout_gravity="bottom" - android:visibility="invisible"> - <LinearLayout - android:id="@+id/actions_group" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_gravity="center_horizontal" - android:orientation="horizontal" - android:divider="@android:color/transparent" - android:showDividers="middle" /> - </FrameLayout> - </FrameLayout> - - <ImageButton - android:id="@+id/settings" - android:layout_width="@dimen/pip_action_size" - android:layout_height="@dimen/pip_action_size" - android:layout_gravity="top|start" - android:padding="@dimen/pip_action_padding" - android:contentDescription="@string/pip_phone_settings" - android:src="@drawable/ic_settings" - android:background="?android:selectableItemBackgroundBorderless" /> - - <ImageButton - android:id="@+id/dismiss" - android:layout_width="@dimen/pip_action_size" - android:layout_height="@dimen/pip_action_size" - android:layout_gravity="top|end" - android:padding="@dimen/pip_action_padding" - android:contentDescription="@string/pip_phone_close" - android:src="@drawable/ic_close_white" - android:background="?android:selectableItemBackgroundBorderless" /> - - <!--TODO (b/156917828): Add content description for a11y purposes?--> - <ImageButton - android:id="@+id/resize_handle" - android:layout_width="@dimen/pip_resize_handle_size" - android:layout_height="@dimen/pip_resize_handle_size" - android:layout_gravity="top|start" - android:layout_margin="@dimen/pip_resize_handle_margin" - android:src="@drawable/pip_resize_handle" - android:background="?android:selectableItemBackgroundBorderless" /> -</FrameLayout> diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index 0822947e8b16..93dd1a12f147 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -81,6 +81,22 @@ android:layout_height="match_parent" android:layout_weight="@integer/qs_footer_actions_weight" android:gravity="center_vertical|end" > + + <com.android.systemui.statusbar.AlphaOptimizedImageView + android:id="@+id/pm_lite" + android:layout_width="@dimen/qs_footer_action_button_size" + android:layout_height="@dimen/qs_footer_action_button_size" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:clickable="true" + android:clipToPadding="false" + android:contentDescription="@string/accessibility_quick_settings_edit" + android:focusable="true" + android:padding="@dimen/qs_footer_icon_padding" + android:src="@*android:drawable/ic_lock_power_off" + android:tint="?android:attr/colorForeground" + android:visibility="gone" + /> + <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch" android:layout_width="@dimen/qs_footer_action_button_size" diff --git a/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml b/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml index ee54f1da8897..c83077371bb0 100644 --- a/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml +++ b/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml @@ -14,4 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. --> -<include layout="@layout/qs_paged_page" /> +<com.android.systemui.qs.SideLabelTileLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/tile_page" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipChildren="false" + android:clipToPadding="false" /> diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml index 571cbbcc77db..1ce87c1c0236 100644 --- a/packages/SystemUI/res/layout/qs_tile_label.xml +++ b/packages/SystemUI/res/layout/qs_tile_label.xml @@ -20,7 +20,6 @@ android:layout_height="wrap_content" android:clipChildren="false" android:clipToPadding="false" - android:minHeight="48dp" android:paddingTop="12dp"> <LinearLayout android:id="@+id/label_group" diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index a8ef7c346b95..bbb6107d149e 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -37,12 +37,6 @@ android:layout_height="match_parent" android:layout_width="match_parent" /> - <ViewStub - android:id="@+id/keyguard_user_switcher_stub" - android:layout="@layout/keyguard_user_switcher" - android:layout_height="match_parent" - android:layout_width="match_parent" /> - <include layout="@layout/keyguard_status_view" android:visibility="gone" /> @@ -61,19 +55,21 @@ android:id="@+id/qs_frame" android:layout="@layout/qs_panel" android:layout_width="@dimen/qs_panel_width" - android:layout_height="match_parent" + android:layout_height="0dp" android:clipToPadding="false" android:clipChildren="false" systemui:viewType="com.android.systemui.plugins.qs.QS" systemui:layout_constraintStart_toStartOf="parent" systemui:layout_constraintEnd_toEndOf="parent" + systemui:layout_constraintTop_toTopOf="parent" + systemui:layout_constraintBottom_toBottomOf="parent" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/qs_edge_guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" - systemui:layout_constraintGuide_percent="0.5" + systemui:layout_constraintGuide_percent="0.4" android:orientation="vertical"/> <com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout @@ -109,5 +105,11 @@ layout="@layout/keyguard_bottom_area" android:visibility="gone" /> + <ViewStub + android:id="@+id/keyguard_user_switcher_stub" + android:layout="@layout/keyguard_user_switcher" + android:layout_height="match_parent" + android:layout_width="match_parent" /> + <include layout="@layout/status_bar_expanded_plugin_frame"/> </com.android.systemui.statusbar.phone.NotificationPanelView> diff --git a/packages/SystemUI/res/layout/text_toast.xml b/packages/SystemUI/res/layout/text_toast.xml index de4e062805fe..ad558d8053fb 100644 --- a/packages/SystemUI/res/layout/text_toast.xml +++ b/packages/SystemUI/res/layout/text_toast.xml @@ -20,32 +20,30 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:maxWidth="@dimen/toast_width" android:orientation="horizontal" - android:background="@drawable/toast_background" - android:backgroundTint="?android:attr/colorBackground" + android:gravity="center_vertical" + android:maxWidth="@*android:dimen/toast_width" + android:background="@android:drawable/toast_frame" android:layout_marginEnd="16dp" android:layout_marginStart="16dp" - android:gravity="center_vertical"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> - <!-- Icon should be 24x24, make slightly larger to allow for shadowing, adjust via padding --> <ImageView android:id="@+id/icon" - android:alpha="@dimen/toast_icon_alpha" - android:padding="11.5dp" - android:layout_width="@dimen/toast_icon_size" - android:layout_height="@dimen/toast_icon_size"/> + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginTop="10dp" + android:layout_marginBottom="10dp" + android:layout_marginEnd="10dp"/> <TextView android:id="@+id/text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:ellipsize="end" android:maxLines="2" android:paddingTop="12dp" android:paddingBottom="12dp" - android:paddingStart="0dp" - android:paddingEnd="22dp" - android:textSize="@dimen/toast_text_size" - android:textColor="?android:attr/textColorPrimary" - android:fontFamily="@*android:string/config_headlineFontFamily" - android:layout_width="match_parent" - android:layout_height="wrap_content"/> + android:lineHeight="20sp" + android:textAppearance="@*android:style/TextAppearance.Toast"/> </LinearLayout> diff --git a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml deleted file mode 100644 index b62018d7cb9e..000000000000 --- a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml +++ /dev/null @@ -1,38 +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. - --> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:padding="12dp"> - - <FrameLayout - android:layout_width="34dp" - android:layout_height="24dp" - android:layout_gravity="center" - android:background="@drawable/tv_rect_shadow_rounded"> - - <ImageView - android:layout_width="13dp" - android:layout_height="13dp" - android:layout_gravity="center" - android:src="@drawable/tv_ic_mic_white"/> - - </FrameLayout> - -</LinearLayout> diff --git a/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml new file mode 100644 index 000000000000..dff148b2c570 --- /dev/null +++ b/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml @@ -0,0 +1,35 @@ +<?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. + --> + + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="12dp" + android:layout_gravity="center"> + + <LinearLayout + android:id="@+id/icons_container" + android:background="@drawable/tv_rect_shadow_rounded" + android:padding="@dimen/privacy_chip_icon_padding" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:orientation="horizontal"/> + +</FrameLayout> + diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index 1810c196c83d..6aac5a34821b 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -21,6 +21,8 @@ android:layout_height="wrap_content" android:gravity="right" android:layout_gravity="right" + android:paddingRight="@dimen/volume_dialog_stream_padding" + android:clipToPadding="false" android:background="@android:color/transparent" android:theme="@style/volume_dialog_theme"> @@ -34,13 +36,15 @@ android:layout_gravity="right" android:background="@android:color/transparent" android:paddingRight="@dimen/volume_dialog_panel_transparent_padding_right" - android:paddingTop="@dimen/volume_dialog_panel_transparent_padding" - android:paddingBottom="@dimen/volume_dialog_panel_transparent_padding" android:paddingLeft="@dimen/volume_dialog_panel_transparent_padding" android:orientation="vertical" - android:clipToPadding="false"> + android:clipToPadding="false" + android:clipChildren="false"> + + <include layout="@layout/volume_ringer_drawer" /> <FrameLayout + android:visibility="gone" android:id="@+id/ringer" android:layout_width="@dimen/volume_dialog_ringer_size" android:layout_height="@dimen/volume_dialog_ringer_size" @@ -77,10 +81,8 @@ android:gravity="right" android:layout_gravity="right" android:orientation="vertical" - android:translationZ="@dimen/volume_dialog_elevation" android:clipChildren="false" - android:clipToPadding="false" - android:background="@drawable/rounded_bg_full" > + android:clipToPadding="false" > <LinearLayout android:id="@+id/volume_dialog_rows" android:layout_width="wrap_content" @@ -88,24 +90,22 @@ android:minWidth="@dimen/volume_dialog_panel_width" android:gravity="center" android:orientation="horizontal" - android:paddingRight="@dimen/volume_dialog_stream_padding" - android:paddingLeft="@dimen/volume_dialog_stream_padding"> + android:layout_marginTop="@dimen/volume_row_slider_padding_start"> <!-- volume rows added and removed here! :-) --> </LinearLayout> <FrameLayout android:id="@+id/settings_container" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/rounded_bg_bottom_background"> + android:layout_height="wrap_content"> <com.android.keyguard.AlphaOptimizedImageButton android:id="@+id/settings" - android:src="@drawable/ic_tune_black_16dp" + android:src="@drawable/horizontal_ellipsis" android:layout_width="@dimen/volume_dialog_tap_target_size" android:layout_height="@dimen/volume_dialog_tap_target_size" android:layout_gravity="center" android:contentDescription="@string/accessibility_volume_settings" android:background="@drawable/ripple_drawable_20dp" - android:tint="?android:attr/textColorPrimary" + android:tint="?android:attr/colorBackgroundFloating" android:soundEffectsEnabled="false" /> </FrameLayout> </LinearLayout> @@ -118,7 +118,6 @@ android:gravity="right" android:layout_gravity="right" android:clipToPadding="false" - android:translationZ="@dimen/volume_dialog_elevation" android:background="@drawable/rounded_bg_full"> <com.android.systemui.volume.CaptionsToggleImageButton android:id="@+id/odi_captions_icon" @@ -127,7 +126,7 @@ android:background="@drawable/rounded_ripple" android:layout_width="match_parent" android:layout_height="match_parent" - android:tint="?android:attr/textColorPrimary" + android:tint="?android:attr/colorAccent" android:layout_gravity="center" android:soundEffectsEnabled="false" sysui:optedOut="false"/> diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml index b9efc5be70c1..fda59b50104a 100644 --- a/packages/SystemUI/res/layout/volume_dialog_row.xml +++ b/packages/SystemUI/res/layout/volume_dialog_row.xml @@ -20,11 +20,12 @@ android:layout_width="@dimen/volume_dialog_panel_width" android:clipChildren="false" android:clipToPadding="false" + android:translationZ="@dimen/volume_dialog_elevation" android:theme="@style/volume_dialog_theme"> <LinearLayout android:layout_height="wrap_content" - android:layout_width="match_parent" + android:layout_width="@dimen/volume_dialog_panel_width" android:gravity="center" android:layout_gravity="center" android:orientation="vertical" > @@ -41,21 +42,23 @@ <FrameLayout android:id="@+id/volume_row_slider_frame" android:layout_width="match_parent" - android:layout_marginTop="@dimen/volume_dialog_slider_margin_top" - android:layout_marginBottom="@dimen/volume_dialog_slider_margin_bottom" - android:layoutDirection="rtl" - android:layout_height="@dimen/volume_dialog_slider_height"> + android:layout_height="@dimen/volume_row_slider_height"> + <include layout="@layout/volume_dnd_icon"/> <SeekBar android:id="@+id/volume_row_slider" + android:paddingLeft="0dp" + android:paddingRight="0dp" + android:paddingStart="0dp" + android:paddingEnd="0dp" android:clickable="true" - android:layout_width="@dimen/volume_dialog_slider_height" + android:layout_width="@dimen/volume_row_slider_height" android:layout_height="match_parent" - android:layoutDirection="rtl" android:layout_gravity="center" - android:rotation="90" /> + android:rotation="270" /> </FrameLayout> <com.android.keyguard.AlphaOptimizedImageButton + android:visibility="gone" android:id="@+id/volume_row_icon" style="@style/VolumeButtons" android:layout_width="@dimen/volume_dialog_tap_target_size" @@ -66,6 +69,4 @@ android:soundEffectsEnabled="false" /> </LinearLayout> - <include layout="@layout/volume_dnd_icon"/> - </FrameLayout> diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml new file mode 100644 index 000000000000..d6e1782382fa --- /dev/null +++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml @@ -0,0 +1,126 @@ +<?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. +--> + +<!-- Contains the active ringer icon and a hidden drawer containing all three ringer options. --> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:clipToPadding="false" + android:clipChildren="false"> + + <!-- Drawer view, invisible by default. --> + <FrameLayout + android:id="@+id/volume_drawer_container" + android:alpha="0.0" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/volume_drawer_bg" + android:orientation="vertical"> + + <!-- View that is animated to a tapped ringer selection, so it appears selected. --> + <FrameLayout + android:id="@+id/volume_drawer_selection_background" + android:alpha="0.0" + android:layout_width="@dimen/volume_ringer_drawer_item_size" + android:layout_height="@dimen/volume_ringer_drawer_item_size" + android:layout_gravity="bottom|right" + android:background="@drawable/volume_drawer_selection_bg" /> + + <LinearLayout + android:id="@+id/volume_drawer_options" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <FrameLayout + android:id="@+id/volume_drawer_vibrate" + android:layout_width="@dimen/volume_ringer_drawer_item_size" + android:layout_height="@dimen/volume_ringer_drawer_item_size" + android:description="@string/volume_ringer_hint_vibrate" + android:gravity="center"> + + <ImageView + android:id="@+id/volume_drawer_vibrate_icon" + android:layout_width="@dimen/volume_ringer_drawer_icon_size" + android:layout_height="@dimen/volume_ringer_drawer_icon_size" + android:layout_gravity="center" + android:tint="?android:attr/colorAccent" + android:src="@drawable/ic_volume_ringer_vibrate" /> + + </FrameLayout> + + <FrameLayout + android:id="@+id/volume_drawer_mute" + android:layout_width="@dimen/volume_ringer_drawer_item_size" + android:layout_height="@dimen/volume_ringer_drawer_item_size" + android:description="@string/volume_ringer_hint_mute" + android:gravity="center"> + + <ImageView + android:id="@+id/volume_drawer_mute_icon" + android:layout_width="@dimen/volume_ringer_drawer_icon_size" + android:layout_height="@dimen/volume_ringer_drawer_icon_size" + android:layout_gravity="center" + android:tint="?android:attr/colorAccent" + android:src="@drawable/ic_volume_ringer_mute" /> + + </FrameLayout> + + <FrameLayout + android:id="@+id/volume_drawer_normal" + android:layout_width="@dimen/volume_ringer_drawer_item_size" + android:layout_height="@dimen/volume_ringer_drawer_item_size" + android:description="@string/volume_ringer_hint_unmute" + android:gravity="center"> + + <ImageView + android:id="@+id/volume_drawer_normal_icon" + android:layout_width="@dimen/volume_ringer_drawer_icon_size" + android:layout_height="@dimen/volume_ringer_drawer_icon_size" + android:layout_gravity="center" + android:tint="?android:attr/colorAccent" + android:src="@drawable/ic_volume_ringer" /> + + </FrameLayout> + + </LinearLayout> + + </FrameLayout> + + <!-- The current ringer selection. When the drawer is opened, this animates to the corresponding + position in the drawer. When the drawer is closed, it animates back. --> + <FrameLayout + android:id="@+id/volume_new_ringer_active_icon_container" + android:layout_width="@dimen/volume_ringer_drawer_item_size" + android:layout_height="@dimen/volume_ringer_drawer_item_size" + android:layout_gravity="bottom|right" + android:description="@string/volume_ringer_change" + android:background="@drawable/volume_drawer_selection_bg"> + + <ImageView + android:id="@+id/volume_new_ringer_active_icon" + android:layout_width="@dimen/volume_ringer_drawer_icon_size" + android:layout_height="@dimen/volume_ringer_drawer_icon_size" + android:layout_gravity="center" + android:tint="?android:attr/colorBackgroundFloating" + android:src="@drawable/ic_volume_media" /> + + </FrameLayout> + +</FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 5db7c25835c8..c3a62b05db20 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Rollees skermskoot"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Maak skermkiekie toe"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Skermkiekievoorskou"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Boonste grens"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Onderste grens"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Skermopnemer"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Verwerk tans skermopname"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Deurlopende kennisgewing vir \'n skermopnamesessie"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Tot sonsopkoms"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aan om <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Tot <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is gedeaktiveer"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is geaktiveer"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ontsluit om NFC te gebruik"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Hierdie toestel behoort aan jou organisasie"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Hierdie toestel behoort aan <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Swiep vanaf ikoon vir foon"</string> <string name="voice_hint" msgid="7476017460191291417">"Swiep vanaf ikoon vir stembystand"</string> <string name="camera_hint" msgid="4519495795000658637">"Swiep vanaf ikoon vir kamera"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Wys profiel"</string> <string name="user_add_user" msgid="4336657383006913022">"Voeg gebruiker by"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuwe gebruiker"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Beëindig gastesessie?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Beëindig gastesessie"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Verwyder gas?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Beëindig sessie"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwyder"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gas!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wiil jy jou sessie voortsit?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Begin van voor af"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Jou organisasie het \'n sertifikaatoutoriteit in jou werkprofiel geïnstalleer. Jou veilige netwerkverkeer kan gemonitor of gewysig word."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"\'n Sertifikaatoutoriteit is op hierdie toestel geïnstalleer. Jou veilige netwerkverkeer kan gemonitor of gewysig word."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Jou administrateur het netwerkloginskrywing aangeskakel, wat verkeer op jou toestel monitor."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Jou administrateur het netwerkloglêers aangeskakel wat verkeer in jou werkprofiel maar nie in jou persoonlike profiel monitor nie."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Jy is gekoppel aan <xliff:g id="VPN_APP">%1$s</xliff:g>, wat jou netwerkaktiwiteit, insluitend e-posse, programme en webwerwe, kan monitor."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Jy is gekoppel aan <xliff:g id="VPN_APP_0">%1$s</xliff:g> en <xliff:g id="VPN_APP_1">%2$s</xliff:g>, wat jou netwerkaktiwiteit, insluitend e-posse, programme en webwerwe, kan monitor."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Jou werkprofiel is gekoppel aan <xliff:g id="VPN_APP">%1$s</xliff:g>, wat jou netwerkaktiwiteit, insluitend e-posse, programme en webwerwe, kan monitor."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te demp. Toeganklikheidsdienste kan dalk gedemp wees."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tik om op vibreer te stel."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tik om te demp."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"demp"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ontdemp"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreer"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Hierdie kennisgewing is outomaties deur die stelsel <b>na Stil gedegradeer</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Hierdie kennisgewing is outomaties <b>hoër gegradeer</b> in jou skakering."</string> <string name="feedback_demoted" msgid="951884763467110604">"Hierdie kennisgewing is outomaties <b>laer gegradeer</b> in jou skakering."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Is dit korrek?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Gee jou terugvoer aan die ontwikkelaar deur. Is dit korrek?"</string> <string name="feedback_response" msgid="4671729244976641339">"Dankie vir jou terugvoer!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kennisgewingkontroles vir <xliff:g id="APP_NAME">%1$s</xliff:g> is oopgemaak"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Skuif na <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Voeg by posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Teël is bygevoeg"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Teël is verwyder"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kitsinstellingswysiger."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-kennisgewing: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Maak instellings oop."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> gebruik tans die <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> het onlangs die <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> gebruik"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(onderneming)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Oproep"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Oproep"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(deur <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"ligging"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors is af"</string> <string name="device_services" msgid="1549944177856658705">"Toesteldienste"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Titelloos"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Tik om hierdie program te herbegin en maak volskerm oop."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Beweeg"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Stelselnavigasie is opgedateer. Gaan na Instellings toe om veranderinge te maak."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Gaan na Instellings toe om stelselnavigasie op te dateer"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Voeg by"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Voorgestel deur <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Kontroles opgedateer"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Toestel is gesluit"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN bevat letters of simbole"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verifieer <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Verkeerde PIN"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 1af18fee0fb9..0c0487dd8c10 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"ቅጽበታዊ ገጽ ዕይታን ይሸብልሉ"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ቅጽበታዊ ገጽ ዕይታን አሰናብት"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"የቅጽበታዊ ገጽ ዕይታ ቅድመ-ዕይታ"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"የላይኛው ወሰን"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"የታችኛው ወሰን"</string> <string name="screenrecord_name" msgid="2596401223859996572">"የማያ መቅጃ"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"የማያ ገጽ ቀረጻን በማሰናዳት ላይ"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"ለአንድ የማያ ገጽ ቀረጻ ክፍለ-ጊዜ በመካሄድ ያለ ማሳወቂያ"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ጸሐይ እስክትወጣ ድረስ"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> ላይ ይበራል"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"እስከ <xliff:g id="TIME">%s</xliff:g> ድረስ"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"ኤንኤፍሲ"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"ኤንኤፍሲ ተሰናክሏል"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"ኤንኤፍሲ ነቅቷል"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCን ለመጠቀም ይክፈቱ"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"ይህ መሣሪያ የድርጅትዎ ነው"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"ይህ መሳሪያ ንብረትነቱ የ<xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ነው"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ለስልክ ከአዶ ላይ ጠረግ ያድርጉ"</string> <string name="voice_hint" msgid="7476017460191291417">"ለድምጽ ረዳት ከአዶ ጠረግ ያድርጉ"</string> <string name="camera_hint" msgid="4519495795000658637">"ለካሜራ ከአዶ ላይ ጠረግ ያድርጉ"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"መገለጫ አሳይ"</string> <string name="user_add_user" msgid="4336657383006913022">"ተጠቃሚ አክል"</string> <string name="user_new_user_name" msgid="2019166282704195789">"አዲስ ተጠቃሚ"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"የእንግዳ ክፍለ-ጊዜ ይብቃ?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"የእንግዳ ክፍለ-ጊዜ ጨርስ"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"እንግዳ ይወገድ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ክፍለ-ጊዜን አብቃ"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"አስወግድ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"እንኳን በደህና ተመለሱ እንግዳ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ክፍለ-ጊዜዎን መቀጠል ይፈልጋሉ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"እንደገና ጀምር"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"የእርስዎ ድርጅት የእውቅና ማረጋገጫ ሰጪ ባለሥልጣን በእርስዎ የሥራ መገለጫ ላይ ጭኗል። የእርስዎ ደኅንነቱ የተጠበቀ አውታረ መረብ ትራፊክ ክትትል ሊደረግበት እና ሊሻሻል ይችላል።"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"የእውቅና ማረጋገጫ ሰጪ ባለሥልጣን በዚህ መሣሪያ ላይ ተጭኗል። የእርስዎ ደኅንነቱ የተጠበቀ አውታረ መረብ ትራፊክ ክትትል ሊደረግበት እና ሊሻሻል ይችላል።"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"የእርስዎ አስተዳዳሪ የአውታረ መረብ ምዝግብ ማስታወሻ መያዝን አብርተዋል፣ ይህም በመሣሪያዎ ላይ ያለውን ትራፊክ ይከታተላል።"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"የእርስዎ አስተዳዳሪ በስራ መገለጫዎ ውስጥ፣ ግን በግል መገለጫዎ ላይ ሳይሆን፣ ትራፊክን የሚቆጣጠር የአውታረ መረብ ምዝግብ ማስታወሻ አብርተዋል።"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"እርስዎ ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የግል የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችለው <xliff:g id="VPN_APP">%1$s</xliff:g> ጋር ተገናኝተዋል።"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የግል የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችሉት <xliff:g id="VPN_APP_0">%1$s</xliff:g> እና <xliff:g id="VPN_APP_1">%2$s</xliff:g> ጋር ተገናኝተዋል።"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"የእርስዎ የሥራ መገለጫ የእርስዎን ኢሜይሎችን፣ መተግበሪያዎችን እና ድር ጣቢያዎችንም ጨምሮ የግል የአውታረ መረብ እንቅስቃሴዎን መከታተል ከሚችለው <xliff:g id="VPN_APP">%1$s</xliff:g> ጋር ተገናኝቷል።"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s። ወደ ንዝረት ለማቀናበር መታ ያድርጉ።"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ።"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ድምጸ-ከል አድርግ"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ድምጸ-ከልን አንሳ"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ንዘር"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ይህ ማሳወቂያ በሥርዓቱ በራስ-ሰር <b>ወደ ዝምታ ዝቅ ተደርጓል </b>።"</string> <string name="feedback_promoted" msgid="2125562787759780807">"ይህ ማሳወቂያ በራስ-ሰር በጥላው ውስጥ <b>ከፍተኛ ደረጃ ተሰጥቶታል</b>።"</string> <string name="feedback_demoted" msgid="951884763467110604">"ይህ ማሳወቂያ በራስ-ሰር በጥላው ውስጥ <b>ዝቅተኛ ደረጃ ተሰጥቶታል</b>።"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"ይህ ትክክል ነበር?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"ግብረመልስዎን ገንቢው እንዲያውቅ ያድርጉ። ይህ ትክክል ነበር?"</string> <string name="feedback_response" msgid="4671729244976641339">"ለግብረመልስዎ እናመሰግናለን!"</string> <string name="feedback_ok" msgid="6481426753298857144">"እሺ"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"የ<xliff:g id="APP_NAME">%1$s</xliff:g> ማሳወቂያ መቆጣጠሪያዎች ተከፍተዋል"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ውሰድ"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ቦታ አክል"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"የ<xliff:g id="POSITION">%1$d</xliff:g> አቀማመጥ"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ሰቅ ታክሏል"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ሰቅ ተወግዷል"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"የፈጣን ቅንብሮች አርታዒ።"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"የ<xliff:g id="ID_1">%1$s</xliff:g> ማሳወቂያ፦ <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ቅንብሮችን ክፈት።"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>ን እየተጠቀመ ነው"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> በቅርብ ጊዜ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>ን ይጠቀማል።"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ድርጅት)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"የስልክ ጥሪ"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"የስልክ ጥሪ"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(እስከ <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"ካሜራ"</string> <string name="privacy_type_location" msgid="7991481648444066703">"አካባቢ"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"ዳሳሾች ጠፍተዋል"</string> <string name="device_services" msgid="1549944177856658705">"የመሣሪያ አገልግሎቶች"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"ርዕስ የለም"</string> - <string name="restart_button_description" msgid="6916116576177456480">"ይህን መተግበሪያ እንደገና ለማስጀመር መታ ያድርጉ እና ወደ ሙሉ ማያ ገጽ ይሂዱ።"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"አንቀሳቅስ"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"የስርዓት ዳሰሳ ተዘምኗል። ለውጦችን ለማድረግ ወደ ቅንብሮች ይሂዱ።"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"የስርዓት ዳሰሳን ለማዘመን ወደ ቅንብሮች ይሂዱ"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"አክል"</string> <string name="controls_dialog_message" msgid="342066938390663844">"በ<xliff:g id="APP">%s</xliff:g> የተጠቆመ"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"መቆጣጠሪያዎች ተዘምነዋል"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"መሣሪያ ተቆልፏል"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"ፒን ፊደሎችን ወይም ምልክቶችን ይይዛል"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> አረጋግጥ"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"የተሳሳተ ፒን"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index b34b39a765b1..ae8df906c974 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"لقطة شاشة موصولة"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"إغلاق لقطة الشاشة"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"معاينة لقطة الشاشة"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"الحد العلوي"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"الحد السفلي"</string> <string name="screenrecord_name" msgid="2596401223859996572">"مسجّل الشاشة"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"جارٍ معالجة تسجيل الشاشة"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"إشعار مستمر لجلسة تسجيل شاشة"</string> @@ -418,6 +420,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"حتى شروق الشمس"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"تفعيل الوضع في <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"حتى <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"تم إيقاف الاتصال القريب المدى"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"تم تفعيل الاتصال القريب المدى"</string> @@ -448,9 +452,11 @@ <string name="notification_tap_again" msgid="4477318164947497249">"انقر مرة أخرى للفتح"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"يمكنك الفتح بالتمرير سريعًا لأعلى."</string> <string name="keyguard_retry" msgid="886802522584053523">"مرِّر سريعًا للأعلى لإعادة المحاولة."</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"يجب فتح قفل الشاشة لاستخدام تقنية الاتصال قصير المدى (NFC)."</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"افتح قفل الشاشة لاستخدام تقنية NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"هذا الجهاز يخص مؤسستك."</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"يمكنك التمرير سريعًا من الرمز لتشغيل الهاتف"</string> <string name="voice_hint" msgid="7476017460191291417">"يمكنك التمرير سريعًا من الرمز لتشغيل المساعد الصوتي"</string> <string name="camera_hint" msgid="4519495795000658637">"يمكنك التمرير سريعًا من الرمز لتشغيل الكاميرا"</string> @@ -471,9 +477,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"عرض الملف الشخصي"</string> <string name="user_add_user" msgid="4336657383006913022">"إضافة مستخدم"</string> <string name="user_new_user_name" msgid="2019166282704195789">"مستخدم جديد"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"هل تريد إنهاء جلسة الضيف؟"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"إنهاء جلسة الضيف"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"هل تريد إزالة جلسة الضيف؟"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"إنهاء الجلسة"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"إزالة"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مرحبًا بك مجددًا في جلسة الضيف"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"هل تريد متابعة جلستك؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"البدء من جديد"</string> @@ -552,6 +559,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ثبّتت مؤسستك مرجعًا مصدّقًا في ملفك الشخصي للعمل. قد تتم مراقبة حركة بيانات شبكتك الآمنة أو تعديلها."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"تم تثبيت مرجع مصدّق على هذا الجهاز. قد تتم مراقبة حركة بيانات شبكتك الآمنة أو تعديلها."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"شغَّل المشرف ميزة تسجيل بيانات الشبكة، والتي يتم من خلالها مراقبة حركة البيانات على جهازك."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"شغَّل المشرف ميزة تسجيل بيانات الشبكة، والتي يتم من خلالها مراقبة حركة البيانات في ملفك الشخصي للعمل ولكن لا تتم مراقبتها في ملفك الشخصي."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"لقد اتصلت بتطبيق <xliff:g id="VPN_APP">%1$s</xliff:g>، الذي يمكن أن يراقب نشاط شبكتك، بما في ذلك رسائل البريد الإلكتروني والتطبيقات والمواقع الإلكترونية."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"لقد اتصلت بتطبيق <xliff:g id="VPN_APP_0">%1$s</xliff:g> و<xliff:g id="VPN_APP_1">%2$s</xliff:g> اللذين يمكنهما مراقبة نشاط شبكتك، بما في ذلك رسائل البريد الإلكتروني والتطبيقات والمواقع الإلكترونية."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"تم ربط الملف الشخصي للعمل بـ <xliff:g id="VPN_APP">%1$s</xliff:g>، الذي يمكنه مراقبة أنشطتك الشخصية على الشبكة، بما في ذلك الرسائل الإلكترونية والتطبيقات ومواقع الويب."</string> @@ -632,6 +640,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. انقر للتجاهل. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. انقر للتعيين على الاهتزاز."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. انقر لكتم الصوت."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"كتم الصوت"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"إعادة الصوت"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"اهتزاز"</string> @@ -745,7 +755,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"تم تلقائيًا <b>خفض ترتيب هذا الإشعار إلى الوضع صامت</b> من خلال النظام."</string> <string name="feedback_promoted" msgid="2125562787759780807">"تم تلقائيًا <b>زيادة ترتيب</b> هذا الإشعار في مركز الإشعارات."</string> <string name="feedback_demoted" msgid="951884763467110604">"تم تلقائيًا <b>خفض ترتيب</b> هذا الإشعار في مركز الإشعارات."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"هل كان هذا صحيحًا؟"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"أخبِر مطوِّر البرامج برأيك. هل كان هذا صحيحًا؟"</string> <string name="feedback_response" msgid="4671729244976641339">"شكرًا على تعليقك"</string> <string name="feedback_ok" msgid="6481426753298857144">"حسنًا"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"تم فتح عناصر التحكم في الإشعارات لتطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -900,6 +910,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"الانتقال إلى <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"الإضافة إلى الموضع <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"الموضع: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"تمت إضافة البطاقة."</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"تمت إزالة البطاقة."</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"برنامج تعديل الإعدادات السريعة."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"إشعار <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"فتح الإعدادات."</string> @@ -987,10 +999,10 @@ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"تستخدم التطبيقات <xliff:g id="TYPES_LIST">%s</xliff:g>."</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"، "</string> <string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" و "</string> - <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"يستخدم تطبيق <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> عملية <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> الآن."</string> - <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"استخدَم تطبيق <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> عملية <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> مؤخرًا."</string> + <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"يستخدم تطبيق <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ميزة <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> الآن."</string> + <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"استخدَم تطبيق <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ميزة <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> مؤخرًا."</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(للمؤسسات)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"مكالمة هاتفية"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"المكالمات الهاتفية"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(من خلال <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"الكاميرا"</string> <string name="privacy_type_location" msgid="7991481648444066703">"الموقع"</string> @@ -998,7 +1010,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"إيقاف أجهزة الاستشعار"</string> <string name="device_services" msgid="1549944177856658705">"خدمات الأجهزة"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"بلا عنوان"</string> - <string name="restart_button_description" msgid="6916116576177456480">"انقر لإعادة تشغيل هذا التطبيق والانتقال إلى وضع ملء الشاشة."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"نقل"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"تم تحديث التنقل داخل النظام. لإجراء التغييرات، يُرجى الانتقال إلى \"الإعدادات\"."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"الانتقال إلى \"الإعدادات\" لتعديل التنقل داخل النظام"</string> @@ -1056,6 +1067,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"إضافة"</string> <string name="controls_dialog_message" msgid="342066938390663844">"اقتراح من <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"تم تعديل عناصر التحكّم."</string> + <string name="controls_tile_locked" msgid="731547768182831938">"الجهاز مُقفل."</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"يشتمل رقم التعريف الشخصي على أحرف أو رموز."</string> <string name="controls_pin_verify" msgid="3452778292918877662">"إثبات ملكية <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"رقم تعريف شخصي خاطئ"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 88b2bb278995..1f925708b2b7 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"স্ক্ৰীনশ্বট স্ক্ৰ’ল কৰক"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"স্ক্ৰীনশ্বট অগ্ৰাহ্য কৰক"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্ৰীনশ্বটৰ পূৰ্বদৰ্শন"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"ওপৰৰ সীমাৰেখা"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"তলৰ সীমাৰেখা"</string> <string name="screenrecord_name" msgid="2596401223859996572">"স্ক্ৰীন ৰেকৰ্ডাৰ"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রীন ৰেকৰ্ডিঙৰ প্ৰক্ৰিয়াকৰণ হৈ আছে"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রীণ ৰেকৰ্ডিং ছেশ্বন চলি থকা সময়ত পোৱা জাননী"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"সূৰ্যোদয়লৈকে"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ত অন কৰক"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> পৰ্যন্ত"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC নিষ্ক্ৰিয় হৈ আছে"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC সক্ষম হৈ আছে"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ব্যৱহাৰ কৰিবলৈ আনলক কৰক"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"এই ডিভাইচটো আপোনাৰ প্ৰতিষ্ঠানৰ"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"এই ডিভাইচটো <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>ৰ"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ফ\'নৰ বাবে আইকনৰপৰা ছোৱাইপ কৰক"</string> <string name="voice_hint" msgid="7476017460191291417">"কণ্ঠধ্বনিৰে সহায়ৰ বাবে আইকনৰ পৰা ছোৱাইপ কৰক"</string> <string name="camera_hint" msgid="4519495795000658637">"কেমেৰা খুলিবলৈ আইকনৰপৰা ছোৱাইপ কৰক"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"প্ৰ\'ফাইল দেখুৱাওক"</string> <string name="user_add_user" msgid="4336657383006913022">"ব্যৱহাৰকাৰী যোগ কৰক"</string> <string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যৱহাৰকাৰী"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"অতিথিৰ ছেশ্বন সমাপ্ত কৰিবনে?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"অতিথিৰ ছেশ্বন সমাপ্ত কৰক"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"অতিথি আঁতৰাবনে?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ সকলো এপ্ আৰু ডেটা মচা হ\'ব।"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ছেশ্বন সমাপ্ত কৰক"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"আঁতৰাওক"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"আপোনাক পুনৰাই স্বাগতম জনাইছোঁ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপুনি আপোনাৰ ছেশ্বন অব্যাহত ৰাখিব বিচাৰেনে?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আকৌ আৰম্ভ কৰক"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"আপোনাৰ প্ৰতিষ্ঠানে আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইলটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰিছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"এই ডিভাইচটোত এটা প্ৰমাণপত্ৰ সম্পৰ্কীয় কৰ্তৃপক্ষ ইনষ্টল কৰা হৈছে। আপোনাৰ সুৰক্ষিত নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ বা সংশোধন কৰা হ\'ব পাৰে।"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"আপোনাৰ প্ৰশাসকে নেটৱৰ্ক লগিং অন কৰিছে, যিয়ে আপোনাৰ ডিভাইচটোত নেটৱৰ্ক ট্ৰেফিক পৰ্যবেক্ষণ কৰে।"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"আপোনাৰ প্ৰশাসকে নেটৱৰ্ক লগিং অন কৰিছে, যিয়ে আপোনাৰ কৰ্মস্থানৰ প্ৰ’ফাইলত ট্ৰেফিক নিৰীক্ষণ কৰে কিন্তু আপোনাৰ ব্যক্তিগত প্ৰ’ফাইলত নকৰে।"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"আপুনি <xliff:g id="VPN_APP">%1$s</xliff:g>ৰে সংযুক্ত হৈ আছে যিয়ে আপোনাৰ ইমেইল, এপ্ আৰু ৱেবছাইটকে ধৰি নেটৱর্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"আপুনি <xliff:g id="VPN_APP_0">%1$s</xliff:g> আৰু <xliff:g id="VPN_APP_1">%2$s</xliff:g>ৰে সংযুক্ত হৈ আছে, যিয়ে আপোনাৰ ইমেইল, এপ্ আৰু ৱেবছাইটকে ধৰি নেটৱর্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"আপুনি <xliff:g id="VPN_APP">%1$s</xliff:g>ৰে সংযুক্ত হৈ আছে যিয়ে আপোনাৰ ইমেইল, এপ্ আৰু ৱেবছাইটকে ধৰি নেটৱর্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। মিউট কৰিবলৈ টিপক। দিব্য়াংগসকলৰ বাবে থকা সেৱা মিউট হৈ থাকিব পাৰে।"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s। কম্পন অৱস্থাত ছেট কৰিবলৈ টিপক।"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s। মিউট কৰিবলৈ টিপক।"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট কৰক"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট কৰক"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"কম্পন কৰক"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ছিষ্টেমটোৱে স্বয়ংক্ৰিয়ভাৱে এই জাননীটোৰ ক্ষেত্ৰত দিয়া <b>গুৰুত্ব নীৰৱ</b>লৈ হ্ৰাস কৰিছে।"</string> <string name="feedback_promoted" msgid="2125562787759780807">"আপোনাৰ শ্বেডত স্বয়ংক্ৰিয়ভাৱে এই জাননীটোৰ <b>স্থান ওপৰলৈ</b> কৰা হৈছে।"</string> <string name="feedback_demoted" msgid="951884763467110604">"আপোনাৰ শ্বেডত স্বয়ংক্ৰিয়ভাৱে এই জাননীটোৰ <b>স্থান তললৈ</b> কৰা হৈছে।"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"এইটো শুদ্ধ আছিলনে?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"বিকাশকৰ্তাক আপোনাৰ মতামত জনাওক। এইটো শুদ্ধ আছিলনে?"</string> <string name="feedback_response" msgid="4671729244976641339">"আপোনাৰ মতামতৰ বাবে ধন্যবাদ!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ঠিক আছে"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ জাননী নিয়ন্ত্ৰণসমূহ খোলা অৱস্থাত আছে"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰলৈ স্থানান্তৰ কৰক"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থানত যোগ দিয়ক"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থান"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"টাইল যোগ দিয়া হৈছে"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"টাইল আঁতৰোৱা হৈছে"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ক্ষিপ্ৰ ছেটিংসমূহৰ সম্পাদক।"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> জাননী: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ছেটিংসমূহ খোলক।"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>এ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ব্যৱহাৰ কৰি আছে"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>এ শেহতীয়াকৈ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ব্যৱহাৰ কৰিছে"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(এণ্টাৰপ্ৰাইজ)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ফ’ন কল"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g>ৰ জৰিয়তে)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"কেমেৰা"</string> <string name="privacy_type_location" msgid="7991481648444066703">"অৱস্থান"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"ছেন্সৰ অফ হৈ আছে"</string> <string name="device_services" msgid="1549944177856658705">"ডিভাইচ সেৱা"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"কোনো শিৰোনাম নাই"</string> - <string name="restart_button_description" msgid="6916116576177456480">"এপ্টো ৰিষ্টাৰ্ট কৰক আৰু পূৰ্ণ স্ক্ৰীণ ব্যৱহাৰ কৰক।"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"আঁতৰাওক"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ছিষ্টেম নেভিগেশ্বন আপডে’ট কৰা হ’ল। সলনি কৰিবলৈ ছেটিংসমূহ-লৈ যাওক।"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ছিষ্টেম নেভিগেশ্বন আপডে’ট কৰিবলৈ ছেটিংসমূহ-লৈ যাওক"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"যোগ দিয়ক"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g>এ পৰামৰ্শ হিচাপে আগবঢ়োৱা"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"নিয়ন্ত্ৰণসমূহ আপডে\'ট কৰা হৈছে"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"ডিভাইচ লক হৈ আছে"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"পিনত বৰ্ণ অথবা প্ৰতীকসমূহ থাকে"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> সত্যাপন কৰক"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"ভুল পিন"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 6f1c347a5987..e813a901c608 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Sürüşdürülərək çəkilən skrinşot"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ekran şəklini ötürün"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran şəklinə önbaxış"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Yuxarı hüdud"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Aşağı hüdud"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Ekran Yazıcısı"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran çəkilişi emal edilir"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekranın video çəkimi ərzində silinməyən bildiriş"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Şəfəq vaxtına qədər"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Bu vaxt aktiv olur: <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Bu vaxtadək: <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC deaktiv edilib"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC aktiv edilib"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC istifadə etmək üçün kiliddən çıxarın"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Bu cihaz təşkilatınıza məxsusdur"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> təşkilatına məxsusdur"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Telefon üçün ikonadan sürüşdürün"</string> <string name="voice_hint" msgid="7476017460191291417">"Səs yardımçısı üçün ikonadan sürüşdürün"</string> <string name="camera_hint" msgid="4519495795000658637">"Kamera üçün ikonadan sürüşdürün"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"İstifadəçi əlavə edin"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yeni istifadəçi"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Qonaq sessiyası bitirilsin?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Qonaq sessiyasını bitirin"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Qonaq silinsin?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Sessiyanı bitirin"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Yığışdır"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Xoş gəlmisiniz!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Sessiya davam etsin?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Yenidən başlayın"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Təşkilat iş profilində sertifikat səlahiyyəti quraşdırdı. Təhlükəsiz şəbəkə ötürülməsinə nəzarət edilə və ya dəyişdirilə bilər."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Bu cihazda sertifikat səlahiyyəti quraşdırıldı. Təhlükəsiz şəbəkə ötürülməsinə nəzarət edilə və ya dəyişdirilə bilər."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Admin cihazda şəbəkə ötürülməsinə nəzarət edən şəbəkə qeydlərini aktiv etdi."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Admininiz şəxsi profilinizdəki deyil, iş profilinizdəki trafikə nəzarət edən şəbəkə qeydiyyatını aktiv edib."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"E-poçt, tətbiq və veb saytlar daxil olmaqla şəbəkə fəaliyyətinə nəzarət edən <xliff:g id="VPN_APP">%1$s</xliff:g> tətbiqinə qoşulusunuz."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"E-poçt, tətbiq və veb saytlar daxil olmaqla şəbəkə fəaliyyətinə nəzarət edən <xliff:g id="VPN_APP_0">%1$s</xliff:g> və <xliff:g id="VPN_APP_1">%2$s</xliff:g> tətbiqlərinə qoşulusunuz."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"İş profili e-poçt, tətbiq və veb saytlar da daxil olmaqla şəbəkə fəaliyyətinə nəzarət edən <xliff:g id="VPN_APP">%1$s</xliff:g> tətbiqinə qoşuludur."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Səssiz etmək üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Vibrasiyanı ayarlamaq üçün klikləyin."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Səssiz etmək üçün klikləyin."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"susdurun"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"səssiz rejimdən çıxarın"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrasiya"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Bu bildiriş sistem tərəfindən avtomatik olaraq <b>Səssiz rejimə keçirilib</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Bu bildiriş avtomatik olaraq siyahıda <b>yuxarı sıraya keçirilib</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Bu bildiriş avtomatik olaraq siyahıda <b>aşağı sıraya keçirilib</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Bu, doğru oldu?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Developerə rəyinizi bildirin. Bu, doğru idi?"</string> <string name="feedback_response" msgid="4671729244976641339">"Rəyiniz üçün təşəkkür edirik!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> üçün bildiriş kontrolları açıqdır"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə köçürün"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə əlavə edin"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyi"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Mozaik əlavə edilib"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Mozaik silinib"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sürətli ayarlar redaktoru."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildiriş: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ayarları açın."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> istifadə edir"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> bu yaxınlarda <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> istifadə edib"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(korporativ)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefon zəngi"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefon zəngi"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> vasitəsilə)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"məkan"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensorlar deaktivdir"</string> <string name="device_services" msgid="1549944177856658705">"Cihaz Xidmətləri"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Başlıq yoxdur"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Bu tətbiqi sıfırlayaraq tam ekrana keçmək üçün klikləyin."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Hərəkət etdirin"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Sistem naviqasiyası yeniləndi. Dəyişiklik etmək üçün Ayarlara daxil olun."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Sistem naviqasiyasını yeniləmək üçün Ayarlara keçin"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Əlavə edin"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> tərəfindən təklif edilib"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Nizamlayıcılar güncəlləndi"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Cihaz kilidlənib"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN hərflər və ya simvollar ehtiva edir"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> cihazını doğrulayın"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Yanlış PIN"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 2185a66477a0..27d4b182b3f7 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Pomerajte snimak ekrana"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Odbacite snimak ekrana"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Gornja granica"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Donja granica"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađujemo video snimka ekrana"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Obaveštenje o sesiji snimanja ekrana je aktivno"</string> @@ -412,6 +414,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do izlaska sunca"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Uključuje se u <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je onemogućen"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je omogućen"</string> @@ -445,6 +449,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Otključajte da biste koristili NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Ovaj uređaj pripada organizaciji"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Prevucite od ikone za telefon"</string> <string name="voice_hint" msgid="7476017460191291417">"Prevucite od ikone za glasovnu pomoć"</string> <string name="camera_hint" msgid="4519495795000658637">"Prevucite od ikone za kameru"</string> @@ -465,9 +471,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaži profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Želite da završite sesiju gosta?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Završi sesiju gosta"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite li da uklonite gosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Završi sesiju"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli nazad, goste!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li da nastavite sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni iz početka"</string> @@ -543,6 +550,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizacija je na poslovnom profilu instalirala autoritet za izdavanje sertifikata. Bezbedni mrežni saobraćaj može da se prati ili menja."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Na ovom uređaju je instaliran autoritet za izdavanje sertifikata. Bezbedni mrežni saobraćaj može da se prati ili menja."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrator je uključio evidentiranje mreže, koje prati saobraćaj na uređaju."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administrator je uključio evidentiranje mreže, koje prati saobraćaj na poslovnom profilu, ali ne i na ličnom profilu."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Povezani ste sa aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>, koja može da nadgleda aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Povezani ste sa aplikacijama <xliff:g id="VPN_APP_0">%1$s</xliff:g> i <xliff:g id="VPN_APP_1">%2$s</xliff:g>, koje mogu da nadgledaju aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Poslovni profil je povezan sa aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>, koja može da nadgleda aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove."</string> @@ -623,6 +631,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da biste isključili zvuk. Zvuk usluga pristupačnosti će možda biti isključen."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Dodirnite da biste podesili na vibraciju."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Dodirnite da biste isključili zvuk."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibracija"</string> @@ -736,7 +746,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Sistem je ovo obaveštenje automatski <b>degradirao u Nečujno</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Ovo obaveštenje je automatski <b>rangirano više</b> na traci sa obaveštenjima."</string> <string name="feedback_demoted" msgid="951884763467110604">"Ovo obaveštenje je automatski <b>rangirano niže</b> na traci sa obaveštenjima."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Da li je to tačno?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Pošaljite programeru povratne informacije. Da li je to tačno?"</string> <string name="feedback_response" msgid="4671729244976641339">"Hvala vam na povratnim informacijama!"</string> <string name="feedback_ok" msgid="6481426753298857144">"Potvrdi"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kontrole obaveštenja za otvaranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -885,6 +895,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premestite na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajte na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozicija"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Pločica je dodata"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Pločica je uklonjena"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač za Brza podešavanja."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obaveštenja za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvori Podešavanja."</string> @@ -975,7 +987,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> koristi: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> je nedavno koristila: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(za preduzeća)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonski poziv"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonski poziv"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(preko: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kameru"</string> <string name="privacy_type_location" msgid="7991481648444066703">"lokaciju"</string> @@ -983,7 +995,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Senzori su isključeni"</string> <string name="device_services" msgid="1549944177856658705">"Usluge za uređaje"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Bez naslova"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Dodirnite da biste restartovali aplikaciju i prešli u režim celog ekrana."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Premesti"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigacija sistema je ažurirana. Da biste uneli izmene, idite u Podešavanja."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Idite u Podešavanja da biste ažurirali navigaciju sistema"</string> @@ -1038,6 +1049,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Dodaj"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Predlaže <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Kontrole su ažurirane"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Uređaj je zaključan"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN sadrži slova ili simbole"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verifikujte: <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Pogrešan PIN"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index e5356ae6486c..ab1cc69ee35c 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Здымак экрана з пракруткай"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Адхіліць здымак экрана"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Перадпрагляд здымка экрана"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Верхняя граніца"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Ніжняя граніца"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Запіс экрана"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Апрацоўваецца запіс экрана"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Бягучае апавяшчэнне для сеанса запісу экрана"</string> @@ -414,6 +416,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Да ўсходу сонца"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Уключана ў <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Да <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC адключаны"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC уключаны"</string> @@ -447,6 +451,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Разблакіруйце, каб выкарыстоўваць NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Гэта прылада належыць вашай арганізацыі"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Гэта прылада належыць арганізацыі \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\""</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Тэлефон: правядзіце пальцам ад значка"</string> <string name="voice_hint" msgid="7476017460191291417">"Галасавая дапамога: правядзіце пальцам ад значка"</string> <string name="camera_hint" msgid="4519495795000658637">"Камера: правядзіце пальцам ад значка"</string> @@ -467,9 +473,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Паказаць профіль"</string> <string name="user_add_user" msgid="4336657383006913022">"Дадаць карыстальніка"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новы карыстальнік"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Завяршыць гасцявы сеанс?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Завяршыць гасцявы сеанс"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Выдаліць госця?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Завяршыць сеанс"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Выдаліць"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"З вяртаннем, госць!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Хочаце працягнуць сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Пачаць зноў"</string> @@ -546,6 +553,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ваша арганізацыя ўсталявала ў вашым працоўным профілі цэнтр сертыфікацыі. Ваш абаронены сеткавы трафік могуць праглядваць ці змяняць."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На гэтай прыладзе ўсталяваны цэнтр сертыфікацыі. Ваш абаронены сеткавы трафік могуць праглядваць ці змяняць."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Ваш адміністратар уключыў вядзенне журнала сеткі, з дапамогай якога адсочваецца трафік на вашай прыладзе."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Ваш адміністратар уключыў вядзенне журнала сеткі, з дапамогай якога адсочваецца трафік у вашым працоўным профілі. Трафік вашага асабістага профілю застаецца недаступным."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Вы падключаны да праграмы <xliff:g id="VPN_APP">%1$s</xliff:g>, якая можа сачыць за вашай сеткавай дзейнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Вы падключаны да праграм <xliff:g id="VPN_APP_0">%1$s</xliff:g> і <xliff:g id="VPN_APP_1">%2$s</xliff:g>, якія могуць сачыць за вашай сеткавай дзейнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Ваш працоўны профіль падключаны да праграмы <xliff:g id="VPN_APP">%1$s</xliff:g>, якая можа сачыць за вашай сеткавай актыўнасцю, уключаючы электронную пошту, праграмы і вэб-сайты."</string> @@ -626,6 +634,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дакраніцеся, каб адключыць гук. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Дакраніцеся, каб уключыць вібрацыю."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Дакраніцеся, каб адключыць гук"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"выключыць гук"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"уключыць гук"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вібрыраваць"</string> @@ -739,7 +749,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Гэта апавяшчэнне аўтаматычна пераведзена сістэмай у <b>рэжым \"Без гуку\"</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Гэта апавяшчэнне аўтаматычна ацэнена як <b>важнае</b> для вас."</string> <string name="feedback_demoted" msgid="951884763467110604">"Гэта апавяшчэнне аўтаматычна ацэнена як <b>няважнае</b> для вас."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Усё правільна?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Напішыце распрацоўшчыку водгук. Усё правільна?"</string> <string name="feedback_response" msgid="4671729244976641339">"Дзякуй за водгук!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ОК"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Кіраванне апавяшчэннямі для <xliff:g id="APP_NAME">%1$s</xliff:g> адкрыта"</string> @@ -890,6 +900,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перамясціць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Дадаць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Пазіцыя <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Плітка дададзена"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Плітка выдалена"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Рэдактар хуткіх налад."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Апавяшчэнне <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Адкрыць налады."</string> @@ -980,7 +992,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Праграма \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\" выкарыстоўвае праграму \"<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>\""</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Праграма \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\" нядаўна выкарыстоўвала праграму \"<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>\""</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(прадпрыемства)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Тэлефонны выклік"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Тэлефонны выклік"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(праз праграму \"<xliff:g id="ATTRIBUTION">%s</xliff:g>\")"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string> <string name="privacy_type_location" msgid="7991481648444066703">"геалакацыя"</string> @@ -988,7 +1000,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Датчыкі выкл."</string> <string name="device_services" msgid="1549944177856658705">"Сэрвісы прылады"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Без назвы"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Націсніце, каб перазапусціць гэту праграму і перайсці ў поўнаэкранны рэжым."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Перамясціць"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Навігацыя ў сістэме абноўлена. Каб унесці змяненні, перайдзіце ў Налады."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Перайдзіце ў Налады, каб абнавіць параметры навігацыі ў сістэме"</string> @@ -1044,6 +1055,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Дадаць"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Прапанавана праграмай \"<xliff:g id="APP">%s</xliff:g>\""</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Элементы кіравання абноўлены"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Прылада блакіравана"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN-код складаецца з літар або знакаў"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Спраўдзіце прыладу \"<xliff:g id="DEVICE">%s</xliff:g>\""</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Няправільны PIN-код"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index adf852b5495b..6716e15ca6cd 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Екранна снимка с превъртане"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Отхвърляне на екранната снимка"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Визуализация на екранната снимка"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Горна граница"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Долна граница"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Запис на екрана"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Записът на екрана се обработва"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущо известие за сесия за записване на екрана"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До изгрев"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ще се включи в <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"КБП е деактивирана"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"КБП е активирана"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Отключете, за да използвате NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Това устройство принадлежи на организацията ви"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Това устройство принадлежи на <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Плъзнете с пръст от иконата, за да използвате телефона"</string> <string name="voice_hint" msgid="7476017460191291417">"Прекарайте пръст от иконата, за да получите гласова помощ"</string> <string name="camera_hint" msgid="4519495795000658637">"Плъзнете с пръст от иконата, за да включите камерата"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показване на потребителския профил"</string> <string name="user_add_user" msgid="4336657383006913022">"Добавяне на потребител"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нов потребител"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Да се прекрати ли сесията като гост?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Прекратяване на сесията като гост"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се премахне ли гостът?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Прекратяване на сесията"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Премахване"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дошли отново в сесията като гост!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Искате ли да продължите сесията си?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Започване отначало"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Организацията ви е инсталирала сертифициращ орган в служебния ви потребителски профил. Трафикът в защитената ви мрежа може да бъде наблюдаван или променян."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На това устройство е инсталиран сертифициращ орган. Трафикът в защитената ви мрежа може да бъде наблюдаван или променян."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Администраторът ви е включил функцията за регистриране на мрежовата активност, която следи трафика на устройството ви."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Администраторът ви е включил функцията за регистриране на мрежовата активност, която следи трафика в служебния ви потребителски профил, но не и в личния."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Установена е връзка с приложението <xliff:g id="VPN_APP">%1$s</xliff:g>, което може да наблюдава активността ви в мрежата, включително имейли, приложения и уебсайтове."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Установена е връзка с приложенията <xliff:g id="VPN_APP_0">%1$s</xliff:g> и <xliff:g id="VPN_APP_1">%2$s</xliff:g>, които могат да наблюдават активността ви в мрежата, включително имейли, приложения и уебсайтове."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Служебният ви потребителски профил е свързан с приложението <xliff:g id="VPN_APP">%1$s</xliff:g>, което може да наблюдава активността ви в мрежата, включително имейли, приложения и уебсайтове."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Докоснете, за да заглушите звука. Възможно е звукът на услугите за достъпност да бъде заглушен."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Докоснете, за да зададете вибриране."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Докоснете, за да заглушите звука."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"спиране"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"пускане"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибриране"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Това известие автоматично бе <b>понижено до беззвучно</b> от системата."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Това известие автоматично бе <b>класирано по-високо</b> в панела ви."</string> <string name="feedback_demoted" msgid="951884763467110604">"Това известие автоматично бе <b>класирано по-ниско</b> в панела ви."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Правилно ли е това?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Изпратете отзивите си на програмиста. Правилно ли е това?"</string> <string name="feedback_response" msgid="4671729244976641339">"Благодарим ви за отзивите!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ОК"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Контролите за известията за <xliff:g id="APP_NAME">%1$s</xliff:g> са оттворени"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместване към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавяне към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Панелът е добавен"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Панелът е премахнат"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор за бързи настройки."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известие от <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отваряне на настройките."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> използва <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> наскоро използва <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративна версия)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефонно обаждане"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефонно обаждане"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(чрез <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"камерата"</string> <string name="privacy_type_location" msgid="7991481648444066703">"местополож."</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Сензорите са изключени"</string> <string name="device_services" msgid="1549944177856658705">"Услуги за устройството"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Няма заглавие"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Докоснете, за да рестартирате това приложение в режим на цял екран."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Преместване"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Режимът за навигиране в системата е актуализиран. За да извършите промени, отворете настройките."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Отворете настройките, за да актуализирате режима за навигиране в системата"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Добавяне"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Предложено от <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Контролите са актуализирани"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"У-вото е заключено"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"ПИН кодът съдържа букви или символи"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Потвърждаване на <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Грешен ПИН код"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 437abb85b445..ee639d6e639c 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"স্ক্রিনশট স্ক্রল করুন"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"স্ক্রিনশট বাতিল করুন"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্রিনশটের প্রিভিউ"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"স্ক্রিনশটের একদম উপরের দিকে"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"স্ক্রিনশটের একদম নিচের দিকে"</string> <string name="screenrecord_name" msgid="2596401223859996572">"স্ক্রিন রেকর্ডার"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রিন রেকর্ডিং প্রসেস হচ্ছে"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রিন রেকর্ডিং সেশন চলার বিজ্ঞপ্তি"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"সূর্যোদয় পর্যন্ত"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>-এ চালু হবে"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> পর্যন্ত"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC অক্ষম করা আছে"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC সক্ষম করা আছে"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ব্যবহার করতে আনলক করুন"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"এই ডিভাইসটি আপনার প্রতিষ্ঠানের"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"এই ডিভাইসটি <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>-এর"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ফোনের জন্য আইকন থেকে সোয়াইপ করুন"</string> <string name="voice_hint" msgid="7476017460191291417">"ভয়েস সহায়তার জন্য আইকন থেকে সোয়াইপ করুন"</string> <string name="camera_hint" msgid="4519495795000658637">"ক্যামেরার জন্য আইকন থেকে সোয়াইপ করুন"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"প্রোফাইল দেখান"</string> <string name="user_add_user" msgid="4336657383006913022">"ব্যবহারকারী জুড়ুন"</string> <string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যবহারকারী"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"গেস্ট সেশন শেষ করতে চান?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"গেস্ট সেশন শেষ করুন"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"অতিথি সরাবেন?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ্লিকেশান ও ডেটা মুছে ফেলা হবে।"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"সেশন শেষ করুন"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"সরান"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"অতিথি, আপনি ফিরে আসায় আপনাকে স্বাগত!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপনি কি আপনার সেশনটি অবিরত রাখতে চান?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আবার শুরু করুন"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"আপনার প্রতিষ্ঠান আপনার অফিস প্রোফাইলে একটি সার্টিফিকেট কর্তৃপক্ষ ইনস্টল করেছে। আপনার নিরাপদ নেটওয়ার্ক ট্রাফিকে নজর রাখা হতে পারে বা তাতে পরিবর্তন করা হতে পারে।"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"এই ডিভাইসে একটি সার্টিফিকেট কর্তৃপক্ষ ইনস্টল করা আছে। আপনার নিরাপদ নেটওয়ার্ক ট্রাফিকে নজর রাখা হতে পারে বা তাতে পরিবর্তন করা হতে পারে।"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"আপনার প্রশাসক নেটওয়ার্ক লগিং চালু করেছেন, যা আপনার ডিভাইসের ট্রাফিকের উপরে নজর রাখে।"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"আপনার অ্যাডমিন নেটওয়ার্ক লগ করা চালু করেছেন যা আপনার অফিস প্রোফাইলে ট্রাফিক মনিটর করে তবে ব্যক্তিগত প্রোফাইলে করে না।"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"আপনি <xliff:g id="VPN_APP">%1$s</xliff:g> এ সংযুক্ত রয়েছেন, যা আপনার ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক অ্যাক্টিভিটির উপর নজর রাখতে পারে।"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"আপনি <xliff:g id="VPN_APP_0">%1$s</xliff:g> এবং <xliff:g id="VPN_APP_1">%2$s</xliff:g> এর সাথে সংযুক্ত রয়েছেন, যেগুলি আপনার ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক অ্যাক্টিভিটির উপর নজর রাখতে পারে।"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"আপনার কর্মস্থলের প্রোফাইল <xliff:g id="VPN_APP">%1$s</xliff:g> এর সাথে সংযুক্ত রয়েছে, যেটি ইমেল, অ্যাপ, এবং ওয়েবসাইট সহ আপনার নেটওয়ার্ক কার্যকলাপে নজর রাখতে পারে।"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। মিউট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s। ভাইব্রেট করতে ট্যাপ করুন।"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s। মিউট করতে ট্যাপ করুন।"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট করুন"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট করুন"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ভাইব্রেট করান"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"সিস্টেম অটোমেটিক এই বিজ্ঞপ্তির <b>লেভেল কমিয়ে সাইলেন্ট</b> করে দিয়েছে।"</string> <string name="feedback_promoted" msgid="2125562787759780807">"আপনার শেডে অটোমেটিক এই বিজ্ঞপ্তির <b>র্যাঙ্ক বাড়িয়ে</b> দেওয়া হয়েছে।"</string> <string name="feedback_demoted" msgid="951884763467110604">"আপনার শেডে অটোমেটিক এই বিজ্ঞপ্তির <b>র্যাঙ্ক কমিয়ে</b> দেওয়া হয়েছে।"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"এটি কি সঠিক ছিল?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"ডেভেলপারকে আপনার মতামত জানান। এতে কোনও ভুল দেখতে পেলেন?"</string> <string name="feedback_response" msgid="4671729244976641339">"মতামতের জন্য ধন্যবাদ!"</string> <string name="feedback_ok" msgid="6481426753298857144">"বুঝেছি"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> খোলা থাকলে বিজ্ঞপ্তি নিয়ন্ত্রণ"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>-এ সরান"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>-এ যোগ করুন"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"টাইল যোগ করা হয়েছে"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"টাইল সরানো হয়েছে"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"দ্রুত সেটিংস সম্পাদক৷"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> বিজ্ঞপ্তি: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"সেটিংস খুলুন।"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> এখন <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ব্যবহার করছে"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> সম্প্রতি <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ব্যবহার করেছে"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"এন্টারপ্রাইজ"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ফোনকল"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ফোন কল"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g>-এর মাধ্যমে)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"ক্যামেরা"</string> <string name="privacy_type_location" msgid="7991481648444066703">"লোকেশন"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"সেন্সর বন্ধ"</string> <string name="device_services" msgid="1549944177856658705">"ডিভাইস সংক্রান্ত পরিষেবা"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"কোনও শীর্ষক নেই"</string> - <string name="restart_button_description" msgid="6916116576177456480">"এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন ও ফুল-স্ক্রিন ব্যবহার করুন।"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"সরান"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"সিস্টেম নেভিগেশন আপডেট হয়েছে। পরিবর্তন করার জন্য সেটিংসে যান।"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"সিস্টেম নেভিগেশন আপডেট করতে সেটিংসে যান"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"যোগ করুন"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> সাজেস্ট করেছে"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"কন্ট্রোল আপডেট করা হয়েছে"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"ডিভাইস লক করা আছে"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"পিন-এ অক্ষর বা চিহ্ন রয়েছে"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> যাচাই করুন"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"ভুল পিন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index f2b3edca02de..01a55d3d06ba 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Pokretni snimak ekrana"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Odbacite snimak ekrana"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Gornja granica"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Donja granica"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađivanje snimka ekrana"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Obavještenje za sesiju snimanja ekrana je u toku"</string> @@ -412,6 +414,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do svitanja"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Uključuje se u <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je onemogućen"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je omogućen"</string> @@ -445,6 +449,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Otključajte da koristite NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Ovaj uređaj pripada vašoj organizaciji"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Prevucite preko ikone da otvorite telefon"</string> <string name="voice_hint" msgid="7476017460191291417">"Prevucite preko ikone za glasovnu pomoć"</string> <string name="camera_hint" msgid="4519495795000658637">"Prevucite od ikone da otvorite kameru"</string> @@ -465,9 +471,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Pokaži profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Završiti sesiju gosta?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Završi sesiju gosta"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite li ukloniti gosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i svi podaci iz ove sesije bit će izbrisani."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Završi sesiju"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Zdravo! Lijepo je opet vidjeti goste."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> @@ -543,6 +550,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Vaša organizacija je instalirala CA certifikat na vašem radnom profilu. Vaš saobraćaj preko sigurne mreže može se pratiti."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"CA certifikat je instaliran na ovom uređaju. Vaš saobraćaj preko sigurne mreže može se pratiti."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Vaš administrator je uključio zapisivanje na mreži, čime se prati saobraćaj na vašem uređaju."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administrator je uključio zapisivanje na mreži, čime se nadzire saobraćaj na vašem radnom profilu, ali ne i na ličnom profilu."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Povezani ste s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g> koja može pratiti vašu aktivnost na mreži, uključujući e-poštu i web lokacije."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Povezani ste s aplikacijama <xliff:g id="VPN_APP_0">%1$s</xliff:g> i <xliff:g id="VPN_APP_1">%2$s</xliff:g> koje mogu pratiti vašu aktivnost na mreži, uključujući e-poštu, aplikacije i web lokacije."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Vaš radni profil je povezan s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g>, koja može pratiti vašu aktivnost na mreži, uključujući e-poruke i web lokacije."</string> @@ -623,6 +631,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da isključite zvuk. Zvukovi usluga pristupačnosti mogu biti isključeni."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Dodirnite da postavite vibraciju."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Dodirnite da isključite zvuk."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string> @@ -736,7 +746,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Sistem je ovo obavještenje automatski <b>unazadio u Nečujno</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Ovo obavještenje je automatski <b>rangirano više</b> u pozadini."</string> <string name="feedback_demoted" msgid="951884763467110604">"Ovo obavještenje je automatski <b>rangirano niže</b> u pozadini."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Je li ovo bilo tačno?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Pošaljite programeru svoje povratne informacije. Je li ovo bilo tačno?"</string> <string name="feedback_response" msgid="4671729244976641339">"Hvala na povratnim informacijama!"</string> <string name="feedback_ok" msgid="6481426753298857144">"UREDU"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Otvorene su kontrole obavještenja za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -885,6 +895,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pomjeranje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kartica je dodana"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kartica je uklonjena"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivanje brzih postavki"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavještenje: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvori postavke."</string> @@ -975,7 +987,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> koristi aplikaciju <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> je nedavno koristila aplikaciju <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(preduzeće)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonski poziv"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonski poziv"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(putem aplikacije <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kameru"</string> <string name="privacy_type_location" msgid="7991481648444066703">"lokaciju"</string> @@ -983,7 +995,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Senzori su isključeni"</string> <string name="device_services" msgid="1549944177856658705">"Usluge uređaja"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Bez naslova"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Dodirnite da ponovo pokrenete ovu aplikaciju i aktivirate prikaz preko cijelog ekrana."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Pomjeri"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigiranje sistemom je ažurirano. Da izvršite promjene, idite u Postavke."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Idite u Postavke da ažurirate navigiranje sistemom"</string> @@ -1038,6 +1049,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Dodaj"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Predlaže <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Kontrole su ažurirane"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Uređaj je zaključan"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN sadrži slova ili simbole"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Potvrdite uređaj <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Pogrešan PIN"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index bdcde318179a..5d8ccbc1f52c 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Captura de pantalla lliscant"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ignora la captura de pantalla"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Previsualització de la captura de pantalla"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Marge superior"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Marge inferior"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Gravació de pantalla"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processant gravació de pantalla"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificació en curs d\'una sessió de gravació de la pantalla"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Fins a l\'alba"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Activat a les <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Fins a les <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"L\'NFC està desactivada"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"L\'NFC està activada"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloqueja per utilitzar l\'NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Aquest dispositiu pertany a la teva organització"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Aquest dispositiu pertany a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Llisca des de la icona per obrir el telèfon"</string> <string name="voice_hint" msgid="7476017460191291417">"Llisca des de la icona per obrir l\'assistent de veu"</string> <string name="camera_hint" msgid="4519495795000658637">"Llisca des de la icona per obrir la càmera"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostra el perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Afegeix un usuari"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Usuari nou"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vols finalitzar la sessió de convidat?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Finalitza la sessió de convidat"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vols suprimir el convidat?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalitza la sessió"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Suprimeix"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvingut de nou, convidat."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vols continuar amb la sessió?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Torna a començar"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"La teva organització ha instal·lat una autoritat de certificació al teu perfil de treball. És possible que el trànsit de xarxa segura se supervisi o es modifiqui."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"S\'ha instal·lat una autoritat de certificació en aquest dispositiu. És possible que el trànsit de xarxa segura se supervisi o es modifiqui."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"L\'administrador ha activat el registre de xarxa, que supervisa el trànsit del teu dispositiu."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"L\'administrador ha activat el registre de xarxa, que monitora el trànsit al teu perfil de treball, però no al personal."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Estàs connectat a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pot supervisar la teva activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Estàs connectat a <xliff:g id="VPN_APP_0">%1$s</xliff:g> i <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que poden supervisar la teva activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"El teu perfil de treball està connectat a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pot supervisar la teva activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca per silenciar el so. Pot ser que els serveis d\'accessibilitat se silenciïn."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Toca per activar la vibració."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toca per silenciar."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"deixar de silenciar"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"El sistema <b>ha disminuït a Silenci</b> el nivell d\'aquesta notificació."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Aquesta notificació s\'ha classificat automàticament amb un <b>nivell superior</b> a l\'àrea de notificacions."</string> <string name="feedback_demoted" msgid="951884763467110604">"Aquesta notificació s\'ha classificat automàticament amb un <b>nivell inferior</b> a l\'àrea de notificacions."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"La informació ha estat correcta?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Fes saber els teus suggeriments al desenvolupador. La informació ha estat correcta?"</string> <string name="feedback_response" msgid="4671729244976641339">"Gràcies pels suggeriments."</string> <string name="feedback_ok" msgid="6481426753298857144">"D\'acord"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"S\'han obert els controls de notificació per a <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mou a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Afegeix a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posició <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"El mosaic s\'ha afegit"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"El mosaic s\'ha suprimit"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuració ràpida."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificació de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Obre la configuració."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> està utilitzant: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Recentment <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ha utilitzat: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresa)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Trucada"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Trucada"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(a través de: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"càmera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"ubicació"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors desactivats"</string> <string name="device_services" msgid="1549944177856658705">"Serveis per a dispositius"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Sense títol"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Toca per reiniciar l\'aplicació i passar a pantalla completa."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mou"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"S\'ha actualitzat el sistema de navegació. Per fer canvis, ves a Configuració."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Ves a Configuració per actualitzar el sistema de navegació"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Afegeix"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Suggerit per <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"S\'han actualitzat els controls"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Dispositiu bloquejat"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"El PIN conté lletres o símbols"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verifica <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN incorrecte"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index aa5a1dd70695..024b836bbe53 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Posunout snímek obrazovky"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Zavřít snímek obrazovky"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Náhled snímku obrazovky"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Horní hranice"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Dolní hranice"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Záznam obrazovky se zpracovává"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Trvalé oznámení o relaci nahrávání"</string> @@ -414,6 +416,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do svítání"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Zapnout v <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je vypnuto"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je zapnuto"</string> @@ -447,6 +451,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC vyžaduje odemknutou obrazovku"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Toto zařízení patří vaší organizaci"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Toto zařízení patří organizaci <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Telefon otevřete přejetím prstem od ikony"</string> <string name="voice_hint" msgid="7476017460191291417">"Hlasovou asistenci otevřete přejetím prstem od ikony"</string> <string name="camera_hint" msgid="4519495795000658637">"Fotoaparát otevřete přejetím prstem od ikony"</string> @@ -467,9 +473,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Zobrazit profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Přidat uživatele"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nový uživatel"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ukončit relaci hosta?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Ukončení relace hosta"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Odstranit hosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ukončit relaci"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstranit"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Vítejte zpět v relaci hosta!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relaci pokračovat?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začít znovu"</string> @@ -546,6 +553,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizace do vašeho pracovního profilu nainstalovala certifikační autoritu. Zabezpečený síťový provoz může být sledován nebo upravován."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"V zařízení je nainstalována certifikační autorita. Zabezpečený síťový provoz může být sledován nebo upravován."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrátor zapnul protokolování sítě, které monitoruje síťový provoz v zařízení."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administrátor zapnul protokolování sítě, které monitoruje síťový provoz ve vašem pracovním profilu (ale ne v osobním)."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Jste připojeni k aplikaci <xliff:g id="VPN_APP">%1$s</xliff:g>, která může sledovat vaši aktivitu v síti, včetně e-mailů, aplikací a webů."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Jste připojeni k aplikacím <xliff:g id="VPN_APP_0">%1$s</xliff:g> a <xliff:g id="VPN_APP_1">%2$s</xliff:g>, které mohou sledovat vaši aktivitu v síti, včetně e-mailů, aplikací a webů."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Váš pracovní profil je připojen k aplikaci <xliff:g id="VPN_APP">%1$s</xliff:g>, která může sledovat vaši aktivitu v síti, včetně e-mailů, aplikací a webů."</string> @@ -626,6 +634,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Klepnutím vypnete zvuk. Služby přístupnosti mohou být ztlumeny."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Klepnutím nastavíte vibrace."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Klepnutím vypnete zvuk."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnout zvuk"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnout zvuk"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrovat"</string> @@ -739,7 +749,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"U tohoto oznámení systém automaticky <b>snížil prioritu na Tiché</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Toto oznámení bylo na panelu automaticky <b>zařazeno výše</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Toto oznámení bylo na panelu automaticky <b>zařazeno níže</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Udělal to správně?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Sdělte vývojáři svůj názor. Udělal to správně?"</string> <string name="feedback_response" msgid="4671729244976641339">"Děkujeme za zpětnou vazbu."</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Ovládací prvky oznámení aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> byly otevřeny"</string> @@ -890,6 +900,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Přesunout na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Přidat dlaždici na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Karta byla přidána"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Karta byla odstraněna"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rychlého nastavení"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Oznámení aplikace <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otevřít nastavení."</string> @@ -980,7 +992,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikace <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> používá aplikaci <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikace <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> nedávno použila aplikaci <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(podniková verze)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonní hovor"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonní hovor"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(prostřednictvím aplikace <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparát"</string> <string name="privacy_type_location" msgid="7991481648444066703">"poloha"</string> @@ -988,7 +1000,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Senzory jsou vypnuty"</string> <string name="device_services" msgid="1549944177856658705">"Služby zařízení"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Bez názvu"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Klepnutím aplikaci restartujete a přejdete na režim celé obrazovky"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Přesunout"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Systémová navigace byla aktualizována. Chcete-li provést změny, přejděte do Nastavení."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Přejděte do Nastavení a aktualizujte systémovou navigaci"</string> @@ -1044,6 +1055,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Přidat"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Návrh z aplikace <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Ovládací prvky aktualizovány"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Zařízení uzamčeno"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Kód PIN obsahuje písmena nebo symboly"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Ověření zařízení <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Chybný PIN"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 5630293c1606..b1a7a5afd938 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Rul screenshot"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Luk screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning af screenshot"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Øverste kant"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Nederste kant"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Skærmoptagelse"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skærmoptagelse"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Konstant notifikation om skærmoptagelse"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Indtil solopgang"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Tænd kl. <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Indtil kl. <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC er deaktiveret"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC er aktiveret"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Lås op for at bruge NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Denne enhed tilhører din organisation"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Denne enhed tilhører <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Stryg fra telefonikonet"</string> <string name="voice_hint" msgid="7476017460191291417">"Stryg fra mikrofonikonet"</string> <string name="camera_hint" msgid="4519495795000658637">"Stryg fra kameraikonet"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Vis profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Tilføj bruger"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny bruger"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vil du afslutte gæstesessionen?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Afslut gæstesessionen"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vil du fjerne gæsten?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Afslut sessionen"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjern"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbage, gæst!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsætte din session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start forfra"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Din organisation har installeret et nøglecenter på din arbejdsprofil. Din sikre netværkstrafik kan overvåges eller ændres."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Der er installeret et nøglecenter på denne enhed. Din sikre netværkstrafik kan overvåges eller ændres."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Din administrator har aktiveret netværksregistrering, som overvåger trafik på din enhed."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Din administrator har aktiveret netværkslogging, som overvåger trafik på din arbejdsprofil, men ikke på din personlige profil."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Du har forbindelse til <xliff:g id="VPN_APP">%1$s</xliff:g>, som kan overvåge din netværksaktivitet, bl.a. mails, apps og websites."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Du har forbindelse til <xliff:g id="VPN_APP_0">%1$s</xliff:g> og <xliff:g id="VPN_APP_1">%2$s</xliff:g>, som kan overvåge din netværksaktivitet, bl.a. mails, apps og websites."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Din arbejdsprofil har forbindelse til <xliff:g id="VPN_APP">%1$s</xliff:g>, som kan overvåge din netværksaktivitet, bl.a. mails, apps og websites."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tryk for at slå lyden fra. Lyden i tilgængelighedstjenester kan blive slået fra."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tryk for at aktivere vibration."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tryk for at slå lyden fra."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"slå lyden fra"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå lyden til"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Denne notifikation blev automatisk <b>angivet som Lydløs</b> af systemet."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Denne notifikation blev automatisk <b>rangeret højere</b> i din skygge."</string> <string name="feedback_demoted" msgid="951884763467110604">"Denne notifikation blev automatisk <b>rangeret lavere</b> i din skygge."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Var dette korrekt?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Giv udvikleren feedback. Var dette korrekt?"</string> <string name="feedback_response" msgid="4671729244976641339">"Tak for din feedback"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Styring af notifikationer for <xliff:g id="APP_NAME">%1$s</xliff:g> blev åbnet"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flyt til <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Føj til placering <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Placering <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kortet blev tilføjet"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kortet blev fjernet"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsværktøj til Kvikmenu."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-notifikation: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Åbn Indstillinger."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> anvender <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> anvendte <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> for nylig"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(til virksomhedsbrug)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonopkald"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonopkald"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(via <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"placering"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Deaktiver sensorer"</string> <string name="device_services" msgid="1549944177856658705">"Enhedstjenester"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Ingen titel"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Tryk for at genstarte denne app, og gå til fuld skærm."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Flyt"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Systemnavigationen blev opdateret. Gå til Indstillinger for at foretage ændringer."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Gå til Indstillinger for at opdatere systemnavigationen"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Tilføj"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Foreslået af <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Betjeningselementerne er opdateret"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Enheden er låst"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Pinkoden indeholder bogstaver eller symboler"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Bekræft <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Forkert pinkode"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index e2468bf04505..88dd62da811c 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Screenshot scrollen"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Screenshot schließen"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshotvorschau"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Oberer Rand"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Unterer Rand"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Bildschirmaufzeichnung"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Bildschirmaufzeichnung…"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Fortlaufende Benachrichtigung für eine Bildschirmaufzeichnung"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Bis Sonnenaufgang"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"An um <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Bis <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ist deaktiviert"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ist aktiviert"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Zur Verwendung von NFC entsperren"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Dieses Gerät gehört deiner Organisation"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Dieses Gerät gehört <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Zum Öffnen des Telefons vom Symbol wegwischen"</string> <string name="voice_hint" msgid="7476017460191291417">"Zum Öffnen des Sprachassistenten vom Symbol wegwischen"</string> <string name="camera_hint" msgid="4519495795000658637">"Zum Öffnen der Kamera vom Symbol wegwischen"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profil öffnen"</string> <string name="user_add_user" msgid="4336657383006913022">"Nutzer hinzufügen"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Neuer Nutzer"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Gastsitzung beenden?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Gastsitzung beenden"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast entfernen?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Sitzung beenden"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Entfernen"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Willkommen zurück im Gastmodus"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Möchtest du deine Sitzung fortsetzen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Neu starten"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Deine Organisation hat ein Zertifikat einer Zertifizierungsstelle in deinem Arbeitsprofil installiert. Eventuell wird dein sicherer Netzwerkverkehr überwacht oder bearbeitet."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Auf dem Gerät ist das Zertifikat einer Zertifizierungsstelle installiert. Eventuell wird dein sicherer Netzwerkverkehr überwacht oder bearbeitet."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Dein Administrator hat die Netzwerkprotokollierung aktiviert. Damit wird der Netzwerkverkehr auf deinem Gerät überwacht."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Dein Administrator hat die Netzwerkprotokollierung aktiviert. Damit werden die Zugriffe in deinem Arbeitsprofil erfasst, jedoch nicht in deinem privaten Profil."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Du bist mit <xliff:g id="VPN_APP">%1$s</xliff:g> verbunden. Die App kann deine Netzwerkaktivitäten (E-Mails, Apps und Websites) erfassen."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Du bist mit <xliff:g id="VPN_APP_0">%1$s</xliff:g> und <xliff:g id="VPN_APP_1">%2$s</xliff:g> verbunden. Die Apps können deine Netzwerkaktivitäten (E-Mails, Apps und Websites) erfassen."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Dein Arbeitsprofil ist mit <xliff:g id="VPN_APP">%1$s</xliff:g> verbunden, die deine Netzwerkaktivitäten wie E-Mails, Apps und Websites überwachen kann."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Zum Stummschalten tippen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Zum Aktivieren der Vibration tippen."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Zum Stummschalten tippen."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"stummschalten"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"Stummschaltung aufheben"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrieren"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Diese Benachrichtigung wurde durch das System automatisch <b>auf „Lautlos“ herabgestuft</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Diese Benachrichtigung wurde in deiner Leiste automatisch <b>höher eingestuft</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Diese Benachrichtigung wurde in deiner Leiste automatisch <b>niedriger eingestuft</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"War das richtig?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Teile dem Entwickler dein Feedback mit. War das richtig?"</string> <string name="feedback_response" msgid="4671729244976641339">"Vielen Dank für dein Feedback."</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Benachrichtigungseinstellungen für <xliff:g id="APP_NAME">%1$s</xliff:g> geöffnet"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Auf Position <xliff:g id="POSITION">%1$d</xliff:g> verschieben"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Zur Position <xliff:g id="POSITION">%1$d</xliff:g> hinzufügen"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Ansicht hinzugefügt"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Ansicht entfernt"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor für Schnelleinstellungen."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Benachrichtigung von <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Einstellungen öffnen."</string> @@ -967,23 +979,17 @@ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Apps verwenden gerade Folgendes: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string> <string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" und "</string> - <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) --> - <skip /> + <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> verwendet gerade die <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>-App"</string> + <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> verwendete kürzlich die <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>-App"</string> + <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(Unternehmen)"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonanruf"</string> + <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(über <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"Kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"Standort"</string> <string name="privacy_type_microphone" msgid="9136763906797732428">"Mikrofon"</string> <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensoren aus"</string> <string name="device_services" msgid="1549944177856658705">"Gerätedienste"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Kein Titel"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Tippe, um die App im Vollbildmodus neu zu starten."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Verschieben"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Systemsteuerungseinstellungen wurden angepasst. Änderungen kannst du in den Einstellungen vornehmen."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Gehe zu den Einstellungen, um die Systemsteuerung anzupassen"</string> @@ -1037,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Hinzufügen"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Vorgeschlagen von <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Gerätekarten aktualisiert"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Gerät gesperrt"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Die PIN enthält Buchstaben oder Symbole"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> bestätigen"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Falsche PIN"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index aa747f24a8df..21330256d079 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Στιγμιότυπο κύλισης"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Παράβλεψη στιγμιότυπου οθόνης"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Προεπισκόπηση στιγμιότυπου οθόνης"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Ανώτατο όριο"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Κατώτατο όριο"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Εγγραφή οθόνης"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Επεξεργασία εγγραφής οθόνης"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ειδοποίηση σε εξέλιξη για μια περίοδο λειτουργίας εγγραφής οθόνης"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Μέχρι την ανατολή"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ενεργοποίηση στις <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Έως <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Το NFC είναι απενεργοποιημένο"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Το NFC είναι ενεργοποιημένο"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ξεκλείδωμα για χρήση του NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Αυτή η συσκευή ανήκει στον οργανισμό σας."</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Αυτή η συσκευή ανήκει στον οργανισμό <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Σύρετε προς τα έξω για τηλέφωνο"</string> <string name="voice_hint" msgid="7476017460191291417">"Σύρετε προς τα έξω για voice assist"</string> <string name="camera_hint" msgid="4519495795000658637">"Σύρετε προς τα έξω για κάμερα"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Εμφάνιση προφίλ"</string> <string name="user_add_user" msgid="4336657383006913022">"Προσθήκη χρήστη"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Νέος χρήστης"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Λήξη περιόδου σύνδεσης επισκέπτη;"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Λήξη περιόδου σύνδεσης επισκέπτη"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Κατάργηση επισκέπτη;"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Λήξη περιόδου σύνδεσης"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Κατάργηση"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Επισκέπτη , καλώς όρισες ξανά!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Θέλετε να συνεχίσετε την περίοδο σύνδεσής σας;"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Έναρξη από την αρχή"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ο οργανισμός σας εγκατέστησε μια αρχή έκδοσης πιστοποιητικών στο προφίλ εργασίας σας. Η ασφαλής επισκεψιμότητα δικτύου σας μπορεί να παρακολουθείται ή να τροποποιείται."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Μια αρχή έκδοσης πιστοποιητικών έχει εγκατασταθεί σε αυτήν τη συσκευή. Η ασφαλής επισκεψιμότητα δικτύου σας μπορεί να παρακολουθείται ή να τροποποιείται."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Ο διαχειριστής σας ενεργοποίησε την καταγραφή δικτύου, η οποία παρακολουθεί την επισκεψιμότητα στη συσκευή σας."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Ο διαχειριστής σας έχει ενεργοποιήσει την καταγραφή δικτύου, η οποία παρακολουθεί την επισκεψιμότητα στο προφίλ εργασίας σας, αλλά όχι στο προσωπικό προφίλ σας."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Έχετε συνδεθεί στην εφαρμογή <xliff:g id="VPN_APP">%1$s</xliff:g>, η οποία μπορεί να παρακολουθεί τη δραστηριότητα δικτύου σας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστοτόπων."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Έχετε συνδεθεί στις εφαρμογές <xliff:g id="VPN_APP_0">%1$s</xliff:g> και <xliff:g id="VPN_APP_1">%2$s</xliff:g>, οι οποίες μπορούν να παρακολουθούν τη δραστηριότητα του δικτύου σας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστοτόπων."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Το προφίλ εργασίας σας είναι συνδεδεμένο στο <xliff:g id="VPN_APP">%1$s</xliff:g>, το οποίο μπορεί να παρακολουθεί τη δραστηριότητα δικτύου σας, συμπεριλαμβανομένων μηνυμάτων ηλεκτρονικού ταχυδρομείου, εφαρμογών και ιστοτόπων."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Πατήστε για σίγαση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Πατήστε για να ενεργοποιήσετε τη δόνηση."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Πατήστε για σίγαση."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"σίγαση"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"κατάργηση σίγασης"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"δόνηση"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Αυτή η ειδοποίηση <b>υποβιβάστηκε σε Αθόρυβη</b> αυτόματα από το σύστημα."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Αυτή η ειδοποίηση <b>κατατάχθηκε υψηλότερα</b> στο πλαίσιο σκίασης με αυτόματο τρόπο."</string> <string name="feedback_demoted" msgid="951884763467110604">"Αυτή η ειδοποίηση <b>κατατάχθηκε χαμηλότερα</b> στο πλαίσιο σκίασης με αυτόματο τρόπο."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Ήταν σωστό αυτό;"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Μοιραστείτε τα σχόλιά σας με τον προγραμματιστή. Ήταν σωστό αυτό;"</string> <string name="feedback_response" msgid="4671729244976641339">"Σας ευχαριστούμε για τα σχόλιά σας!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ΟΚ"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Τα στοιχεία ελέγχου ειδοποιήσεων για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> άνοιξαν"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Μετακίνηση στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Προσθήκη στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Το πλακίδιο προστέθηκε"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Το πλακίδιο καταργήθηκε"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Επεξεργασία γρήγορων ρυθμίσεων."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ειδοποίηση <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Άνοιγμα ρυθμίσεων."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Χρήση <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> από την εφαρμογή <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Πρόσφατη χρήση <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> από την εφαρμογή <xliff:g id="APPLICATION_NAME">%1$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(για επιχειρήσεις)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Τηλεφωνική κλήση"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Τηλεφωνική κλήση"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(μέσω <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"κάμερα"</string> <string name="privacy_type_location" msgid="7991481648444066703">"τοποθεσία"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Αισθητήρες ανενεργοί"</string> <string name="device_services" msgid="1549944177856658705">"Υπηρεσίες συσκευής"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Χωρίς τίτλο"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Πατήστε για επανεκκίνηση αυτής της εφαρμογής και ενεργοποίηση πλήρους οθόνης."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Μετακίνηση"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Η πλοήγηση συστήματος ενημερώθηκε. Για να κάνετε αλλαγές, μεταβείτε στις Ρυθμίσεις."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Μεταβείτε στις Ρυθμίσεις για να ενημερώσετε την πλοήγηση συστήματος"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Προσθήκη"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Προτείνεται από <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Ενημέρωση στοιχείων ελέγχου"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Η συσκευή κλειδώθηκε"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Το PIN περιέχει γράμματα ή σύμβολα"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Επαλήθευση <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Λάθος PIN"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 88a40c346e79..0a0f8f7aab1e 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Scroll screenshot"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dismiss screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Top boundary"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Bottom boundary"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> @@ -410,6 +412,7 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduce brightness"</string> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string> @@ -443,6 +446,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Unlock to use NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organisation"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Swipe from icon for phone"</string> <string name="voice_hint" msgid="7476017460191291417">"Swipe from icon for voice assist"</string> <string name="camera_hint" msgid="4519495795000658637">"Swipe from icon for camera"</string> @@ -463,9 +468,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"End Guest session"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> @@ -540,6 +546,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organisation installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Your admin has turned on network logging, which monitors traffic on your device."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"You\'re connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"You\'re connected to <xliff:g id="VPN_APP_0">%1$s</xliff:g> and <xliff:g id="VPN_APP_1">%2$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string> @@ -620,6 +627,7 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tap to set to vibrate."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tap to mute."</string> + <string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string> @@ -733,7 +741,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically <b>demoted to silent</b> by the system."</string> <string name="feedback_promoted" msgid="2125562787759780807">"This notification was automatically <b>ranked higher</b> in your shade."</string> <string name="feedback_demoted" msgid="951884763467110604">"This notification was automatically <b>ranked lower</b> in your shade."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Let the developer know your feedback. Was this correct?"</string> <string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string> @@ -880,6 +888,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string> @@ -970,7 +980,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Phone call"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"location"</string> @@ -978,7 +988,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors off"</string> <string name="device_services" msgid="1549944177856658705">"Device Services"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Tap to restart this app and go full screen."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Move"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"System navigation updated. To make changes, go to Settings."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Go to Settings to update system navigation"</string> @@ -1032,6 +1041,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Add"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Suggested by <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Controls updated"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Device locked"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN contains letters or symbols"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verify <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Wrong PIN"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index bf7883af8138..403fdeec6641 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Scroll screenshot"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dismiss screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Top boundary"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Bottom boundary"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> @@ -410,6 +412,7 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduce brightness"</string> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string> @@ -443,6 +446,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Unlock to use NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organisation"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Swipe from icon for phone"</string> <string name="voice_hint" msgid="7476017460191291417">"Swipe from icon for voice assist"</string> <string name="camera_hint" msgid="4519495795000658637">"Swipe from icon for camera"</string> @@ -463,9 +468,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"End Guest session"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> @@ -540,6 +546,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organisation installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Your admin has turned on network logging, which monitors traffic on your device."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"You\'re connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"You\'re connected to <xliff:g id="VPN_APP_0">%1$s</xliff:g> and <xliff:g id="VPN_APP_1">%2$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string> @@ -620,6 +627,7 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tap to set to vibrate."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tap to mute."</string> + <string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string> @@ -733,7 +741,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically <b>demoted to silent</b> by the system."</string> <string name="feedback_promoted" msgid="2125562787759780807">"This notification was automatically <b>ranked higher</b> in your shade."</string> <string name="feedback_demoted" msgid="951884763467110604">"This notification was automatically <b>ranked lower</b> in your shade."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Let the developer know your feedback. Was this correct?"</string> <string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string> @@ -880,6 +888,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string> @@ -970,7 +980,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Phone call"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"location"</string> @@ -978,7 +988,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors off"</string> <string name="device_services" msgid="1549944177856658705">"Device Services"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Tap to restart this app and go full screen."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Move"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"System navigation updated. To make changes, go to Settings."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Go to Settings to update system navigation"</string> @@ -1032,6 +1041,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Add"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Suggested by <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Controls updated"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Device locked"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN contains letters or symbols"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verify <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Wrong PIN"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 88a40c346e79..0a0f8f7aab1e 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Scroll screenshot"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dismiss screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Top boundary"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Bottom boundary"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> @@ -410,6 +412,7 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduce brightness"</string> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string> @@ -443,6 +446,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Unlock to use NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organisation"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Swipe from icon for phone"</string> <string name="voice_hint" msgid="7476017460191291417">"Swipe from icon for voice assist"</string> <string name="camera_hint" msgid="4519495795000658637">"Swipe from icon for camera"</string> @@ -463,9 +468,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"End Guest session"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> @@ -540,6 +546,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organisation installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Your admin has turned on network logging, which monitors traffic on your device."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"You\'re connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"You\'re connected to <xliff:g id="VPN_APP_0">%1$s</xliff:g> and <xliff:g id="VPN_APP_1">%2$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string> @@ -620,6 +627,7 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tap to set to vibrate."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tap to mute."</string> + <string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string> @@ -733,7 +741,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically <b>demoted to silent</b> by the system."</string> <string name="feedback_promoted" msgid="2125562787759780807">"This notification was automatically <b>ranked higher</b> in your shade."</string> <string name="feedback_demoted" msgid="951884763467110604">"This notification was automatically <b>ranked lower</b> in your shade."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Let the developer know your feedback. Was this correct?"</string> <string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string> @@ -880,6 +888,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string> @@ -970,7 +980,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Phone call"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"location"</string> @@ -978,7 +988,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors off"</string> <string name="device_services" msgid="1549944177856658705">"Device Services"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Tap to restart this app and go full screen."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Move"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"System navigation updated. To make changes, go to Settings."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Go to Settings to update system navigation"</string> @@ -1032,6 +1041,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Add"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Suggested by <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Controls updated"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Device locked"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN contains letters or symbols"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verify <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Wrong PIN"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 88a40c346e79..0a0f8f7aab1e 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Scroll screenshot"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dismiss screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Top boundary"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Bottom boundary"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> @@ -410,6 +412,7 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduce brightness"</string> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string> @@ -443,6 +446,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Unlock to use NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organisation"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Swipe from icon for phone"</string> <string name="voice_hint" msgid="7476017460191291417">"Swipe from icon for voice assist"</string> <string name="camera_hint" msgid="4519495795000658637">"Swipe from icon for camera"</string> @@ -463,9 +468,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"End Guest session"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> @@ -540,6 +546,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organisation installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Your admin has turned on network logging, which monitors traffic on your device."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"You\'re connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"You\'re connected to <xliff:g id="VPN_APP_0">%1$s</xliff:g> and <xliff:g id="VPN_APP_1">%2$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps and websites."</string> @@ -620,6 +627,7 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tap to set to vibrate."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tap to mute."</string> + <string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string> @@ -733,7 +741,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically <b>demoted to silent</b> by the system."</string> <string name="feedback_promoted" msgid="2125562787759780807">"This notification was automatically <b>ranked higher</b> in your shade."</string> <string name="feedback_demoted" msgid="951884763467110604">"This notification was automatically <b>ranked lower</b> in your shade."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Let the developer know your feedback. Was this correct?"</string> <string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string> @@ -880,6 +888,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string> @@ -970,7 +980,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Phone call"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"location"</string> @@ -978,7 +988,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors off"</string> <string name="device_services" msgid="1549944177856658705">"Device Services"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Tap to restart this app and go full screen."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Move"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"System navigation updated. To make changes, go to Settings."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Go to Settings to update system navigation"</string> @@ -1032,6 +1041,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Add"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Suggested by <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Controls updated"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Device locked"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN contains letters or symbols"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verify <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Wrong PIN"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index a14566ac0c1a..a665fb12b662 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Scroll screenshot"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dismiss screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Top boundary"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Bottom boundary"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string> @@ -410,6 +412,7 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Until sunrise"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"On at <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Until <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Reduce brightness"</string> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string> @@ -443,6 +446,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Unlock to use NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organization"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"This device belongs to <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Swipe from icon for phone"</string> <string name="voice_hint" msgid="7476017460191291417">"Swipe from icon for voice assist"</string> <string name="camera_hint" msgid="4519495795000658637">"Swipe from icon for camera"</string> @@ -463,9 +468,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Add user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End guest session?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"End guest session"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start over"</string> @@ -540,6 +546,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Your organization installed a certificate authority in your work profile. Your secure network traffic may be monitored or modified."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"A certificate authority is installed on this device. Your secure network traffic may be monitored or modified."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Your admin has turned on network logging, which monitors traffic on your device."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Your admin has turned on network logging, which monitors traffic in your work profile but not in your personal profile."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"You\'re connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"You\'re connected to <xliff:g id="VPN_APP_0">%1$s</xliff:g> and <xliff:g id="VPN_APP_1">%2$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Your work profile is connected to <xliff:g id="VPN_APP">%1$s</xliff:g>, which can monitor your network activity, including emails, apps, and websites."</string> @@ -620,6 +627,7 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tap to set to vibrate."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tap to mute."</string> + <string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string> @@ -733,7 +741,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically <b>demoted to Silent</b> by the system."</string> <string name="feedback_promoted" msgid="2125562787759780807">"This notification was automatically <b>ranked higher</b> in your shade."</string> <string name="feedback_demoted" msgid="951884763467110604">"This notification was automatically <b>ranked lower</b> in your shade."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Was this correct?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Let the developer know your feedback. Was this correct?"</string> <string name="feedback_response" msgid="4671729244976641339">"Thanks for your feedback!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Notification controls for <xliff:g id="APP_NAME">%1$s</xliff:g> opened"</string> @@ -880,6 +888,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Move to <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Add to position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tile added"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tile removed"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Quick settings editor."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> notification: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Open settings."</string> @@ -970,7 +980,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> is using the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> used the <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recently"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Phone call"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(through <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"location"</string> @@ -978,7 +988,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensors off"</string> <string name="device_services" msgid="1549944177856658705">"Device Services"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"No title"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Tap to restart this app and go full screen."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Move"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"System navigation updated. To make changes, go to Settings."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Go to Settings to update system navigation"</string> @@ -1032,6 +1041,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Add"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Suggested by <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Controls updated"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Device locked"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN contains letters or symbols"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verify <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Wrong PIN"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 4b482b914164..c97194f1fe08 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Desplazar captura de pantalla"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Descartar captura de pantalla"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de la captura de pantalla"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Límite superior"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Límite inferior"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Grabadora de pantalla"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación pantalla"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación constante para una sesión de grabación de pantalla"</string> @@ -115,7 +117,7 @@ <string name="screenrecord_share_label" msgid="5025590804030086930">"Compartir"</string> <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Se canceló la grabación de pantalla"</string> <string name="screenrecord_save_message" msgid="490522052388998226">"Se guardó la grabación de pantalla; presiona para verla"</string> - <string name="screenrecord_delete_error" msgid="2870506119743013588">"Error al borrar la grabación de pantalla"</string> + <string name="screenrecord_delete_error" msgid="2870506119743013588">"No se pudo borrar la grabación de pantalla"</string> <string name="screenrecord_permission_error" msgid="7856841237023137686">"Error al obtener permisos"</string> <string name="screenrecord_start_error" msgid="2200660692479682368">"Error al iniciar la grabación de pantalla"</string> <string name="usb_preference_title" msgid="1439924437558480718">"Opciones de transferencia de archivos por USB"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hasta el amanecer"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"A la(s) <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hasta la(s) <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"La tecnología NFC está inhabilitada"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"La tecnología NFC está habilitada"</string> @@ -440,9 +444,11 @@ <string name="notification_tap_again" msgid="4477318164947497249">"Presiona de nuevo para abrir"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string> <string name="keyguard_retry" msgid="886802522584053523">"Desliza el dedo hacia arriba para volver a intentarlo"</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquee el dispositivo para usar NFC"</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquea el dispositivo para usar NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertenece a tu organización"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Desliza el dedo para desbloquear el teléfono."</string> <string name="voice_hint" msgid="7476017460191291417">"Desliza el dedo desde el ícono para abrir asistente de voz."</string> <string name="camera_hint" msgid="4519495795000658637">"Desliza el dedo para acceder a la cámara."</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Agregar usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Usuario nuevo"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"¿Quieres finalizar la sesión de invitado?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Finalizar sesión de invitado"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"¿Eliminar invitado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalizar sesión"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eliminar"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenido nuevamente, invitado."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres retomar la sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Tu organización instaló una autoridad de certificación en tu perfil de trabajo. Es posible que se controle o modifique el tráfico de tu red segura."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Hay una autoridad de certificación instalada en este dispositivo. Es posible que se controle o modifique el tráfico de tu red segura."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Tu administrador activó el registro de red, que supervisa el tráfico en tu dispositivo."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"El administrador activó el registro de red, que supervisa el tráfico de tu perfil de trabajo, pero no el de tu perfil personal."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Estás conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que puede controlar la actividad de tu red, incluidos los correos electrónicos, las apps y los sitios web."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Estás conectado a <xliff:g id="VPN_APP_0">%1$s</xliff:g> y <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que pueden controlar tu actividad de red, incluidos los correos electrónicos, las apps y los sitios web."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Tu perfil de trabajo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que puede controlar tu actividad de red, incluidos los correos electrónicos, las apps y los sitios web."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Presiona para silenciar. Es posible que los servicios de accesibilidad estén silenciados."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Presiona para establecer el modo vibración."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Presiona para silenciar."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"El sistema <b>descendió esta notificación a Silenciada</b> de forma automática."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Se clasificó esta notificación <b>en una posición superior</b> de forma automática en el panel."</string> <string name="feedback_demoted" msgid="951884763467110604">"Se clasificó esta notificación <b>en una posición inferior</b> de forma automática en el panel."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"¿Te parece bien?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Envía tus comentarios al desarrollador. ¿Te parece bien?"</string> <string name="feedback_response" msgid="4671729244976641339">"Gracias por tus comentarios."</string> <string name="feedback_ok" msgid="6481426753298857144">"Aceptar"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Se abrieron los controles de notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Agregar a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Se agregó la tarjeta"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Se quitó la tarjeta"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de Configuración rápida"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir Configuración"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está usando <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> usó <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recientemente"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresarial)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Teléfono"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Llamada telefónica"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(a través de <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"cámara"</string> <string name="privacy_type_location" msgid="7991481648444066703">"ubicación"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Se desactivaron los sensores"</string> <string name="device_services" msgid="1549944177856658705">"Servicios del dispositivo"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Sin título"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Presiona para reiniciar esta app y acceder al modo de pantalla completa."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mover"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Se actualizó el sistema de navegación. Para hacer cambios, ve a Configuración."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Ve a Configuración para actualizar la navegación del sistema"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Agregar"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Sugerido por <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Controles actualizados"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Dispos. bloqueado"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"El PIN contiene letras o símbolos"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verificar <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN incorrecto"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 36fee3f89da9..4c5ef0b090fc 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Hacer captura de pantalla continua"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Cerrar captura de pantalla"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de captura de pantalla"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Margen superior"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Margen inferior"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Grabación de pantalla"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación de pantalla"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación continua de una sesión de grabación de la pantalla"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hasta el amanecer"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"A las <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hasta las <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"El NFC está desactivado"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"El NFC está activado"</string> @@ -440,9 +444,11 @@ <string name="notification_tap_again" msgid="4477318164947497249">"Toca de nuevo para abrir"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string> <string name="keyguard_retry" msgid="886802522584053523">"Desliza el dedo hacia arriba para volverlo a intentar"</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquear para usar NFC"</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquea para usar el NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertenece a tu organización"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertenece a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Desliza desde el icono para abrir el teléfono"</string> <string name="voice_hint" msgid="7476017460191291417">"Desliza desde el icono para abrir asistente de voz"</string> <string name="camera_hint" msgid="4519495795000658637">"Desliza desde el icono para abrir la cámara"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Añadir usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuevo usuario"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"¿Finalizar sesión de invitado?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Finalizar sesión de invitado"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"¿Quitar invitado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalizar sesión"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Quitar"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Hola de nuevo, invitado"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres continuar con la sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Tu organización ha instalado una entidad de certificación en tu perfil de trabajo. Es posible que se supervise o se modifique tu tráfico de red seguro."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Se ha instalado una entidad de certificación en este dispositivo. Es posible que se supervise o se modifique tu tráfico de red seguro."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"El administrador ha activado el registro de la red para supervisar el tráfico en tu dispositivo."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Tu administrador ha activado el registro de la red, por lo que se monitorizará el tráfico de tu perfil de trabajo, aunque no el de tu perfil personal."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Te has conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que puede supervisar tu actividad de red, como los correos electrónicos, las aplicaciones y los sitios web."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Te has conectado a <xliff:g id="VPN_APP_0">%1$s</xliff:g> y <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que pueden supervisar tu actividad de red, como los correos electrónicos, las aplicaciones y los sitios web."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Tu perfil de trabajo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que puede supervisar tu actividad de red, como los correos electrónicos, las aplicaciones y los sitios web."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca para silenciar. Los servicios de accesibilidad pueden silenciarse."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Toca para activar la vibración."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toca para silenciar."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"El sistema ha <b>disminuido automáticamente a Silencio</b> la importancia de esta notificación."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Esta notificación se ha colocado automáticamente en una <b>posición más alta</b> en tu pantalla de notificaciones."</string> <string name="feedback_demoted" msgid="951884763467110604">"Esta notificación se ha colocado automáticamente en una <b>posición más baja</b> en tu pantalla de notificaciones."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"¿Estuvo bien?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Envía tus comentarios al desarrollador. ¿Ha estado bien?"</string> <string name="feedback_response" msgid="4671729244976641339">"Gracias por tus comentarios."</string> <string name="feedback_ok" msgid="6481426753298857144">"Aceptar"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Se han abierto los controles de las notificaciones de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Añadir a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tarjeta añadida"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tarjeta quitada"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de ajustes rápidos."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir ajustes."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está usando este elemento: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ha usado recientemente este elemento: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresa)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Llamada telefónica"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Llamada telefónica"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(a través de <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"cámara"</string> <string name="privacy_type_location" msgid="7991481648444066703">"ubicación"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensores desactivados"</string> <string name="device_services" msgid="1549944177856658705">"Servicios del dispositivo"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Sin título"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Toca para reiniciar esta aplicación e ir a la pantalla completa."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mover"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Se ha actualizado la navegación del sistema. Para hacer cambios, ve a Ajustes."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Ve a Ajustes para actualizar la navegación del sistema"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Añadir"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Sugerido por <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Controles actualizados"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Dispositivo bloqueado"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"El PIN contiene letras o símbolos"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verifica <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN incorrecto"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index ff043e0872af..65c5930d29d3 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Ekraanipildi kerimine"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ekraanipildist loobumine"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekraanipildi eelvaade"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Ülempiir"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Alampiir"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Ekraanisalvesti"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekraanisalvestuse töötlemine"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pooleli märguanne ekraanikuva salvestamise seansi puhul"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Kuni päikesetõusuni"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Sisse kell <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Kuni <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC on keelatud"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC on lubatud"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC kasutamiseks avage."</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"See seade kuulub teie organisatsioonile"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Selle seadme omanik on <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Telefoni kasutamiseks pühkige ikoonilt eemale"</string> <string name="voice_hint" msgid="7476017460191291417">"Häälabi kasutamiseks pühkige ikoonilt eemale"</string> <string name="camera_hint" msgid="4519495795000658637">"Kaamera kasutamiseks pühkige ikoonilt eemale"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Kuva profiil"</string> <string name="user_add_user" msgid="4336657383006913022">"Lisa kasutaja"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Uus kasutaja"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Kas lõpetada külastajaseanss?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Lõpeta külastajaseanss"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Kas eemaldada külaline?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Lõpeta seanss"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eemalda"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tere tulemast tagasi, külaline!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Kas soovite seansiga jätkata?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Alusta uuesti"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Teie organisatsioon installis teie tööprofiilile sertifikaadi volituse. Teie turvalist võrguliiklust võidakse jälgida ja muuta."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Sertifikaadi volitus on sellesse seadmesse installitud. Teie turvalist võrguliiklust võidakse jälgida ja muuta."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Teie administraator lülitas sisse võrgu logimise funktsiooni, mis jälgib teie seadmes liiklust."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Teie administraator on sisse lülitanud võrgu logimise funktsiooni, mis jälgib liiklust teie võrguprofiilil, kuid mitte teie isiklikul profiilil."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Teil on ühendus rakendusega <xliff:g id="VPN_APP">%1$s</xliff:g>, mis saab jälgida teie võrgutegevusi, sh meile, rakendusi ja veebisaite."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Teil on ühendus rakendustega <xliff:g id="VPN_APP_0">%1$s</xliff:g> ja <xliff:g id="VPN_APP_1">%2$s</xliff:g>, mis saavad jälgida teie võrgutegevusi, sh meile, rakendusi ja veebisaite."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Teie tööprofiil on ühendatud rakendusega <xliff:g id="VPN_APP">%1$s</xliff:g>, mis saab jälgida teie võrgutegevusi, sh meile, rakendusi ja veebisaite."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Puudutage vaigistamiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Puudutage vibreerimise määramiseks."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Puudutage vaigistamiseks."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vaigistamine"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vaigistuse tühistamine"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreerimine"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Süsteem määras sellele märguandele automaatselt prioriteedi <b>Vaikne</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Sellele märguandele määrati teie märguandealas automaatselt <b>kõrgem prioriteet</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Sellele märguandele määrati teie märguandealas automaatselt <b>madalam prioriteet</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Kas see oli õige?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Andke arendajale tagasisidet. Kas see oli õige?"</string> <string name="feedback_response" msgid="4671729244976641339">"Täname tagasiside eest!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> märguannete juhtelemendid on avatud"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Teisaldamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Asend <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Paan on lisatud"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Paan on eemaldatud"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kiirseadete redigeerija."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Teenuse <xliff:g id="ID_1">%1$s</xliff:g> märguanne: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ava seaded."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> kasutab järgmist: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> kasutas hiljuti järgmist: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ettevõte)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonikõne"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonikõne"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(üksuse <xliff:g id="ATTRIBUTION">%s</xliff:g> kaudu)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kaamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"asukoht"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Andurid on välja lülitatud"</string> <string name="device_services" msgid="1549944177856658705">"Seadme teenused"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Pealkiri puudub"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Puudutage rakenduse taaskäivitamiseks ja täisekraanrežiimi aktiveerimiseks."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Teisalda"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Süsteemis navigeerimine on värskendatud. Muutmiseks avage jaotis Seaded."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Süsteemi navigeerimise värskendamiseks avage jaotis Seaded"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Lisa"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Soovitas <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Juhtelemente värskendati"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Seade on lukustatud"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN-kood sisaldab tähti või sümboleid"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Kinnitage <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Vale PIN-kood"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 7517f17623fc..9cebde110938 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Pantaila-argazki etengabea"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Baztertu pantaila-argazkia"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pantaila-argazkiaren aurrebista"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Goiko ertza"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Beheko ertza"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Pantaila-grabagailua"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pantaila-grabaketa prozesatzen"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pantailaren grabaketa-saioaren jakinarazpen jarraitua"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Egunsentira arte"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktibatze-ordua: <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Desaktibatze-ordua: <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Desgaituta dago NFC"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Gaituta dago NFC"</string> @@ -440,9 +444,11 @@ <string name="notification_tap_again" msgid="4477318164947497249">"Irekitzeko, ukitu berriro"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Pasatu hatza gora irekitzeko"</string> <string name="keyguard_retry" msgid="886802522584053523">"Berriro saiatzeko, pasatu hatza gora"</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desblokeatu NFC erabiltzeko"</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desblokea ezazu NFC erabiltzeko"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Gailu hau zure erakundearena da"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Gailu hau <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> erakundearena da"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Pasatu hatza ikonotik, telefonoa irekitzeko"</string> <string name="voice_hint" msgid="7476017460191291417">"Pasatu hatza ikonotik, ahots-laguntza irekitzeko"</string> <string name="camera_hint" msgid="4519495795000658637">"Pasatu hatza ikonotik, kamera irekitzeko"</string> @@ -458,14 +464,15 @@ <string name="keyguard_indication_charging_time_fast" msgid="7895986003578341126">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bizkor kargatzen (<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte)"</string> <string name="keyguard_indication_charging_time_slowly" msgid="245442950133408398">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mantso kargatzen (<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte)"</string> <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string> - <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Aldatu erabiltzailez. <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> da saioa hasita duena."</string> - <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Uneko erabiltzailea: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> + <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Aldatu erabiltzailea. <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> da saioa hasita daukana."</string> + <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Erabiltzailea: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string> <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Erakutsi profila"</string> <string name="user_add_user" msgid="4336657383006913022">"Gehitu erabiltzailea"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Erabiltzaile berria"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Gonbidatuentzako saioa amaitu nahi duzu?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Amaitu gonbidatuentzako saioa"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gonbidatua kendu nahi duzu?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Amaitu saioa"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kendu"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Ongi etorri berriro, gonbidatu hori!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Saioarekin jarraitu nahi duzu?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Hasi berriro"</string> @@ -474,7 +481,7 @@ <string name="guest_notification_text" msgid="4202692942089571351">"Aplikazioak eta datuak ezabatzeko, kendu gonbidatua"</string> <string name="guest_notification_remove_action" msgid="4153019027696868099">"KENDU GONBIDATUA"</string> <string name="user_logout_notification_title" msgid="3644848998053832589">"Amaitu erabiltzailearen saioa"</string> - <string name="user_logout_notification_text" msgid="7441286737342997991">"Amaitu uneko erabiltzailearen saioa"</string> + <string name="user_logout_notification_text" msgid="7441286737342997991">"Amaitu erabiltzailearen saioa"</string> <string name="user_logout_notification_action" msgid="7974458760719361881">"AMAITU ERABILTZAILEAREN SAIOA"</string> <string name="user_add_user_title" msgid="4172327541504825032">"Beste erabiltzaile bat gehitu?"</string> <string name="user_add_user_message_short" msgid="2599370307878014791">"Erabiltzaile bat gehitzen duzunean, horrek bere eremua konfiguratu beharko du.\n\nEdozein erabiltzailek egunera ditzake beste erabiltzaile guztien aplikazioak."</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Erakundeak ziurtagiri-emaile bat instalatu dizu laneko profilean. Baliteke sareko trafiko segurua gainbegiratzea edo aldatzea."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Ziurtagiri-emaile bat dago instalatuta gailuan. Baliteke sareko trafiko segurua gainbegiratzea edo aldatzea."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratzaileak sare-erregistroak aktibatu ditu; horrela, zure gailuko trafikoa gainbegira dezake."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratzaileak sarearen erregistroak aktibatu ditu; horrela, zure laneko profileko trafikoa gainbegira dezake, baina ez zure profil pertsonalekoa."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora konektatuta zaude eta hark sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> eta <xliff:g id="VPN_APP_1">%2$s</xliff:g> aplikazioetara konektatuta zaude, eta haiek sareko jarduerak gainbegira ditzakete, mezu elektronikoak, aplikazioak eta webguneak barne."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"<xliff:g id="VPN_APP">%1$s</xliff:g> aplikaziora dago konektatuta laneko profila, eta aplikazio horrek sareko jarduerak gainbegira ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sakatu audioa desaktibatzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Sakatu hau dardara ezartzeko."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Sakatu hau audioa desaktibatzeko."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desaktibatu audioa"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktibatu audioa"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dardara"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Jakinarazpen hau automatikoki <b>aldatu da soinurik gabeko modura</b> du sistemak."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Jakinarazpen hau automatikoki <b>igo da mailaz</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Jakinarazpen hau automatikoki <b>jaitsi da mailaz</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Zuzena al da hau?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Jakinarazi oharrak garatzaileari. Zuzena al da?"</string> <string name="feedback_response" msgid="4671729244976641339">"Mila esker iritzia emateagatik!"</string> <string name="feedback_ok" msgid="6481426753298857144">"Ados"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Ireki dira <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak kontrolatzeko aukerak"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren lekura"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren lekuan"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>garren lekua"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Gehitu da lauza"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kendu da lauza"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ezarpen bizkorren editorea."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> zerbitzuaren jakinarazpena: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ireki ezarpenak."</string> @@ -947,7 +959,7 @@ <string name="running_foreground_services_msg" msgid="3009459259222695385">"Sakatu bateria eta datuen erabilerari buruzko xehetasunak ikusteko"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"Datu-konexioa desaktibatu nahi duzu?"</string> <string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> erabilita ezingo dituzu erabili datuak edo Internet. Wifi-sare baten bidez soilik konektatu ahal izango zara Internetera."</string> - <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"Uneko operadorea"</string> + <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"Zure operadorea"</string> <string name="touch_filtered_warning" msgid="8119511393338714836">"Aplikazio bat baimen-eskaera oztopatzen ari denez, ezarpenek ezin dute egiaztatu erantzuna."</string> <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> aplikazioaren zatiak erakusteko baimena eman nahi diozu <xliff:g id="APP_0">%1$s</xliff:g> aplikazioari?"</string> <string name="slice_permission_text_1" msgid="6675965177075443714">"- <xliff:g id="APP">%1$s</xliff:g> aplikazioaren informazioa irakur dezake."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> aplikazioa <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> erabiltzen ari da"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> aplikazioak <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> erabili du duela gutxi"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enpresa)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefono-deia"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefono-deia"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> aplikazioaren bidez)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"kokapena"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sentsoreak desaktibatuta daude"</string> <string name="device_services" msgid="1549944177856658705">"Gailuetarako zerbitzuak"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Ez du izenik"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Berrabiarazi aplikazio hau eta ezarri pantaila osoko modua."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Eraman"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Eguneratu da sistemaren nabigazioa. Aldaketak egiteko, joan Ezarpenak atalera."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Sistemaren nabigazioa eguneratzeko, joan Ezarpenak atalera"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Gehitu"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> aplikazioak iradoki du"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Eguneratu dira kontrolatzeko aukerak"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Gailua blokeatuta"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN kodeak hizkiak edo ikurrak ditu"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Egiaztatu <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN okerra"</string> @@ -1043,8 +1055,8 @@ <string name="controls_structure_tooltip" msgid="4355922222944447867">"Pasatu hatza aukera gehiago ikusteko"</string> <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Gomendioak kargatzen"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multimedia-edukia"</string> - <string name="controls_media_close_session" msgid="3957093425905475065">"Ezkutatu uneko saioa."</string> - <string name="controls_media_active_session" msgid="1984383994625845642">"Ezin da ezkutatu uneko saioa."</string> + <string name="controls_media_close_session" msgid="3957093425905475065">"Ezkutatu saioa."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Ezin da ezkutatu saioa."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Baztertu"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Berrekin"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Ezarpenak"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 231eb45fef64..04b14f1c482b 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"نماگرفت پیمایشی"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"رد کردن نماگرفت"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"پیشنمایش نماگرفت"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"مرز بالایی"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"مرز پایینی"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ضبطکننده صفحهنمایش"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"درحال پردازش ضبط صفحهنمایش"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"اعلان درحال انجام برای جلسه ضبط صفحهنمایش"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"تا طلوع آفتاب"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ساعت <xliff:g id="TIME">%s</xliff:g> روشن میشود"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"تا<xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"ارتباط میدان نزدیک (NFC)"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"«ارتباط میدان نزدیک» (NFC) غیرفعال است"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"«ارتباط میدان نزدیک» (NFC) فعال است"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"برای استفاده از NFC، قفل را باز کنید"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"این دستگاه به سازمان شما تعلق دارد"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"این دستگاه به <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> تعلق دارد"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"انگشتتان را از نماد تلفن تند بکشید"</string> <string name="voice_hint" msgid="7476017460191291417">"برای «دستیار صوتی»، تند بکشید"</string> <string name="camera_hint" msgid="4519495795000658637">"انگشتتان را از نماد دوربین تند بکشید"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"نمایش نمایه"</string> <string name="user_add_user" msgid="4336657383006913022">"افزودن کاربر"</string> <string name="user_new_user_name" msgid="2019166282704195789">"کاربر جدید"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"جلسه مهمان تمام شود؟"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"پایان دادن به جلسه مهمان"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"مهمان حذف شود؟"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامهها و دادههای این جلسه حذف خواهد شد."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"پایان جلسه"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"حذف"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مهمان گرامی، بازگشتتان را خوش آمد میگوییم!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"آیا میخواهید جلسهتان را ادامه دهید؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"شروع مجدد"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"سازمان شما مرجع گواهینامهای در نمایه کاری شما نصب کرده است. ممکن است ترافیک امن شبکه شما پایش یا تغییر داده شود."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"مرجع گواهینامهای در این دستگاه نصب شده است. ممکن است ترافیک امن شبکه شما پایش یا تغییر داده شود."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"سرپرست سیستم شما گزارشگیری از شبکه را (که ترافیک دستگاه شما را پایش میکند) روشن کرده است."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"سرپرست شما گزارشگیری شبکه را که بر ترافیک نمایه کاریتان نظارت میکند، اما بر ترافیک نمایه شخصیتان نظارت نمیکند روشن کرده است."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل شدهاید، که میتواند فعالیت شبکه شما را (ازجمله ایمیلها، برنامهها و وبسایتها) پایش کند."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"به <xliff:g id="VPN_APP_0">%1$s</xliff:g> و <xliff:g id="VPN_APP_1">%2$s</xliff:g> متصل شدهاید، که میتوانند فعالیت شما را در شبکه (ازجمله ایمیلها، برنامهها و وبسایتها) پایش کنند."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"نمایه کاری شما به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل است، که میتواند فعالیت شما در شبکه (ازجمله ایمیلها، برنامهها و وبسایتها) را پایش کند."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. برای صامت کردن ضربه بزنید. ممکن است سرویسهای دسترسپذیری صامت شود."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. برای تنظیم روی لرزش، ضربه بزنید."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. برای صامت کردن ضربه بزنید."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"صامت کردن"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"باصدا کردن"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"لرزش"</string> @@ -709,9 +719,9 @@ <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"سیستم را تنظیم کنید که تشخیص دهد اعلان صدا و لرزش داشته باشد یا نه"</string> <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>وضعیت:</b> به «پیشفرض» ارتقا یافت"</string> <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>وضعیت:</b> به «بیصدا» تنزل یافت"</string> - <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>وضعیت:</b> در رتبهبندی بالاتری قرار گرفت"</string> - <string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>وضعیت:</b> در رتبهبندی پایینتری قرار گرفت"</string> - <string name="notification_channel_summary_priority" msgid="7952654515769021553">"در بالای بخش مکالمه بهصورت حبابک شناور نشان داده میشود و تصویر نمایه را در صفحه قفل نمایش میدهد"</string> + <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>وضعیت:</b> در ردهبندی بالاتری قرار گرفت"</string> + <string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>وضعیت:</b> در ردهبندی پایینتری قرار گرفت"</string> + <string name="notification_channel_summary_priority" msgid="7952654515769021553">"در بالای بخش مکالمه بهصورت حبابک شناور نشان داده میشود و عکس نمایه را در صفحه قفل نمایش میدهد"</string> <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"تنظیمات"</string> <string name="notification_priority_title" msgid="2079708866333537093">"اولویت"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> از ویژگیهای مکالمه پشتیبانی نمیکند"</string> @@ -731,9 +741,9 @@ <string name="notification_appops_ok" msgid="2177609375872784124">"تأیید"</string> <string name="feedback_alerted" msgid="5192459808484271208">"سیستم این اعلان را بهطور خودکار <b>به «پیشفرض» ارتقا داد</b>."</string> <string name="feedback_silenced" msgid="9116540317466126457">"سیستم این اعلان را بهطور خودکار <b>به «بیصدا» تنزل داد</b>."</string> - <string name="feedback_promoted" msgid="2125562787759780807">"این اعلان بهطور خودکار در کشوی اعلانات <b>در رتبهبندی بالاتری قرار گرفت</b>."</string> - <string name="feedback_demoted" msgid="951884763467110604">"این اعلان بهطور خودکار در کشوی اعلانات <b>در رتبهبندی پایینتری قرار گرفت</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"این مورد درست بود؟"</string> + <string name="feedback_promoted" msgid="2125562787759780807">"این اعلان بهطور خودکار در کشوی اعلانات <b>در ردهبندی بالاتری قرار گرفت</b>."</string> + <string name="feedback_demoted" msgid="951884763467110604">"این اعلان بهطور خودکار در کشوی اعلانات <b>در ردهبندی پایینتری قرار گرفت</b>."</string> + <string name="feedback_prompt" msgid="3656728972307896379">"بازخوردتان را به اطلاع توسعهدهنده برسانید. این مورد درست بود؟"</string> <string name="feedback_response" msgid="4671729244976641339">"از بازخوردتان سپاسگزاریم!"</string> <string name="feedback_ok" msgid="6481426753298857144">"تأیید"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"کنترلهای اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> باز شد"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"انتقال به <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"افزودن به موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"کاشی اضافه شد"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"کاشی حذف شد"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ویرایشگر تنظیمات سریع."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"اعلان <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"باز کردن تنظیمات."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> درحال استفاده از <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> است"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> اخیراً از <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> استفاده کرده است"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(شرکتی)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"تماس تلفنی"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(ازطریق <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"دوربین"</string> <string name="privacy_type_location" msgid="7991481648444066703">"مکان"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"حسگرها خاموش است"</string> <string name="device_services" msgid="1549944177856658705">"سرویسهای دستگاه"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"بدون عنوان"</string> - <string name="restart_button_description" msgid="6916116576177456480">"برای بازراهاندازی این برنامه و تغییر به حالت تمامصفحه، ضربه بزنید."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"انتقال"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"پیمایش سیستم بهروزرسانی شد. برای انجام تغییرات به «تنظیمات» بروید."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"برای بهروزرسانی پیمایش سیستم، به «تنظیمات» بروید"</string> @@ -986,7 +997,7 @@ <string name="priority_onboarding_title" msgid="2893070698479227616">"مکالمه روی اولویت تنظیم شده است"</string> <string name="priority_onboarding_behavior" msgid="5342816047020432929">"مکالمههای اولویتدار:"</string> <string name="priority_onboarding_show_at_top_text" msgid="1678400241025513541">"نمایش در بالای بخش مکالمه"</string> - <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"تصویر نمایه را در صفحه قفل نمایش میدهد"</string> + <string name="priority_onboarding_show_avatar_text" msgid="5756291381124091508">"عکس نمایه را در صفحه قفل نمایش میدهد"</string> <string name="priority_onboarding_appear_as_bubble_text" msgid="4227039772250263122">"بهشکل حبابک شناور روی برنامهها ظاهر میشود"</string> <string name="priority_onboarding_ignores_dnd_text" msgid="2918952762719600529">"وقفه در «مزاحم نشوید»"</string> <string name="priority_onboarding_done_button_title" msgid="4569550984286506007">"متوجهام"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"افزودن"</string> <string name="controls_dialog_message" msgid="342066938390663844">"پیشنهاد <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"کنترلها بهروزرسانی شد"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"دستگاه قفل است"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"پین شامل حروف یا نماد است"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"تأیید <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"پین اشتباه است"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index a6c43d96ae81..d08bfb5daabb 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Vieritä kuvakaappausta"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Hylkää kuvakaappaus"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Kuvakaappauksen esikatselu"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Yläraja"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Alaraja"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Näytön tallentaja"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Näytön tallennusta käsitellään"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pysyvä ilmoitus näytön tallentamisesta"</string> @@ -123,7 +125,7 @@ <string name="use_ptp_button_title" msgid="7676427598943446826">"Käytä kamerana (PTP)"</string> <string name="installer_cd_button_title" msgid="5499998592841984743">"Asenna Android File Transfer -sovellus Macille"</string> <string name="accessibility_back" msgid="6530104400086152611">"Takaisin"</string> - <string name="accessibility_home" msgid="5430449841237966217">"Aloituspainike"</string> + <string name="accessibility_home" msgid="5430449841237966217">"Aloitus"</string> <string name="accessibility_menu" msgid="2701163794470513040">"Valikko"</string> <string name="accessibility_accessibility_button" msgid="4089042473497107709">"Esteettömyys"</string> <string name="accessibility_rotate_button" msgid="1238584767612362586">"Näytön kääntäminen"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Auringonnousuun"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Päälle klo <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> asti"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC on poistettu käytöstä"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC on käytössä"</string> @@ -440,9 +444,11 @@ <string name="notification_tap_again" msgid="4477318164947497249">"Avaa napauttamalla uudelleen"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Avaa pyyhkäisemällä ylös"</string> <string name="keyguard_retry" msgid="886802522584053523">"Yritä uudelleen pyyhkäisemällä ylös"</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Avaa lukitus käyttääksesi NFC:tä"</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Avaa lukitus, jotta voit käyttää NFC:tä"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Organisaatiosi omistaa tämän laitteen"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g> omistaa tämän laitteen"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Avaa puhelu pyyhkäisemällä."</string> <string name="voice_hint" msgid="7476017460191291417">"Avaa ääniapuri pyyhkäisemällä kuvakkeesta."</string> <string name="camera_hint" msgid="4519495795000658637">"Avaa kamera pyyhkäisemällä."</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Näytä profiili"</string> <string name="user_add_user" msgid="4336657383006913022">"Lisää käyttäjä"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Uusi käyttäjä"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Lopetetaanko Vierailija-käyttökerta?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Lopeta Vierailija-käyttökerta"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Poistetaaanko vieras?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Lopeta käyttökerta"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Poista"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tervetuloa takaisin!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Haluatko jatkaa istuntoa?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Aloita alusta"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisaatiosi lisäsi työprofiiliin varmenteen myöntäjän. Suojattua verkkoliikennettäsi voidaan valvoa tai muuttaa."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Laitteeseen on asennettu varmenteen myöntäjä. Suojattua verkkoliikennettäsi voidaan valvoa tai muuttaa."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Järjestelmänvalvoja on ottanut käyttöön verkkolokitietojen tallentamisen, joka valvoo laitteellasi tapahtuvaa liikennettä."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Järjestelmänvalvoja on ottanut käyttöön verkkolokitietojen tallentamisen. Sen avulla seurataan liikennettä työprofiilissasi mutta ei henkilökohtaisessa profiilissasi."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Olet yhteydessä sovellukseen <xliff:g id="VPN_APP">%1$s</xliff:g>, joka voi valvoa verkkotoimintaasi, esimerkiksi sähköposteja, sovelluksia ja verkkosivustoja."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Olet yhteydessä sovelluksiin <xliff:g id="VPN_APP_0">%1$s</xliff:g> ja <xliff:g id="VPN_APP_1">%2$s</xliff:g>, jotka voivat valvoa verkkotoimintaasi, esimerkiksi sähköposteja, sovelluksia ja verkkosivustoja."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Työprofiilisi on yhteydessä sovellukseen <xliff:g id="VPN_APP">%1$s</xliff:g>, joka voi valvoa toimintaasi verkossa, esimerkiksi sähköposteja, sovelluksia ja verkkosivustoja."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Mykistä koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Siirry värinätilaan napauttamalla."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Mykistä napauttamalla."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mykistä"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"poista mykistys"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"värinä"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Järjestelmä <b>hiljensi</b> tämän ilmoituksen automaattisesti."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Tämä ilmoitus valittiin automaattisesti <b>tärkeämmäksi</b> ilmoitusalueella."</string> <string name="feedback_demoted" msgid="951884763467110604">"Tämä ilmoitus valittiin automaattisesti <b>vähemmän tärkeäksi</b> ilmoitusalueella."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Oliko tämä oikein?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Anna kehittäjälle palautetta. Oliko tämä oikein?"</string> <string name="feedback_response" msgid="4671729244976641339">"Kiitos palautteesta!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Sovelluksen <xliff:g id="APP_NAME">%1$s</xliff:g> ilmoitusten hallinta on avattu."</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Siirrä paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisää paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Paikka <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kiekko lisätty"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kiekko poistettu"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Pika-asetusten muokkausnäkymä"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ilmoitus kohteesta <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Avaa asetukset."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> käyttää kohdetta <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> käytti kohdetta <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> äskettäin"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(yritys)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Puhelu"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Puhelu"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(kautta: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"sijainti"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Anturit pois päältä"</string> <string name="device_services" msgid="1549944177856658705">"Laitepalvelut"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Ei nimeä"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Napauta, niin sovellus käynnistyy uudelleen ja siirtyy koko näytön tilaan."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Siirrä"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Järjestelmän navigointitapa vaihdettu. Voit muuttaa sitä asetuksista."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Vaihda järjestelmän navigointitapaa asetuksista"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Lisää"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Ehdottaja: <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Säätimet päivitetty"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Laite lukittu"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN-koodi sisältää kirjaimia tai symboleja"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Vahvista <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Väärä PIN-koodi"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 07f8af5ad4e2..3d1ab93cb238 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Faire défiler la capture d\'écran"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Fermer la capture d\'écran"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Limite supérieure"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Limite inférieure"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Trait. de l\'enregist. d\'écran…"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement d\'écran"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Jusqu\'à l\'aube"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Actif à <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC désactivée"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC activée"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Déverrouillez l\'écran pour utiliser la NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Cet appareil appartient à votre organisation"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Balayez à partir de l\'icône pour accéder au téléphone"</string> <string name="voice_hint" msgid="7476017460191291417">"Balayez à partir de l\'icône pour accéder à l\'assist. vocale"</string> <string name="camera_hint" msgid="4519495795000658637">"Balayez à partir de l\'icône pour accéder à l\'appareil photo"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afficher le profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Mettre fin à la session d\'invité?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Mettre fin à la session d\'invité"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Supprimer l\'invité?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Fermer la session"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Supprimer"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recommencer"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Votre entreprise a installé une autorité de certification dans votre profil professionnel. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Une autorité de certification est installée sur cet appareil. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Votre administrateur a activé la journalisation réseau, qui surveille le trafic sur votre appareil."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Votre administrateur a activé la journalisation réseau, qui surveille le trafic dans votre profil professionnel, mais pas dans votre profil personnel."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Vous êtes connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>, qui peut contrôler votre activité réseau, y compris les courriels, les applications et les sites Web."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Vous êtes connecté à <xliff:g id="VPN_APP_0">%1$s</xliff:g> et à <xliff:g id="VPN_APP_1">%2$s</xliff:g>, qui peuvent contrôler votre activité sur le réseau, y compris l\'activité relative aux courriels, aux applications et aux sites Web."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Votre profil professionnel est connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>, qui peut contrôler votre activité réseau, y compris les courriels, les applications et les sites Web."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Touchez pour couper le son. Il est possible de couper le son des services d\'accessibilité."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Touchez pour activer les vibrations."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Touchez pour couper le son."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"désactiver le son"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"La notification a été automatiquement <b>abaissée à la catégorie Silencieux</b> par le système."</string> <string name="feedback_promoted" msgid="2125562787759780807">"La notification a été automatiquement <b>élevée d\'un niveau</b> dans votre volet."</string> <string name="feedback_demoted" msgid="951884763467110604">"La notification a été automatiquement <b>abaissée d\'un niveau</b> dans votre volet."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Était-ce correct?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Faites part de vos commentaires au concepteur. Était-ce correct?"</string> <string name="feedback_response" msgid="4671729244976641339">"Merci de vos commentaires!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Les paramètres des notifications pour <xliff:g id="APP_NAME">%1$s</xliff:g> sont ouverts"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tuile ajoutée"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tuile retirée"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de paramètres rapides."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> utilise cet élément : <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> a récemment utilisé cet élément : <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(entreprise)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Appel téléphonique"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Appel téléphonique"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(par l\'intermédiaire de <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"appareil photo"</string> <string name="privacy_type_location" msgid="7991481648444066703">"position"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Capteurs désactivés"</string> <string name="device_services" msgid="1549944177856658705">"Services de l\'appareil"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Sans titre"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Touchez pour redémarrer cette application et passer en plein écran."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Déplacer"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"La navigation système a été mise à jour. Pour apporter des modifications, accédez au menu Paramètres."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Accédez au menu Paramètres pour mettre à jour la navigation système"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Ajouter"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Suggestion de <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Commandes mises à jour"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Appareil verrouillé"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Le NIP contient des lettres ou des symboles"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Vérifier <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"NIP incorrect"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 7bbbff668710..c6d0674f97a1 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Faire défiler la capture d\'écran"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Fermer la capture d\'écran"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Limite supérieure"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Limite inférieure"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Enregistrement de l\'écran…"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement de l\'écran"</string> @@ -236,7 +238,7 @@ <skip /> <string name="accessibility_notification_dismissed" msgid="4411652015138892952">"Notification masquée"</string> <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Volet des notifications"</string> - <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Paramètres rapides"</string> + <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Réglages rapides"</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Écran de verrouillage"</string> <string name="accessibility_desc_settings" msgid="6728577365389151969">"Paramètres"</string> <string name="accessibility_desc_recent_apps" msgid="1748675199348914194">"Aperçu"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Jusqu\'à l\'aube"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"À partir de <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC désactivée"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"La technologie NFC est activée"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Déverrouillez l\'écran pour pouvoir utiliser NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Cet appareil appartient à votre organisation"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Cet appareil appartient à <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Balayer pour téléphoner"</string> <string name="voice_hint" msgid="7476017460191291417">"Balayer l\'écran depuis l\'icône pour l\'assistance vocale"</string> <string name="camera_hint" msgid="4519495795000658637">"Balayer pour prendre une photo"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afficher le profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Fermer la session Invité ?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Fermer la session Invité"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Supprimer l\'invité ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Fermer la session"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Supprimer"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la dernière session ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Non, nouvelle session"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Votre entreprise a installé une autorité de certification dans votre profil professionnel. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Une autorité de certification est installée sur cet appareil. Votre trafic sur le réseau sécurisé peut être contrôlé ou modifié."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Votre administrateur a activé la journalisation du réseau, pour contrôler le trafic sur votre appareil."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Votre administrateur a activé la journalisation réseau, qui surveille le trafic de votre profil professionnel, mais pas celui de votre profil personnel."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Vous êtes connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>, qui peut contrôler votre activité sur le réseau, y compris l\'activité relative aux e-mails, aux applications et aux sites Web."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Vous êtes connecté à <xliff:g id="VPN_APP_0">%1$s</xliff:g> et à <xliff:g id="VPN_APP_1">%2$s</xliff:g>, qui peuvent contrôler votre activité sur le réseau, y compris l\'activité relative aux e-mails, aux applications et aux sites Web."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Votre profil professionnel est connecté à <xliff:g id="VPN_APP">%1$s</xliff:g>, qui peut contrôler votre activité sur le réseau, y compris l\'activité relative aux e-mails, aux applications et aux sites Web."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Appuyez pour ignorer. Vous pouvez ignorer les services d\'accessibilité."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Appuyez pour mettre en mode vibreur."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Appuyez pour ignorer."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"couper le son"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"activer le vibreur"</string> @@ -635,7 +645,7 @@ <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string> <string name="show_battery_percentage" msgid="6235377891802910455">"Afficher le pourcentage intégré de la batterie"</string> <string name="show_battery_percentage_summary" msgid="9053024758304102915">"Affichez le pourcentage correspondant au niveau de la batterie dans l\'icône de la barre d\'état lorsque l\'appareil n\'est pas en charge."</string> - <string name="quick_settings" msgid="6211774484997470203">"Configuration rapide"</string> + <string name="quick_settings" msgid="6211774484997470203">"Réglages rapides"</string> <string name="status_bar" msgid="4357390266055077437">"Barre d\'état"</string> <string name="overview" msgid="3522318590458536816">"Aperçu"</string> <string name="demo_mode" msgid="263484519766901593">"Mode de démonstration de l\'interface du système"</string> @@ -645,13 +655,13 @@ <string name="status_bar_alarm" msgid="87160847643623352">"Alarme"</string> <string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string> <string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string> - <string name="add_tile" msgid="6239678623873086686">"Ajouter une tuile"</string> - <string name="broadcast_tile" msgid="5224010633596487481">"Tuile de diffusion"</string> + <string name="add_tile" msgid="6239678623873086686">"Ajouter un bloc"</string> + <string name="broadcast_tile" msgid="5224010633596487481">"Bloc de diffusion"</string> <string name="zen_alarm_warning_indef" msgid="5252866591716504287">"Vous n\'entendrez pas votre prochaine alarme <xliff:g id="WHEN">%1$s</xliff:g>, sauf si vous désactivez cette option avant."</string> <string name="zen_alarm_warning" msgid="7844303238486849503">"Vous n\'entendrez pas votre prochaine alarme <xliff:g id="WHEN">%1$s</xliff:g>."</string> <string name="alarm_template" msgid="2234991538018805736">"à <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="alarm_template_far" msgid="3561752195856839456">"le <xliff:g id="WHEN">%1$s</xliff:g>"</string> - <string name="accessibility_quick_settings_detail" msgid="544463655956179791">"Configuration rapide – <xliff:g id="TITLE">%s</xliff:g>"</string> + <string name="accessibility_quick_settings_detail" msgid="544463655956179791">"Réglages rapides – <xliff:g id="TITLE">%s</xliff:g>"</string> <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Point d\'accès"</string> <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil professionnel"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"Divertissant pour certains, mais pas pour tous"</string> @@ -664,8 +674,8 @@ <string name="activity_not_found" msgid="8711661533828200293">"L\'application n\'est pas installée sur votre appareil."</string> <string name="clock_seconds" msgid="8709189470828542071">"Afficher les secondes sur l\'horloge"</string> <string name="clock_seconds_desc" msgid="2415312788902144817">"Afficher les secondes dans la barre d\'état. Cela risque de réduire l\'autonomie de la batterie."</string> - <string name="qs_rearrange" msgid="484816665478662911">"Réorganiser la fenêtre de configuration rapide"</string> - <string name="show_brightness" msgid="6700267491672470007">"Afficher la luminosité dans fenêtre de configuration rapide"</string> + <string name="qs_rearrange" msgid="484816665478662911">"Réorganiser les Réglages rapides"</string> + <string name="show_brightness" msgid="6700267491672470007">"Afficher la luminosité dans les Réglages rapides"</string> <string name="experimental" msgid="3549865454812314826">"Paramètres expérimentaux"</string> <string name="enable_bluetooth_title" msgid="866883307336662596">"Activer le Bluetooth ?"</string> <string name="enable_bluetooth_message" msgid="6740938333772779717">"Pour connecter un clavier à votre tablette, vous devez avoir activé le Bluetooth."</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"La notification a été automatiquement <b>abaissée à la catégorie \"Silencieux\"</b> par le système."</string> <string name="feedback_promoted" msgid="2125562787759780807">"La notification a été automatiquement <b>élevée d\'un niveau</b> dans votre volet."</string> <string name="feedback_demoted" msgid="951884763467110604">"La notification a été automatiquement <b>abaissée d\'un niveau</b> dans votre volet."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Est-ce que c\'était correct ?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Faites part de vos commentaires au développeur. Est-ce que c\'était correct ?"</string> <string name="feedback_response" msgid="4671729244976641339">"Merci de nous avoir envoyé vos commentaires."</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Les commandes de notification sont disponibles pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -873,18 +883,20 @@ </string-array> <string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification à faible priorité"</string> <string name="other" msgid="429768510980739978">"Autre"</string> - <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"supprimer la carte"</string> - <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ajouter la carte à la fin"</string> - <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer la carte"</string> - <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ajouter une carte"</string> + <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"supprimer le bloc"</string> + <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ajouter le bloc à la fin"</string> + <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer le bloc"</string> + <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ajouter un bloc"</string> <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> - <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur de configuration rapide."</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Bloc ajouté"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Bloc supprimé"</string> + <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Éditeur Réglages rapides"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <xliff:g id="ID_1">%1$s</xliff:g> : <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ouvrir les paramètres."</string> - <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Ouvrir la fenêtre de configuration rapide."</string> - <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Fermer la fenêtre de configuration rapide."</string> + <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Ouvrir les Réglages rapides."</string> + <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Fermer les Réglages rapides."</string> <string name="accessibility_quick_settings_alarm_set" msgid="7237918261045099853">"Alarme définie."</string> <string name="accessibility_quick_settings_user" msgid="505821942882668619">"Connecté en tant que <xliff:g id="ID_1">%s</xliff:g>"</string> <string name="data_connection_no_internet" msgid="691058178914184544">"Aucun accès à Internet"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> utilise <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> a utilisé <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> récemment"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(Enterprise)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Appel téléphonique"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Appel téléphonique"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(via <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"appareil photo"</string> <string name="privacy_type_location" msgid="7991481648444066703">"position"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Capteurs désactivés"</string> <string name="device_services" msgid="1549944177856658705">"Services pour l\'appareil"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Sans titre"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Appuyez pour redémarrer cette application et activer le mode plein écran."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Déplacer"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigation système mise à jour. Pour apporter des modifications, accédez aux paramètres."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Accédez aux paramètres pour mettre à jour la navigation système"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Ajouter"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Suggérée par <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Commandes mises à jour"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Appareil verrouillé"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Le code contient des lettres ou des symboles"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Valider <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Code incorrect"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 0ac82616c4aa..08841a2ef866 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Realizar unha captura de pantalla continua"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ignorar a captura de pantalla"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa da captura de pantalla"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Bordo superior"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Bordo inferior"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Gravadora da pantalla"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando gravación pantalla"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación en curso sobre unha sesión de gravación de pantalla"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Ata o amencer"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Activarase ás: <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Utilizarase ata as: <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"A opción NFC está desactivada"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"A opción NFC está activada"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquea o dispositivo para utilizar a NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence á túa organización"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertence a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Pasa o dedo desde a icona para acceder ao teléfono"</string> <string name="voice_hint" msgid="7476017460191291417">"Pasa o dedo desde a icona para acceder ao asistente de voz"</string> <string name="camera_hint" msgid="4519495795000658637">"Pasa o dedo desde a icona para acceder á cámara"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Engadir usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuario"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Queres finalizar a sesión de invitado?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Finalizar sesión de invitado"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Queres eliminar o invitado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalizar sesión"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eliminar"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvido de novo, convidado."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Queres continuar coa túa sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Comezar de novo"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"A túa organización instalou unha autoridade de certificación no teu perfil de traballo. É posible que se controle ou se modifique o teu tráfico de rede segura."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Este dispositivo ten unha autoridade de certificación instalada. É posible que se controle ou se modifique o teu tráfico de rede segura."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"O administrador activou o rexistro na rede, que controla o tráfico do teu dispositivo."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"O administrador activou o rexistro na rede, que supervisa o tráfico do teu perfil de traballo, pero non o do perfil persoal."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Estás conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode controlar a túa actividade na rede, mesmo os correos electrónicos, as aplicacións e os sitios web."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Estás conectado a <xliff:g id="VPN_APP_0">%1$s</xliff:g> e a <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que poden controlar a túa actividade na rede, mesmo os correos electrónicos, as aplicacións e os sitios web."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"O teu perfil de traballo está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode controlar a túa actividade na rede, mesmo os correos electrónicos, as aplicacións e os sitios web."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca para silenciar. Pódense silenciar os servizos de accesibilidade."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Toca para establecer a vibración."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toca para silenciar."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activar o son"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"O sistema <b>diminuíu a Silencioso</b> o nivel desta notificación de forma automática."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Esta notificación <b>clasificouse nun nivel superior</b> de forma automática no teu ton."</string> <string name="feedback_demoted" msgid="951884763467110604">"Esta notificación <b>clasificouse nun nivel inferior</b> de forma automática no teu ton."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"A información era correcta?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Faille saber a túa opinión ao programador. A información era correcta?"</string> <string name="feedback_response" msgid="4671729244976641339">"Grazas polo teu comentario"</string> <string name="feedback_ok" msgid="6481426753298857144">"Aceptar"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Abríronse os controis de notificacións da aplicación <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engadir á posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Engadiuse a tarxeta"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Quitouse a tarxeta"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuración rápida."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configuración."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está utilizando a aplicación <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> utilizou recentemente a aplicación <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(versión empresarial)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Chamada de teléfono"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Chamada de teléfono"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(mediante <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"a cámara"</string> <string name="privacy_type_location" msgid="7991481648444066703">"a localiz."</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Desactivar sensores"</string> <string name="device_services" msgid="1549944177856658705">"Servizos do dispositivo"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Sen título"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Toca o botón para reiniciar esta aplicación e abrila en pantalla completa."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mover"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Actualizouse a navegación do sistema. Para facer cambios, vai a Configuración."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Para actualizar a navegación do sistema, vai a Configuración"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Engadir"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Control suxerido por <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Actualizáronse os controis"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Disposit. bloqueado"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"O PIN contén letras ou símbolos"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verificar <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"O PIN é incorrecto"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index f4e13332a34a..867bc2f6d38b 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"સ્ક્રીનશૉટ પર સ્ક્રોલ કરો"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"સ્ક્રીનશૉટ છોડી દો"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"સ્ક્રીનશૉટનો પ્રીવ્યૂ"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"સ્ક્રીનશૉટની સૌથી ઉપરની બાજુની સીમા"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"સ્ક્રીનશૉટની સૌથી નીચેની બાજુની સીમા"</string> <string name="screenrecord_name" msgid="2596401223859996572">"સ્ક્રીન રેકૉર્ડર"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"સ્ક્રીન રેકૉર્ડિંગ ચાલુ છે"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"સ્ક્રીન રેકોર્ડિંગ સત્ર માટે ચાલુ નોટિફિકેશન"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"સૂર્યોદય સુધી"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> વાગ્યે ચાલુ"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> વાગ્યા સુધી"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC અક્ષમ કરેલ છે"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC સક્ષમ કરેલ છે"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCનો ઉપયોગ કરવા માટે અનલૉક કરો"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"આ ડિવાઇસ તમારી સંસ્થાની માલિકીનું છે"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"આ ડિવાઇસ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>ની માલિકીનું છે"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ફોન માટે આયકનમાંથી સ્વાઇપ કરો"</string> <string name="voice_hint" msgid="7476017460191291417">"વૉઇસ સહાય માટે આયકનમાંથી સ્વાઇપ કરો"</string> <string name="camera_hint" msgid="4519495795000658637">"કૅમેરા માટે આયકનમાંથી સ્વાઇપ કરો"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"પ્રોફાઇલ બતાવો"</string> <string name="user_add_user" msgid="4336657383006913022">"વપરાશકર્તા ઉમેરો"</string> <string name="user_new_user_name" msgid="2019166282704195789">"નવો વપરાશકર્તા"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"શું અતિથિ સત્ર સમાપ્ત કરીએ?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"અતિથિ સત્ર સમાપ્ત કરો"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"અતિથિ દૂર કરીએ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ્લિકેશનો અને ડેટા કાઢી નાખવામાં આવશે."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"સત્ર સમાપ્ત કરો"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"દૂર કરો"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ફરી સ્વાગત છે, અતિથિ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"શું તમે તમારું સત્ર ચાલુ કરવા માંગો છો?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"શરૂ કરો"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"તમારી સંસ્થાએ તમારી કાર્ય પ્રોફાઇલમાં પ્રમાણપત્ર સત્તાધિકારી ઇન્સ્ટૉલ કર્યું છે. તમારા સુરક્ષિત નેટવર્ક ટ્રાફિકનું નિયમન થઈ શકે છે અથવા તેમાં ફેરફાર કરવામાં આવી શકે છે."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"આ ઉપકરણ પર પ્રમાણપત્ર સત્તાધિકારી ઇન્સ્ટૉલ કરેલ છે. તમારા સુરક્ષિત નેટવર્ક ટ્રાફિકનું નિયમન થઈ શકે છે અથવા તેમાં ફેરફાર કરવામાં આવી શકે છે."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"તમારા વ્યવસ્થાપકે નેટવર્ક લૉગિંગ ચાલુ કર્યું છે, જે તમારા ઉપકરણ પર નેટવર્ક ટ્રાફિકનું નિયમન કરે છે."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"તમારા વ્યવસ્થાપકે નેટવર્ક લૉગ ઇન ચાલુ કર્યુ છે, જે તમારી વ્યક્તિગત પ્રોફાઇલમાં નહીં, પરંતુ ઑફિસની પ્રોફાઇલમાં ટ્રાફિકનું નિરીક્ષણ કરે છે."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"તમે <xliff:g id="VPN_APP">%1$s</xliff:g> સાથે કનેક્ટ થયાં છો, જે ઇમેઇલ, ઍપ્લિકેશનો અને વેબસાઇટ સહિત તમારી નેટવર્ક પ્રવૃત્તિનું નિરીક્ષણ કરી શકે છે."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"તમે <xliff:g id="VPN_APP_0">%1$s</xliff:g> અને <xliff:g id="VPN_APP_1">%2$s</xliff:g> સાથે કનેક્ટ થયાં છો, જે ઇમેઇલ, ઍપ્લિકેશનો અને વેબસાઇટ સહિત તમારી નેટવર્ક પ્રવૃત્તિનું નિરીક્ષણ કરી શકે છે."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"તમારી કાર્યાલયની પ્રોફાઇલ <xliff:g id="VPN_APP">%1$s</xliff:g> સાથે કનેક્ટ કરેલ છે, જે ઇમેઇલ, ઍપ્લિકેશનો અને વેબસાઇટો સહિતની તમારી નેટવર્ક પ્રવૃત્તિનું નિયમન કરી શકે છે."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. મ્યૂટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. કંપન પર સેટ કરવા માટે ટૅપ કરો."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. મ્યૂટ કરવા માટે ટૅપ કરો."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"મ્યૂટ કરો"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"અનમ્યૂટ કરો"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"વાઇબ્રેટ"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"સિસ્ટમ દ્વારા આ નોટિફિકેશનને ઑટોમૅટિક રીતે <b>સાઇલન્ટ પર અવનત</b> કરવામાં આવ્યું."</string> <string name="feedback_promoted" msgid="2125562787759780807">"તમારા શેડમાં આ નોટિફિકેશનને ઑટોમૅટિક રીતે <b>ઉપલી રેંક</b> આપવામાં આવી."</string> <string name="feedback_demoted" msgid="951884763467110604">"તમારા શેડમાં આ નોટિફિકેશનને ઑટોમૅટિક રીતે <b>નીચલી રેંક</b> આપવામાં આવી."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"શું આ યોગ્ય હતું?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"ડેવલપરને તમારા પ્રતિસાદ વિશે જાણવા દો. શું આ યોગ્ય હતું?"</string> <string name="feedback_response" msgid="4671729244976641339">"તમારા પ્રતિસાદ બદલ આભાર!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ઓકે"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> માટે સૂચના નિયંત્રણો ચાલુ છે"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> પર ખસેડો"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"જગ્યા પર <xliff:g id="POSITION">%1$d</xliff:g> ઉમેરો"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"જગ્યા <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ટાઇલ ઉમેરી"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ટાઇલ કાઢી નાખી"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ઝડપી સેટિંગ્સ સંપાદક."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> નોટિફિકેશન: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"સેટિંગ્સ ખોલો."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>નો ઉપયોગ કરી રહી છે"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>એ તાજેતરમાં જ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>નો ઉપયોગ કર્યો છે"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(એન્ટરપ્રાઇઝ)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ફોન કૉલ"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ફોન કૉલ"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> મારફતે)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"કૅમેરા"</string> <string name="privacy_type_location" msgid="7991481648444066703">"સ્થાન"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"સેન્સર બંધ છે"</string> <string name="device_services" msgid="1549944177856658705">"ડિવાઇસ સેવાઓ"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"કોઈ શીર્ષક નથી"</string> - <string name="restart_button_description" msgid="6916116576177456480">"આ ઍપ ફરીથી ચાલુ કરવા માટે ટૅપ કરીને પૂર્ણ સ્ક્રીન કરો."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ખસેડો"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"સિસ્ટમ નૅવિગેશન અપડેટ કર્યું. ફેરફારો કરવા માટે, સેટિંગ પર જાઓ."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"સિસ્ટમ નૅવિગેશનને અપડેટ કરવા માટે સેટિંગ પર જાઓ"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"ઉમેરો"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> દ્વારા સૂચન કરેલા"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"નિયંત્રણ અપડેટ કર્યા"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"ડિવાઇસ લૉક કરેલું છે"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"પિનમાં અક્ષરો અથવા પ્રતીકોનો સમાવેશ થાય છે"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g>ને ચકાસો"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"ખોટો પિન"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 50b078f97ccb..a357ad2b09bc 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"स्क्रीनशॉट को स्क्रोल करें"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"स्क्रीनशॉट को खारिज करें"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉट की झलक"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"स्क्रीनशॉट को ऊपर से काटने की सीमा"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"स्क्रीनशॉट को नीचे से काटने की सीमा"</string> <string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रिकॉर्डर"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रिकॉर्डिंग को प्रोसेस किया जा रहा है"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रिकॉर्ड सेशन के लिए जारी सूचना"</string> @@ -412,6 +414,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सुबह तक चालू रहेगी"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> पर चालू हाेगी"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> तक चालू रहेगी"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"एनएफ़सी"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC बंद है"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC चालू है"</string> @@ -442,9 +446,11 @@ <string name="notification_tap_again" msgid="4477318164947497249">"खोलने के लिए फिर से टैप करें"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"खोलने के लिए ऊपर स्वाइप करें"</string> <string name="keyguard_retry" msgid="886802522584053523">"फिर से कोशिश करने के लिए ऊपर की ओर स्वाइप करें"</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"एनएफ़सी इस्तेमाल करने के लिए, स्क्रीन को अनलॉक करें"</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"एनएफ़सी इस्तेमाल करने के लिए स्क्रीन को अनलॉक करें"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"इस डिवाइस का मालिकाना हक आपके संगठन के पास है"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"इस डिवाइस का मालिकाना हक <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> के पास है"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"फ़ोन के लिए आइकॉन से स्वाइप करें"</string> <string name="voice_hint" msgid="7476017460191291417">"\'आवाज़ से डिवाइस का इस्तेमाल\' आइकॉन से स्वाइप करें"</string> <string name="camera_hint" msgid="4519495795000658637">"कैमरे के लिए आइकॉन से स्वाइप करें"</string> @@ -465,9 +471,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफ़ाइल दिखाएं"</string> <string name="user_add_user" msgid="4336657383006913022">"उपयोगकर्ता जोड़ें"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नया उपयोगकर्ता"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"मेहमान के तौर पर ब्राउज़ करने का सेशन खत्म करना चाहते हैं?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"मेहमान के तौर पर ब्राउज़ करने का सेशन खत्म करें"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथि को निकालें?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सत्र के सभी ऐप्स और डेटा को हटा दिया जाएगा."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"सेशन खत्म करें"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"निकालें"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथि, आपका फिर से स्वागत है!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"क्या आप अपना सत्र जारी रखना चाहते हैं?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"फिर से शुरू करें"</string> @@ -542,6 +549,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"आपके संगठन ने आपकी वर्क प्रोफ़ाइल में एक प्रमाणपत्र अनुमति इंस्टॉल की है. आपके सुरक्षित नेटवर्क ट्रैफ़िक की निगरानी या उसमें बदलाव किया जा सकता है."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"इस डिवाइस पर एक प्रमाणपत्र अनुमति इंस्टॉल की है. आपके सुरक्षित नेटवर्क ट्रैफ़िक की निगरानी या उसमें बदलाव किया जा सकता है."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"आपके व्यवस्थापक ने नेटवर्क लॉगिंग चालू किया है, जो आपके डिवाइस पर ट्रैफ़िक की निगरानी करता है."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"आपके एडमिन ने नेटवर्क लॉगिंग की सुविधा चालू कर दी है. इससे आपकी वर्क प्रोफ़ाइल पर आने वाले ट्रैफ़िक की निगरानी की जाती है. हालांकि, इससे आपकी निजी प्रोफ़ाइल की निगरानी नहीं की जाती."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"आप <xliff:g id="VPN_APP">%1$s</xliff:g> से कनेक्ट हैं, जो ईमेल, ऐप्लिकेशन और वेबसाइटों सहित आपकी नेटवर्क गतिविधि की निगरानी कर सकते हैं."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"आप <xliff:g id="VPN_APP_0">%1$s</xliff:g> और <xliff:g id="VPN_APP_1">%2$s</xliff:g> से कनेक्ट हैं, जो ईमेल, ऐप्लिकेशन और वेबसाइटों सहित आपकी नेटवर्क गतिविधि की निगरानी कर सकते हैं."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"आपकी वर्क प्रोफ़ाइल <xliff:g id="VPN_APP">%1$s</xliff:g> से कनेक्ट है, जो ईमेल, ऐप्लिकेशन और वेबसाइटों सहित आपकी नेटवर्क गतिविधि की निगरानी कर सकता है."</string> @@ -622,6 +630,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. कंपन (वाइब्रेशन) पर सेट करने के लिए छूएं."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. म्यूट करने के लिए टैप करें."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करें"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्यूट करें"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"वाइब्रेशन की सुविधा चालू करें"</string> @@ -735,7 +745,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"सिस्टम ने अपने-आप <b>इस सूचना का लेवल घटाकर, इसे साइलेंट</b> पर सेट किया था."</string> <string name="feedback_promoted" msgid="2125562787759780807">"आपकी शेड में, इस सूचना को अपने-आप <b>रैंकिंग में ऊपर</b> किया गया था."</string> <string name="feedback_demoted" msgid="951884763467110604">"आपकी शेड में, इस सूचना को अपने-आप <b>रैंकिंग में नीचे</b> किया गया था."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"आपको यह सुविधा कैसी लगी?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"डेवलपर को अपना सुझाव/राय दें. आपको यह सुविधा कैसी लगी?"</string> <string name="feedback_response" msgid="4671729244976641339">"सुझाव या शिकायत के लिए धन्यवाद!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ठीक है"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> के लिए सूचना नियंत्रण चालू हैं"</string> @@ -882,6 +892,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर ले जाएं"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर जोड़ें"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"टाइल की पोज़िशन <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"टाइल जोड़ी गई"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"टाइल हटाई गई"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"त्वरित सेटिंग संपादक."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिंग खोलें."</string> @@ -955,8 +967,8 @@ <string name="slice_permission_text_1" msgid="6675965177075443714">"- यह <xliff:g id="APP">%1$s</xliff:g> से सूचना पढ़ सकता है"</string> <string name="slice_permission_text_2" msgid="6758906940360746983">"- यह <xliff:g id="APP">%1$s</xliff:g> में कार्रवाई कर सकता है"</string> <string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g> को किसी भी ऐप्लिकेशन के हिस्से (स्लाइस) दिखाने की मंज़ूरी दें"</string> - <string name="slice_permission_allow" msgid="6340449521277951123">"मंज़ूरी दें"</string> - <string name="slice_permission_deny" msgid="6870256451658176895">"नामंज़ूर करें"</string> + <string name="slice_permission_allow" msgid="6340449521277951123">"अनुमति दें"</string> + <string name="slice_permission_deny" msgid="6870256451658176895">"अनुमति न दें"</string> <string name="auto_saver_title" msgid="6873691178754086596">"बैटरी सेवर शेड्यूल करने के लिए टैप करें"</string> <string name="auto_saver_text" msgid="3214960308353838764">"जब बैटरी खत्म होने वाली हो तब \'बैटरी सेवर\' चालू करें"</string> <string name="no_auto_saver_action" msgid="7467924389609773835">"जी नहीं, शुक्रिया"</string> @@ -971,16 +983,15 @@ <string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" और "</string> <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> का इस्तेमाल कर रहा है"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ने हाल ही में <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> का इस्तेमाल किया"</string> - <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"एंटरप्राइज़ वर्शन"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string> - <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"<xliff:g id="ATTRIBUTION">%s</xliff:g> के ज़रिए"</string> + <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(एंटरप्राइज़ वर्शन)"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"फ़ोन कॉल"</string> + <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> के ज़रिए)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"कैमरा"</string> <string name="privacy_type_location" msgid="7991481648444066703">"जगह"</string> <string name="privacy_type_microphone" msgid="9136763906797732428">"माइक्रोफ़ोन"</string> <string name="sensor_privacy_mode" msgid="4462866919026513692">"सेंसर बंद हैं"</string> <string name="device_services" msgid="1549944177856658705">"डिवाइस सेवाएं"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"कोई शीर्षक नहीं"</string> - <string name="restart_button_description" msgid="6916116576177456480">"इस ऐप्लिकेशन को रीस्टार्ट करने और फ़ुल स्क्रीन चालू करने के लिए टैप करें."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ले जाएं"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"सिस्टम नेविगेशन अपडेट हो गया. बदलाव करने के लिए \'सेटिंग\' पर जाएं."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"सिस्टम नेविगेशन अपडेट करने के लिए \'सेटिंग\' में जाएं"</string> @@ -1034,6 +1045,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"जोड़ें"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> से मिला सुझाव"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"कंट्रोल अपडेट किए गए"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"डिवाइस लॉक है"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"पिन में अक्षर या चिह्न शामिल होते हैं"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> की पुष्टि करें"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"गलत पिन"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index c80f26260fca..d8064ab2fb64 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Pomicanje snimke zaslona"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Odbacivanje snimke zaslona"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimke zaslona"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Gornja granica"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Donja granica"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Snimač zaslona"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrada snimanja zaslona"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Tekuća obavijest za sesiju snimanja zaslona"</string> @@ -412,6 +414,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do izlaska sunca"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Uključuje se u <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je onemogućen"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je omogućen"</string> @@ -445,6 +449,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Otključajte da biste upotrijebili NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Ovaj uređaj pripada vašoj organizaciji"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Ovaj uređaj pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Prijeđite prstom od ikone za telefon"</string> <string name="voice_hint" msgid="7476017460191291417">"Prijeđite prstom od ikone za glasovnu pomoć"</string> <string name="camera_hint" msgid="4519495795000658637">"Prijeđite prstom od ikone za fotoaparat"</string> @@ -465,9 +471,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaz profila"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodavanje korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Završiti gostujuću sesiju?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Završi gostujuću sesiju"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ukloniti gosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji bit će izbrisani."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Završi sesiju"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli natrag, gostu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> @@ -543,6 +550,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Vaša je organizacija instalirala izdavač certifikata na vašem radnom profilu. Vaš sigurni mrežni promet možda se nadzire ili modificira."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Na ovom je uređaju instaliran izdavač certifikata. Vaš sigurni mrežni promet možda se nadzire ili modificira."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrator je uključio mrežni zapisnik koji nadzire promet na vašem uređaju."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administrator je uključio mrežni zapisnik koji prati promet na vašem poslovnom profilu, ali ne i na osobnom profilu."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Povezani ste s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g> koja može nadzirati vašu aktivnost na mreži, uključujući e-poštu, aplikacije i web-lokacije."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Povezani ste s aplikacijama <xliff:g id="VPN_APP_0">%1$s</xliff:g> i <xliff:g id="VPN_APP_1">%2$s</xliff:g> koje mogu nadzirati vašu aktivnost na mreži, uključujući e-poruke, aplikacije i web-lokacije."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Vaš je radni profil povezan s aplikacijom <xliff:g id="VPN_APP">%1$s</xliff:g> koja može nadzirati vašu aktivnost na mreži, uključujući e-poruke, aplikacije i web-lokacije."</string> @@ -623,6 +631,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da biste isključili zvuk. Usluge pristupačnosti možda neće imati zvuk."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Dodirnite da biste postavili na vibraciju."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Dodirnite da biste isključili zvuk."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključivanje zvuka"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključivanje zvuka"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string> @@ -736,7 +746,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Sustav je ovu obavijest automatski <b>prebacio u bešumnu</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Ova obavijest automatski je <b>rangirana više</b> na vašem zaslonu."</string> <string name="feedback_demoted" msgid="951884763467110604">"Ova obavijest automatski je <b>rangirana niže</b> na vašem zaslonu."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Je li to bilo točno?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Recite razvojnom programeru što mislite. Je li to bilo točno?"</string> <string name="feedback_response" msgid="4671729244976641339">"Zahvaljujemo na povratnim informacijama!"</string> <string name="feedback_ok" msgid="6481426753298857144">"U redu"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Otvorene su kontrole obavijesti za <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -885,6 +895,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premještanje u prostoriju <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kartica je dodana"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kartica je uklonjena"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač brzih postavki."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavijest: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvaranje postavki."</string> @@ -975,7 +987,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> koristi sljedeće: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> nedavno je koristila sljedeće: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(za poslovne korisnike)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonski poziv"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonski poziv"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(putem aplikacije <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparat"</string> <string name="privacy_type_location" msgid="7991481648444066703">"lokaciju"</string> @@ -983,7 +995,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Senzori su isključeni"</string> <string name="device_services" msgid="1549944177856658705">"Usluge uređaja"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Bez naslova"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Dodirnite da biste ponovo pokrenuli tu aplikaciju i prikazali je na cijelom zaslonu."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Premjesti"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Ažurirana je navigacija sustavom. Možete je promijeniti u Postavkama."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Navigaciju sustavom možete ažurirati u Postavkama"</string> @@ -1038,6 +1049,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Dodaj"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Preporuka s kanala <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Kontrole su ažurirane"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Uređaj je zaključan"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN sadrži slova ili simbole"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Potvrdite uređaj <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Pogrešan PIN"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 868b2e7cbbb0..a736f88ffd6d 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Görgethető képernyőkép"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Képernyőkép elvetése"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Képernyőkép előnézete"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Felső határ"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Alsó határ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Képernyőrögzítő"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Képernyőrögzítés feldolgozása"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Folyamatban lévő értesítés képernyőrögzítési munkamenethez"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Napfelkeltéig"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Be: <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Eddig: <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Az NFC ki van kapcsolva"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Az NFC be van kapcsolva"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Az NFC használatához oldja fel a képernyőzárat"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Ez az eszköz az Ön szervezetének tulajdonában van"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Ez az eszköz a(z) <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> tulajdonában van"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"A telefonhoz csúsztasson az ikonról"</string> <string name="voice_hint" msgid="7476017460191291417">"A hangsegéd eléréséhez csúsztassa ujját az ikonról"</string> <string name="camera_hint" msgid="4519495795000658637">"A fényképezőhöz csúsztasson az ikonról"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profil megjelenítése"</string> <string name="user_add_user" msgid="4336657383006913022">"Felhasználó hozzáadása"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Új felhasználó"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Befejezi a vendég munkamenetet?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"A vendég munkamenet befejezése"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Eltávolítja a vendég munkamenetet?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Munkamenet befejezése"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eltávolítás"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Örülünk, hogy visszatért, vendég!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Folytatja a munkamenetet?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Újrakezdés"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Szervezete tanúsítványkibocsátót telepített a munkaprofilba. Ezáltal figyelhetik és befolyásolhatják az Ön biztonságos hálózati forgalmát."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Az eszközre tanúsítványkibocsátó van telepítve. Ezáltal figyelhetik és befolyásolhatják az Ön biztonságos hálózati forgalmát."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"A rendszergazda bekapcsolta az eszköz forgalmát figyelő hálózati naplózást."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"A rendszergazda bekapcsolta a hálózati naplózást, amely a munkaprofilban figyeli a forgalmat, a személyes profilban azonban nem."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Ön kapcsolódik a(z) <xliff:g id="VPN_APP">%1$s</xliff:g> alkalmazáshoz, amely figyelheti hálózati tevékenységét, beleértve a levelezést, valamint az alkalmazás- és webhelyhasználatot."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Ön csatlakozik a(z) <xliff:g id="VPN_APP_0">%1$s</xliff:g> és a(z) <xliff:g id="VPN_APP_1">%2$s</xliff:g> alkalmazásokhoz, amelyek figyelhetik hálózati tevékenységét, beleértve a levelezést, valamint az alkalmazás- és webhelyhasználatot."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Munkaprofilja csatlakozik a(z) <xliff:g id="VPN_APP">%1$s</xliff:g> alkalmazáshoz, amely figyelheti hálózati tevékenységét, beleértve a levelezést, az alkalmazásokat és a webhelyeket."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Koppintson a némításhoz. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Koppintson a rezgés beállításához."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Koppintson a némításhoz."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"némítás"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"némítás feloldása"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rezgés"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Ezt az értesítést automatikusan <b>némára állította</b> a rendszer."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Ezt az értesítést automatikusan <b>előrébb sorolta</b> a rendszer az értesítési felületen."</string> <string name="feedback_demoted" msgid="951884763467110604">"Ezt az értesítést automatikusan <b>hátrébb sorolta</b> a rendszer az értesítési felületen."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Megfelelő ez így?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Ossza meg a fejlesztővel a visszajelzését. Megfelelő ez így?"</string> <string name="feedback_response" msgid="4671729244976641339">"Köszönjük a visszajelzést!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> értesítésvezérlői megnyitva"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Áthelyezés ide: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Hozzáadás a következő pozícióhoz: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. hely"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kártya hozzáadva"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kártya eltávolítva"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Gyorsbeállítások szerkesztője"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-értesítések: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Beállítások megnyitása."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"A(z) <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> használja a következőt: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"A(z) <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> nemrég használta a következőt: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(vállalati)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonhívás"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonhívás"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(a következőn keresztül: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"helyadatok"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Érzékelők kikapcsolva"</string> <string name="device_services" msgid="1549944177856658705">"Eszközszolgáltatások"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Nincs cím"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Koppintson az alkalmazás újraindításához és a teljes képernyős mód elindításához."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Áthelyezés"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"A rendszer-navigáció módja megváltozott. Módosításához nyissa meg a Beállításokat."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"A rendszer-navigációs lehetőségeket a Beállításokban módosíthatja"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Hozzáadás"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> javasolta"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Vezérlők frissítve"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Az eszköz zárolva van"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"A PIN-kód betűket vagy szimbólumokat tartalmaz"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> ellenőrzése"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Helytelen PIN-kód"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 3c088749ed57..40ddf54cec04 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Ոլորել սքրինշոթը"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Փակել սքրինշոթը"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Սքրինշոթի նախադիտում"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Վերևի սահմանագիծ"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Ներքևի սահմանագիծ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Էկրանի տեսագրիչ"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Էկրանի տեսագրության մշակում"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Էկրանի տեսագրման աշխատաշրջանի ընթացիկ ծանուցում"</string> @@ -410,6 +412,7 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Մինչև լուսաբաց"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Կմիանա՝ <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Մինչև <xliff:g id="TIME">%s</xliff:g>"</string> + <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Պայծառության նվազեցում"</string> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC-ն անջատված է"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC-ն միացված է"</string> @@ -443,6 +446,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ապակողպեք՝ NFC-ն օգտագործելու համար"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Այս սարքը պատկանում է ձեր կազմակերպությանը"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Այս սարքը պատկանում է «<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>» կազմակերպությանը"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Սահահարվածեք հեռախոսի պատկերակից"</string> <string name="voice_hint" msgid="7476017460191291417">"Սահահարվածեք ձայնային հուշման պատկերակից"</string> <string name="camera_hint" msgid="4519495795000658637">"Սահահարվածեք խցիկի պատկերակից"</string> @@ -463,9 +468,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Ցույց տալ դիտարկումը"</string> <string name="user_add_user" msgid="4336657383006913022">"Ավելացնել օգտատեր"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Նոր օգտատեր"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ավարտե՞լ հյուրի աշխատաշրջանը"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Ավարտել հյուրի աշխատաշրջանը"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Հեռացնե՞լ հյուրին:"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր ծրագրերն ու տվյալները կջնջվեն:"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ավարտել աշխատաշրջանը"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Հեռացնել"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Բարի վերադարձ, հյուր:"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Դուք ցանկանու՞մ եք շարունակել ձեր գործողությունը:"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Սկսել"</string> @@ -540,6 +546,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ձեր կազմակերպությունը ձեր աշխատանքային պրոֆիլում տեղադրել է վկայագրման կենտրոն։ Ձեր ցանցի ապահով թրաֆիկը կարող է վերահսկվել կամ փոփոխվել։"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Այս սարքում տեղադրված է վկայագրման կենտրոն։ Ձեր ցանցի ապահով թրաֆիկը կարող է վերահսկվել կամ փոփոխվել։"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Ձեր ադմինիստրատորը միացրել է ցանցային իրադարձությունների գրանցումը, որը վերահսկում է ձեր սարքի թրաֆիկը։"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Ձեր ադմինիստրատորը միացրել է ցանցային իրադարձությունների գրանցումը, որը վերահսկում է ձեր աշխատանքային պրոֆիլի թրաֆիկը (այլ ոչ անձնական պրոֆիլը)։"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Դուք կապակցված եք <xliff:g id="VPN_APP">%1$s</xliff:g> հավելվածին, որը կարող է վերահսկել ձեր ցանցային գործողությունը, այդ թվում նաև էլփոստը, հավելվածները և կայքերը:"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Դուք կապակցված եք <xliff:g id="VPN_APP_0">%1$s</xliff:g> և <xliff:g id="VPN_APP_1">%2$s</xliff:g> հավելվածներին, որոնք կարող են վերահսկել ձեր ցանցային գործունեությունը, այդ թվում նաև էլփոստը, հավելվածները և կայքերը:"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Ձեր աշխատանքային պրոֆիլը կապակցված է <xliff:g id="VPN_APP">%1$s</xliff:g> հավելվածին, որը կարող է վերահսկել ձեր ցանցային գործունեությունը, այդ թվում նաև էլփոստը, հավելվածները և կայքերը:"</string> @@ -620,6 +627,7 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s: Հպեք՝ ձայնն անջատելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s։ Հպեք՝ թրթռոցը միացնելու համար։"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s։ Հպեք՝ ձայնը անջատելու համար։"</string> + <string name="volume_ringer_change" msgid="3574969197796055532">"Հպեք՝ զանգակի ռեժիմը փոխելու համար"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"անջատել ձայնը"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"միացնել ձայնը"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"միացնել թրթռոցը"</string> @@ -733,7 +741,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Այս ծանուցման կարևորության մակարդակը ավտոմատ <b>իջեցվել է և դարձել անձայն</b>։"</string> <string name="feedback_promoted" msgid="2125562787759780807">"Ծանուցումների վահանակում այս ծանուցուման կարևորության մակարդակն ավտոմատ <b>բարձրացվել է</b>։"</string> <string name="feedback_demoted" msgid="951884763467110604">"Ծանուցումների վահանակում այս ծանուցուման կարևորության մակարդակն ավտոմատ <b>իջեցվել է</b>։"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Սա ճի՞շտ էր"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Ուղարկեք ձեր կարծիքը մշակողին։ Սա ճի՞շտ էր։"</string> <string name="feedback_response" msgid="4671729244976641339">"Շնորհակալություն արձագանքելու համար"</string> <string name="feedback_ok" msgid="6481426753298857144">"Եղավ"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի ծանուցումների կառավարումը բաց է"</string> @@ -880,6 +888,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Տեղափոխել դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ավելացնել դիրք <xliff:g id="POSITION">%1$d</xliff:g>-ում"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Սալիկն ավելացվեց"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Սալիկը հեռացվեց"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Արագ կարգավորումների խմբագրիչ:"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ծանուցում՝ <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Բացել կարգավորումները:"</string> @@ -970,7 +980,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> հավելվածն օգտագործում է «<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>» գործառույթը"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> հավելվածը վերջերս օգտագործել է «<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>» գործառույթը"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(կորպորատիվ տարբերակ)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Հեռախոսազանգ"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Հեռախոսազանգ"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g>-ի միջոցով)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"տեսախցիկը"</string> <string name="privacy_type_location" msgid="7991481648444066703">"վայրը"</string> @@ -978,7 +988,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Տվիչներն անջատած են"</string> <string name="device_services" msgid="1549944177856658705">"Սարքի ծառայություններ"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Անանուն"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Հպեք՝ հավելվածը վերագործարկելու և լիաէկրան ռեժիմին անցնելու համար։"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Տեղափոխել"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Համակարգի նավիգացիան թարմացվեց: Փոփոխություններ անելու համար անցեք կարգավորումներ:"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Թարմացրեք համակարգի նավիգացիան կարգավորումներում"</string> @@ -1032,6 +1041,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Ավելացնել"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Առաջարկվել է <xliff:g id="APP">%s</xliff:g> հավելվածի կողմից"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Կառավարման տարրերը թարմացվեցին"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Սարքը կողպված է"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN կոդը տառեր և նշաններ է պարունակում"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Ստուգել <xliff:g id="DEVICE">%s</xliff:g> սարքը"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN կոդը սխալ է"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index a5d8a27cf5a0..e20dd51276e7 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Men-scroll screenshot"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Menutup screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pratinjau screenshot"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Batas atas"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Batas bawah"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Perekam Layar"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses perekaman layar"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifikasi yang sedang berjalan untuk sesi rekaman layar"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Sampai pagi"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktif pada <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Sampai <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC dinonaktifkan"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC diaktifkan"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Buka kunci untuk menggunakan NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Perangkat ini milik organisasi Anda"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Perangkat ini milik <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Geser dari ikon untuk telepon"</string> <string name="voice_hint" msgid="7476017460191291417">"Geser dari ikon untuk bantuan suara"</string> <string name="camera_hint" msgid="4519495795000658637">"Geser dari ikon untuk kamera"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Tampilkan profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Tambahkan pengguna"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baru"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Akhiri sesi tamu?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Akhiri sesi tamu"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Hapus tamu?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data di sesi ini akan dihapus."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Akhiri sesi"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hapus"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat datang kembali, tamu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Lanjutkan sesi Anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulai ulang"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisasi Anda menginstal otoritas sertifikat di profil kerja. Traffic jaringan aman Anda mungkin dipantau atau diubah."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Otoritas sertifikat diinstal di perangkat. Traffic jaringan aman Anda mungkin dipantau atau diubah."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Admin telah mengaktifkan pencatatan jaringan, yang memantau traffic di perangkat."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Admin telah mengaktifkan logging jaringan, yang memantau traffic di profil kerja, tetapi tidak di profil pribadi."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Anda tersambung ke <xliff:g id="VPN_APP">%1$s</xliff:g>, yang dapat memantau aktivitas jaringan, termasuk email, aplikasi, dan situs web."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Anda tersambung ke <xliff:g id="VPN_APP_0">%1$s</xliff:g> dan <xliff:g id="VPN_APP_1">%2$s</xliff:g>, yang dapat memantau aktivitas jaringan, termasuk email, aplikasi, dan situs web."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Profil kerja Anda tersambung ke <xliff:g id="VPN_APP">%1$s</xliff:g>, yang dapat memantau aktivitas jaringan, termasuk email, aplikasi, dan situs."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ketuk untuk membisukan. Layanan aksesibilitas mungkin dibisukan."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Ketuk untuk menyetel agar bergetar."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Ketuk untuk menonaktifkan."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Tanpa suara"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktifkan"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Notifikasi ini otomatis <b>didemosikan menjadi Senyap</b> oleh sistem."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Notifikasi ini otomatis <b>diberi peringkat lebih tinggi</b> di shade Anda."</string> <string name="feedback_demoted" msgid="951884763467110604">"Notifikasi ini otomatis <b>diberi peringkat lebih rendah</b> di shade Anda."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Sudah benar?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Berikan masukan kepada developer. Sudah benar?"</string> <string name="feedback_response" msgid="4671729244976641339">"Terima kasih atas masukan Anda"</string> <string name="feedback_ok" msgid="6481426753298857144">"Oke"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kontrol notifikasi untuk <xliff:g id="APP_NAME">%1$s</xliff:g> dibuka"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pindahkan ke <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan ke posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kartu ditambahkan"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kartu dihapus"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor setelan cepat."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifikasi <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buka setelan."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> sedang menggunakan <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> menggunakan <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> baru-baru ini"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(perusahaan)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Panggilan telepon"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Panggilan telepon"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(melalui <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"lokasi"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensor nonaktif"</string> <string name="device_services" msgid="1549944177856658705">"Layanan Perangkat"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Tanpa judul"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Ketuk untuk memulai ulang aplikasi ini dan membuka layar penuh."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Pindahkan"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigasi sistem diupdate. Untuk melakukan perubahan, buka Setelan."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Buka Setelan untuk mengupdate navigasi sistem"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Tambahkan"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Disarankan oleh <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Kontrol diperbarui"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Perangkat terkunci"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN berisi huruf atau simbol"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verifikasi <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN salah"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 5f794e76150f..0c994209f544 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Flettiskjáskot"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Loka skjámynd"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Forskoðun skjámyndar"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Efri mörk"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Neðri mörk"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Skjáupptaka"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Vinnur úr skjáupptöku"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Áframhaldandi tilkynning fyrir skjáupptökulotu"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Til sólarupprásar"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Virkt kl. <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Til <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Slökkt á NFC"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Kveikt á NFC"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Taktu úr lás til að nota NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Þetta tæki tilheyrir fyrirtækinu þínu"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Þetta tæki tilheyrir <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Strjúktu frá tákninu fyrir síma"</string> <string name="voice_hint" msgid="7476017460191291417">"Strjúktu frá tákninu fyrir raddaðstoð"</string> <string name="camera_hint" msgid="4519495795000658637">"Strjúktu frá tákninu fyrir myndavél"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Sýna snið"</string> <string name="user_add_user" msgid="4336657383006913022">"Bæta notanda við"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nýr notandi"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ljúka gestalotu?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Ljúka gestalotu"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Fjarlægja gest?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ljúka lotu"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjarlægja"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkominn aftur, gestur!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Viltu halda áfram með lotuna?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Byrja upp á nýtt"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Fyrirtækið þitt setti upp CA-vottorð á vinnusniðinu þínu. Eftirlit kann að vera haft með öruggri netnotkun þinni eða henni kann að vera breytt."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"CA-vottorð er uppsett á þessu tæki. Eftirlit kann að vera haft með öruggri netnotkun þinni eða henni kann að vera breytt."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Kerfisstjóri hefur kveikt á eftirliti netkerfa, sem fylgist með netumferð á tækinu þínu."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Stjórnandinn kveikti á eftirliti netkerfa sem fylgist með netumferð á vinnusniðinu þínu en ekki á eigin sniði."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Þú ert með tengingu við <xliff:g id="VPN_APP">%1$s</xliff:g>, sem getur fylgst með netnotkun þinni, þ. á m. tölvupósti, forritum og vefsvæðum."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Þú ert með tengingu við <xliff:g id="VPN_APP_0">%1$s</xliff:g> og <xliff:g id="VPN_APP_1">%2$s</xliff:g>, sem geta fylgst með netnotkun þinni, þar á meðal tölvupósti, forritum og vefsvæðum."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Vinnusniðið þitt er tengt <xliff:g id="VPN_APP">%1$s</xliff:g>, sem getur fylgst með netnotkun þinni, þ. á m. tölvupósti, forritum og vefsvæðum."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ýttu til að þagga. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Ýttu til að stilla á titring."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Ýttu til að þagga."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"þagga"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"hætta að þagga"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titringur"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Stig þessarar tilkynningar var sjálfkrafa <b>lækkað í þögult</b> af kerfinu."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Stig þessarar tilkynningar var sjálfkrafa <b>hækkað</b> í tilkynningaglugganum þínum."</string> <string name="feedback_demoted" msgid="951884763467110604">"Stig þessarar tilkynningar var sjálfkrafa <b>lækkað</b> í tilkynningaglugganum þínum."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Var þetta rétt?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Sendu framleiðandanum ábendingu. Var þetta rétt?"</string> <string name="feedback_response" msgid="4671729244976641339">"Takk fyrir að segja þína skoðun!"</string> <string name="feedback_ok" msgid="6481426753298857144">"Í lagi"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Opnað fyrir tilkynningastýringar <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Færa í <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bæta við í stöðu <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Staða <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Reit bætt við"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Reitur fjarlægður"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Flýtistillingaritill."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> tilkynning: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Opna stillingar."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> er að nota <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> notaði <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> nýlega"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(fyrirtæki)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Símtal"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Símtal"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(í gegnum <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"myndavél"</string> <string name="privacy_type_location" msgid="7991481648444066703">"staðsetning"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Slökkt á skynjurum"</string> <string name="device_services" msgid="1549944177856658705">"Tækjaþjónusta"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Enginn titill"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Ýttu til að endurræsa forritið og sýna það á öllum skjánum."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Færa"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Kerfisstjórnun uppfærð. Þú getur breytt þessu í stillingunum."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Farðu í stillingar til að uppfæra kerfisstjórnun"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Bæta við"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Tillaga frá <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Stýringar uppfærðar"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Tækið er læst"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN inniheldur bókstafi eða tákn"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Staðfesta <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Rangt PIN-númer"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index cfd07970b0cd..527a6956d5e3 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Scorri screenshot"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ignora screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Anteprima screenshot"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Limite superiore"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Limite inferiore"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Registrazione dello schermo"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Elaboraz. registraz. schermo"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifica costante per una sessione di registrazione dello schermo"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Fino all\'alba"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Attivazione alle <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Fino alle <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC non attiva"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC attiva"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Sblocca per usare NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Questo dispositivo appartiene alla tua organizzazione"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Questo dispositivo appartiene a <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Scorri per accedere al telefono"</string> <string name="voice_hint" msgid="7476017460191291417">"Scorri dall\'icona per accedere a Voice Assist"</string> <string name="camera_hint" msgid="4519495795000658637">"Scorri dall\'icona per accedere alla fotocamera"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostra profilo"</string> <string name="user_add_user" msgid="4336657383006913022">"Aggiungi utente"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuovo utente"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vuoi terminare la sessione Ospite?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Termina sessione Ospite"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Rimuovere l\'ospite?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Termina sessione"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Rimuovi"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bentornato, ospite."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vuoi continuare la sessione?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Ricomincia"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"La tua organizzazione ha installato un\'autorità di certificazione nel tuo profilo di lavoro. Il tuo traffico di rete protetto potrebbe essere monitorato o modificato."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Sul dispositivo è installata un\'autorità di certificazione. Il tuo traffico di rete protetto potrebbe essere monitorato o modificato."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"L\'amministratore ha attivato i log di rete, che consentono di monitorare il traffico sul dispositivo."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"L\'amministratore ha attivato i log di rete, che consentono di monitorare il traffico nel profilo di lavoro, ma non nel profilo personale."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Sei connesso a <xliff:g id="VPN_APP">%1$s</xliff:g>, che consente di monitorare le attività di rete, inclusi siti web, email e app."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Sei connesso a <xliff:g id="VPN_APP_0">%1$s</xliff:g> e <xliff:g id="VPN_APP_1">%2$s</xliff:g>, che consentono di monitorare le attività di rete, inclusi siti web, email e app."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Il tuo profilo di lavoro è collegato a <xliff:g id="VPN_APP">%1$s</xliff:g>, da cui è possibile monitorare la tua attività di rete, inclusi siti web, email e app."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tocca per disattivare l\'audio. L\'audio dei servizi di accessibilità può essere disattivato."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tocca per attivare la vibrazione."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tocca per disattivare l\'audio."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"disattiva l\'audio"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"riattiva l\'audio"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrazione"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Questa notifica è stata <b>retrocessa automaticamente a Silenziosa</b> dal sistema."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Questa notifica è stata <b>posizionata automaticamente più in alto</b> nell\'area notifiche."</string> <string name="feedback_demoted" msgid="951884763467110604">"Questa notifica è stata <b>posizionata automaticamente più in basso</b> nell\'area notifiche."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Era corretto?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Fai conoscere la tua opinione allo sviluppatore. La classificazione era corretta?"</string> <string name="feedback_response" msgid="4671729244976641339">"Grazie per il feedback."</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Controlli di gestione delle notifiche per <xliff:g id="APP_NAME">%1$s</xliff:g> aperti"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Sposta nella posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Aggiungi alla posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Riquadro aggiunto"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Riquadro rimosso"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor di impostazioni rapide."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifica di <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Apri le impostazioni."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> sta usando: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ha usato di recente: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonata"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(tramite <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"Fotocamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"luogo"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensori disattivati"</string> <string name="device_services" msgid="1549944177856658705">"Servizi del dispositivo"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Senza titolo"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Tocca per riavviare l\'app e passare a schermo intero."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Sposta"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigazione del sistema aggiornata. Per apportare modifiche, usa le Impostazioni."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Usa le Impostazioni per aggiornare la navigazione del sistema"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Aggiungi"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Suggerito da <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Controlli aggiornati"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Dispositivo bloccato"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Il PIN contiene lettere o simboli"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verifica <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN errato"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index c4d088d24cce..9fc8b23c8e6e 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"צילום מסך נגלל"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"סגירת צילום מסך"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"תצוגה מקדימה של צילום מסך"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"הקצה העליון"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"הקצה התחתון"</string> <string name="screenrecord_name" msgid="2596401223859996572">"מקליט המסך"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"מתבצע עיבוד של הקלטת מסך"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"התראה מתמשכת לסשן הקלטת מסך"</string> @@ -414,6 +416,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"עד הזריחה"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"יתחיל בשעה <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"עד <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC מושבת"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC מופעל"</string> @@ -444,9 +448,11 @@ <string name="notification_tap_again" msgid="4477318164947497249">"הקש שוב כדי לפתוח"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"צריך להחליק כדי לפתוח"</string> <string name="keyguard_retry" msgid="886802522584053523">"יש להחליק למעלה כדי לנסות שוב"</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"יש לבטל נעילה כדי להשתמש ב-NFC"</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"יש לבטל את הנעילה כדי להשתמש ב-NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"המכשיר הזה שייך לארגון שלך"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"המכשיר הזה שייך לארגון <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"החלק מהסמל כדי להפעיל את הטלפון"</string> <string name="voice_hint" msgid="7476017460191291417">"החלק מהסמל כדי להפעיל את המסייע הקולי"</string> <string name="camera_hint" msgid="4519495795000658637">"החלק מהסמל כדי להפעיל את המצלמה"</string> @@ -467,9 +473,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"הצג פרופיל"</string> <string name="user_add_user" msgid="4336657383006913022">"הוספת משתמש"</string> <string name="user_new_user_name" msgid="2019166282704195789">"משתמש חדש"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"להפסיק את הגלישה כאורח?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"הפסקת הגלישה כאורח"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"להסיר אורח?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בפעילות זו באתר יימחקו."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"הפסקת הגלישה"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"הסר"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"שמחים לראותך שוב!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"האם ברצונך להמשיך בפעילות באתר?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ברצוני להתחיל מחדש"</string> @@ -546,6 +553,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"הארגון שלך התקין רשות אישורים בפרופיל העבודה. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"במכשיר זה מותקנת רשות אישורים. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"מנהל המערכת הפעיל את התכונה \'רישום התנועה ברשת\', שמנטרת את תנועת הנתונים במכשיר."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"מנהל המערכת הפעיל את תכונת רישום התנועה ברשת, שמנטרת את תנועת הנתונים בפרופיל העבודה, אבל לא בפרופיל האישי."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"אתה מחובר לאפליקציה <xliff:g id="VPN_APP">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"אתה מחובר לאפליקציות <xliff:g id="VPN_APP_0">%1$s</xliff:g> ו-<xliff:g id="VPN_APP_1">%2$s</xliff:g>, שיכולות לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"פרופיל העבודה שלך מחובר לאפליקציה <xliff:g id="VPN_APP">%1$s</xliff:g>, שיכולה לעקוב אחר הפעילות שלך ברשת, כולל הודעות אימייל, אפליקציות ואתרים."</string> @@ -626,6 +634,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. הקש כדי להשתיק. ייתכן ששירותי הנגישות מושתקים."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. הקש כדי להעביר למצב רטט."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. הקש כדי להשתיק."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ביטול ההשתקה"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"רטט"</string> @@ -739,7 +749,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ההתראה הזו <b>הורדה בדרגה ל\'שקט\'</b> באופן אוטומטי על-ידי המערכת."</string> <string name="feedback_promoted" msgid="2125562787759780807">"ההתראה הזו <b>דורגה גבוה יותר</b> באופן אוטומטי בהתראות שלך."</string> <string name="feedback_demoted" msgid="951884763467110604">"ההתראה הזו <b>דורגה נמוך יותר</b&gt באופן אוטומטי בהתראות שלך."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"האם פעולה זו הייתה נכונה?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"אפשר לשלוח למפתח את המשוב שלך. האם פעולה זו הייתה נכונה?"</string> <string name="feedback_response" msgid="4671729244976641339">"תודה על המשוב!"</string> <string name="feedback_ok" msgid="6481426753298857144">"אישור"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"פקדי ההודעות של <xliff:g id="APP_NAME">%1$s</xliff:g> נפתחו"</string> @@ -890,6 +900,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"העברה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"הוספה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"האריח נוסף"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"האריח הוסר"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"עורך הגדרות מהירות."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"התראות <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"פתיחת הגדרות."</string> @@ -977,23 +989,17 @@ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"אפליקציות משתמשות ב<xliff:g id="TYPES_LIST">%s</xliff:g>."</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string> <string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" וגם "</string> - <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) --> - <skip /> + <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"האפליקציה <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> משתמשת ב<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> + <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"האפליקציה <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> השתמשה לאחרונה ב<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> + <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(גרסה ארגונית)"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"שיחת טלפון"</string> + <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(באמצעות <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"מצלמה"</string> <string name="privacy_type_location" msgid="7991481648444066703">"מיקום"</string> <string name="privacy_type_microphone" msgid="9136763906797732428">"מיקרופון"</string> <string name="sensor_privacy_mode" msgid="4462866919026513692">"החיישנים כבויים"</string> <string name="device_services" msgid="1549944177856658705">"שירותים למכשיר"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"ללא שם"</string> - <string name="restart_button_description" msgid="6916116576177456480">"צריך להקיש כדי להפעיל מחדש את האפליקציה הזו ולעבור למסך מלא."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"העברה"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"הניווט במערכת עודכן. אפשר לערוך שינויים דרך ההגדרות."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"יש לעבור להגדרות כדי לעדכן את הניווט במערכת"</string> @@ -1049,6 +1055,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"הוספה"</string> <string name="controls_dialog_message" msgid="342066938390663844">"הוצע על-ידי <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"הפקדים עודכנו"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"המכשיר נעול"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"קוד האימות מכיל אותיות או סמלים"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"אימות <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"קוד גישה שגוי"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 3fcc7138d9b3..1b18b5ce4607 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"スクリーンショットをスクロールします"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"スクリーンショットを閉じます"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"スクリーンショットのプレビュー"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"上部境界"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"下部境界"</string> <string name="screenrecord_name" msgid="2596401223859996572">"スクリーン レコーダー"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"画面の録画を処理しています"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"画面の録画セッション中の通知"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"日の出まで"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>にオン"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g>まで"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC は無効です"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC は有効です"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC を使用するには、ロックを解除してください"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"これは組織が所有するデバイスです"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"これは <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> が所有するデバイスです"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"右にスワイプして通話"</string> <string name="voice_hint" msgid="7476017460191291417">"アイコンからスワイプして音声アシストを起動"</string> <string name="camera_hint" msgid="4519495795000658637">"左にスワイプしてカメラを起動"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"プロファイルを表示"</string> <string name="user_add_user" msgid="4336657383006913022">"ユーザーを追加"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新しいユーザー"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ゲスト セッションを終了しますか?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"ゲスト セッションを終了する"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ゲストを削除しますか?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"セッションを終了"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"削除"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"おかえりなさい、ゲストさん"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"セッションを続行しますか?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"最初から開始"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"組織によって、あなたの仕事用プロファイルに認証局がインストールされました。保護されたネットワーク トラフィックが監視、変更される場合があります。"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"このデバイスには認証局がインストールされています。保護されたネットワーク トラフィックが監視、変更される可能性があります。"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"管理者がネットワーク ログを有効にしているため、このデバイスのトラフィックが監視されています。"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"管理者がネットワーク ログを有効にしているため、仕事用プロファイルのトラフィックは監視されています(個人用プロファイルは対象外)。"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"<xliff:g id="VPN_APP">%1$s</xliff:g> に接続しています。このアプリはあなたのネットワーク アクティビティ(メール、アプリ、ウェブサイトなど)を監視できます。"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> と <xliff:g id="VPN_APP_1">%2$s</xliff:g> に接続しています。これらのアプリは、あなたのネットワーク アクティビティ(メール、アプリ、ウェブサイト)を監視できます。"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"この仕事用プロファイルは <xliff:g id="VPN_APP">%1$s</xliff:g> に接続しています。このアプリはあなたのネットワーク アクティビティ(メール、アプリ、ウェブサイトなど)を監視できます。"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。タップしてミュートします。ユーザー補助機能サービスがミュートされる場合があります。"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s。タップしてバイブレーションに設定します。"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s。タップしてミュートします。"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ミュート"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ミュートを解除"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"バイブレーション"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"この通知は、システムによって自動的に<b>ランクがサイレントに下がり</b>ました。"</string> <string name="feedback_promoted" msgid="2125562787759780807">"この通知は、自動的にシェード内の<b>ランクが上がり</b>ました。"</string> <string name="feedback_demoted" msgid="951884763467110604">"この通知は、自動的にシェード内の<b>ランクが下がり</b>ました。"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"間違いありませんか?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"皆様のご意見がデベロッパーに届きます。これで間違いありませんか?"</string> <string name="feedback_response" msgid="4671729244976641339">"フィードバックをお寄せいただきありがとうございます。"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> の通知管理は開いています"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> に移動"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ポジション <xliff:g id="POSITION">%1$d</xliff:g> に追加"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"タイルを追加しました"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"タイルを削除しました"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"クイック設定エディタ"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> の通知: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"設定を開きます。"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> は <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> を使用しています"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> は最近 <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> を使用しました"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(エンタープライズ版)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"電話"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"通話"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> 経由)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"カメラ"</string> <string name="privacy_type_location" msgid="7991481648444066703">"現在地情報"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"センサー OFF"</string> <string name="device_services" msgid="1549944177856658705">"デバイス サービス"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"タイトルなし"</string> - <string name="restart_button_description" msgid="6916116576177456480">"タップしてこのアプリを再起動すると、全画面表示になります。"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"移動"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"システム ナビゲーションを更新しました。変更するには [設定] に移動してください。"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"システム ナビゲーションを更新するには [設定] に移動してください"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"追加"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> によるおすすめ"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"コントロールを更新しました"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"デバイス: ロック状態"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN に英字や記号を含める"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g>の確認"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN が間違っています"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 5c11fa44f3fc..e2b9fac8f208 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"ეკრანის ანაბეჭდში გადაადგილება"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ეკრანის ანაბეჭდის დახურვა"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ეკრანის ანაბეჭდის გადახედვა"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"ზედა საზღვარი"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"ქვედა საზღვარი"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ეკრანის ჩამწერი"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ეკრანის ჩანაწერი მუშავდება"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"უწყვეტი შეტყობინება ეკრანის ჩაწერის სესიისთვის"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"მზის ამოსვლამდე"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ჩაირთოს <xliff:g id="TIME">%s</xliff:g>-ზე"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g>-მდე"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC გათიშულია"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ჩართულია"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"განბლოკეთ NFC-ის გამოსაყენებლად"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"ამ მოწყობილობას ფლობს თქვენი ორგანიზაცია"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"ამ მოწყობილობას ფლობს <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ტელეფონისთვის გადაფურცლეთ ხატულადან"</string> <string name="voice_hint" msgid="7476017460191291417">"ხმოვანი დახმარებისთვის გადაფურცლეთ ხატულადან"</string> <string name="camera_hint" msgid="4519495795000658637">"კამერისთვის გადაფურცლეთ ხატულადან"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"პროფილის ჩვენება"</string> <string name="user_add_user" msgid="4336657383006913022">"მომხმარებლის დამატება"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ახალი მომხმარებელი"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"დასრულდეს სტუმრის სესია?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"სტუმრის სესიის დასრულება"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"სტუმრის ამოშლა?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"სესიის დასრულება"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ამოშლა"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"სტუმარო, გვიხარია, რომ დაბრუნდით!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"გსურთ, თქვენი სესიის გაგრძელება?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ხელახლა დაწყება"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"თქვენმა ორგანიზაციამ სამსახურის პროფილში სერტიფიცირების ორგანო დააინსტალირა. თქვენი ქსელის დაცული ტრაფიკი შეიძლება შეიცვალოს, ან მასზე მონიტორინგი განხორციელდეს."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ამ მოწყობილობაზე დაინსტალირებულია სერტიფიცირების ორგანო. თქვენი ქსელის დაცული ტრაფიკი შეიძლება შეიცვალოს, ან მასზე მონიტორინგი განხორციელდეს."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"თქვენმა ადმინისტრატორმა ჩართო ქსელის ჟურნალირება, რომელიც თქვენი მოწყობილობის ტრაფიკის მონიტორინგს ახორციელებს."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"თქვენმა ადმინისტრატორმა ქსელის ჟურნალირება ჩართო, რომელიც ახორციელებს თქვენი სამსახურის პროფილის, მაგრამ არა პირადი პროფილის, ტრაფიკის მონიტორინგს."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"თქვენ დაკავშირებული ხართ <xliff:g id="VPN_APP">%1$s</xliff:g>-თან, რომელსაც შეუძლია თქვენი ქსელის აქტივობის (მათ შორის, ელფოსტის, აპებისა და ვებსაიტების) მონიტორინგი."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"თქვენ დაკავშირებული ხართ <xliff:g id="VPN_APP_0">%1$s</xliff:g>-სა და <xliff:g id="VPN_APP_1">%2$s</xliff:g>-თან, რომელთაც შეუძლია თქვენი ქსელის აქტივობის (მათ შორის, ელფოსტის, აპებისა და ვებსაიტების) მონიტორინგი."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"თქვენი სამსახურის პროფილი დაკავშირებულია <xliff:g id="VPN_APP">%1$s</xliff:g>-თან, რომელსაც შეუძლია თქვენი ქსელის აქტივობის (მათ შორის, ელფოსტის, აპებისა და ვებსაიტების) მონიტორინგი."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. შეეხეთ დასადუმებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. შეეხეთ ვიბრაციაზე დასაყენებლად."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. შეეხეთ დასადუმებლად."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"დადუმება"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"დადუმების მოხსნა"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ვიბრაცია"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ეს შეტყობინება ავტომატურად <b>გადავიდა „უხმო“ სტატუსზე</b> სისტემის მიერ."</string> <string name="feedback_promoted" msgid="2125562787759780807">"ეს შეტყობინება ავტომატურად <b>ჩაითვალა უფრო</b> პრიორიტეტულად."</string> <string name="feedback_demoted" msgid="951884763467110604">"ეს შეტყობინება ავტომატურად <b>ჩაითვალა ნაკლებად</b> პრიორიტეტულად."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"სწორია ეს?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"მიაწოდეთ დეველოპერს თქვენი გამოხმაურება. სწორია ეს?"</string> <string name="feedback_response" msgid="4671729244976641339">"გმადლობთ გამოხმაურებისთვის!"</string> <string name="feedback_ok" msgid="6481426753298857144">"კარგი"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"შეტყობინებების მართვა „<xliff:g id="APP_NAME">%1$s</xliff:g>“-ისთვის გახსნილია"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"გადატანა <xliff:g id="POSITION">%1$d</xliff:g>-ზე"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"დამატება პოზიციაზე <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"პოზიცია <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"მოზაიკის ფილა დაემატა"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"მოზაიკის ფილა ამოიშალა"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"სწრაფი პარამეტრების რედაქტორი."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> შეტყობინება: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"პარამეტრების გახსნა."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> იყენებს აპს <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"აპმა <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ახლახან გამოიყენა <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(კორპორაციული)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"სატელეფონო ზარი"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"სატელეფონო ზარი"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(აპის <xliff:g id="ATTRIBUTION">%s</xliff:g> მეშვეობით)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"კამერა"</string> <string name="privacy_type_location" msgid="7991481648444066703">"მდებარეობა"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"სენსორების გამორთვა"</string> <string name="device_services" msgid="1549944177856658705">"მოწყობილობის სერვისები"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"უსათაურო"</string> - <string name="restart_button_description" msgid="6916116576177456480">"შეეხეთ ამ აპის გადასატვირთად და გადადით სრულ ეკრანზე."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"გადატანა"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"სისტემური ნავიგაცია განახლდა. ცვლილებების შესატანად გადადით პარამეტრებზე."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"სისტემური ნავიგაციის გასაახლებლად გადადით პარამეტრებზე"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"დამატება"</string> <string name="controls_dialog_message" msgid="342066938390663844">"შემოთავაზებულია <xliff:g id="APP">%s</xliff:g>-ის მიერ"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"მართვის საშუალებები განახლდა"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"მოწყობილ. ჩაკეტილია"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN-კოდი შეიცავს ასოებს ან სიმბოლოებს"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"დაადასტურეთ <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN-კოდი არასწორია"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 89f905733fa6..1fbb8a51372b 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Скриншотты айналдыру"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Скриншотты жабу"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотты алдын ала қарау"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Жоғарғы шектік сызық"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Төменгі шектік сызық"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Экран жазғыш"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Экран жазғыш бейнесін өңдеу"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды бейнеге жазудың ағымдағы хабарландыруы"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Күн шыққанға дейін"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Қосылу уақыты: <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> дейін"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC өшірулі"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC қосулы"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC пайдалану үшін құлыпты ашыңыз."</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Бұл құрылғы ұйымыңызға тиесілі."</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Бұл құрылғы <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ұйымына тиесілі."</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Телефонды ашу үшін белгішеден әрі қарай сырғытыңыз"</string> <string name="voice_hint" msgid="7476017460191291417">"Дауыс көмекшісін ашу үшін белгішеден әрі қарай сырғытыңыз"</string> <string name="camera_hint" msgid="4519495795000658637">"Камераны ашу үшін белгішеден әрі қарай сырғытыңыз"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профильді көрсету"</string> <string name="user_add_user" msgid="4336657383006913022">"Пайдаланушы қосу"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Жаңа пайдаланушы"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Қонақ сеансы аяқталсын ба?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Қонақ сеансын аяқтау"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Қонақты жою керек пе?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданбалар мен деректер жойылады."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Сеансты аяқтау"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Алып тастау"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Қош келдіңіз, қонақ"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансты жалғастыру керек пе?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Қайта бастау"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ұйымыңыз жұмыс профиліңізде сертификат орнатқан. Қорғалған желі трафигіңіз бақылануы немесе өзгертілуі мүмкін."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Осы құрылғыда сертификат орнатылған. Қорғалған желі трафигіңіз бақылануы немесе өзгертілуі мүмкін."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Әкімші құрылғыңыздағы трафикті бақылайтын желі журналын жүргізуді қосқан."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Әкімші жеке профильдегі емес, жұмыс профиліндегі трафикті қадағалау үшін желі журналын жүргізуді қосып қойған."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Желідегі әрекеттеріңізді, соның ішінде электрондық хабарларды, қолданбаларды және вебсайттарды бақылай алатын <xliff:g id="VPN_APP">%1$s</xliff:g> желісіне қосылдыңыз."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Желідегі әрекеттеріңізді, соның ішінде электрондық хабарларды, қолданбаларды және вебсайттарды бақылай алатын <xliff:g id="VPN_APP_0">%1$s</xliff:g> және <xliff:g id="VPN_APP_1">%2$s</xliff:g> желілеріне қосылдыңыз."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Жұмыс профиліңіз желідегі белсенділігіңізді, соның ішінде электрондық хабарларды, қолданбаларды және веб-сайттарды бақылай алатын <xliff:g id="VPN_APP">%1$s</xliff:g> қолданбасына қосылған."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дыбысын өшіру үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Діріл режимін орнату үшін түртіңіз."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Дыбысын өшіру үшін түртіңіз."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дыбысын өшіру"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дыбысын қосу"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дірілдету"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Жүйе бұл хабарландырудың деңгейін автоматты түрде <b>\"Үнсіз\" санатына төмендетті</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Жүйе бұл хабарландырудың маңыздылық деңгейін автоматты түрде <b>көтерді</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Жүйе бұл хабарландырудың маңыздылық деңгейін автоматты түрде <b>төмендетті</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Барлығы дұрыс па?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Әзірлеушіге пікіріңізді білдіріңіз. Барлығы дұрыс па?"</string> <string name="feedback_response" msgid="4671729244976641339">"Пікіріңіз үшін рақмет!"</string> <string name="feedback_ok" msgid="6481426753298857144">"Жарайды"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> хабарландыруларын басқару элементтері ашылды"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> орнына жылжыту"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> орнына қосу"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> орны"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Бөлшек қосылды."</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Бөлшек өшірілді."</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Жылдам параметрлер өңдегіші."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> хабарландыруы: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Параметрлерді ашу."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> қолданбасы қазір <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> пайдаланады"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> қолданбасы жақында <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> пайдаланды"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративтік)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефон қоңырауы"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефон қоңырауы"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> арқылы)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string> <string name="privacy_type_location" msgid="7991481648444066703">"геодерек"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Датчиктер өшірулі"</string> <string name="device_services" msgid="1549944177856658705">"Құрылғы қызметтері"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Атауы жоқ"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Жылжыту"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Жүйе навигациясы жаңартылды. Өзгерту енгізу үшін \"Параметрлер\" бөліміне өтіңіз."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Жүйе навигациясын жаңарту үшін \"Параметрлер\" бөліміне өтіңіз."</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Енгізу"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ұсынған"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Басқару элементтері жаңартылды"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Құрылғы құлыпталды."</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN коды әріптерден не таңбалардан құралады."</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> растау"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN коды қате"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 85557070a51a..e6f96688a15d 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"មុខងារថតរូបថតអេក្រង់រំកិល"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ច្រានចោលរូបថតអេក្រង់"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ការមើលរូបថតអេក្រង់សាកល្បង"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"បន្ទាត់បែងចែកខាងលើ"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"បន្ទាត់បែងចែកខាងក្រោម"</string> <string name="screenrecord_name" msgid="2596401223859996572">"មុខងារថតអេក្រង់"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"កំពុងដំណើរការការថតអេក្រង់"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"ការជូនដំណឹងដែលកំពុងដំណើរការសម្រាប់រយៈពេលប្រើការថតសកម្មភាពអេក្រង់"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"រហូតដល់ពេលថ្ងៃរះ"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"បើកនៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"រហូតដល់ម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"បានបិទ NFC"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"បានបើក NFC"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"ដោះសោ ដើម្បីប្រើ NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"ឧបករណ៍នេះគឺជាកម្មសិទ្ធិរបស់ស្ថាប័នអ្នក"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"ឧបករណ៍នេះគឺជាកម្មសិទ្ធិរបស់ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"អូសចេញពីរូបតំណាងដើម្បីប្រើទូរស័ព្ទ"</string> <string name="voice_hint" msgid="7476017460191291417">"អូសចេញពីរូបតំណាងដើម្បីប្រើជំនួយសំឡេង"</string> <string name="camera_hint" msgid="4519495795000658637">"អូសចេញពីរូបតំណាងដើម្បីប្រើកាមេរ៉ា"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"បង្ហាញប្រវត្តិរូប"</string> <string name="user_add_user" msgid="4336657383006913022">"បន្ថែមអ្នកប្រើ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"អ្នកប្រើថ្មី"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"បញ្ចប់វគ្គភ្ញៀវឬ?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"បញ្ចប់វគ្គភ្ញៀវ"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"លុបភ្ញៀវ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ទិន្នន័យ និងកម្មវិធីទាំងអស់ក្នុងសម័យនេះនឹងត្រូវបានលុប។"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"បញ្ចប់វគ្គ"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"លុបចេញ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"សូមស្វាគមន៍ការត្រឡប់មកវិញ, ភ្ញៀវ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"តើអ្នកចង់បន្តសម័យរបស់អ្នក?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ចាប់ផ្ដើម"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ស្ថាប័នរបស់អ្នកបានដំឡើងអាជ្ញាធរវិញ្ញាបនបត្រនៅក្នុងកម្រងព័ត៌មានការងារ។ ចរាចរណ៍បណ្តាញដែលមានសុវត្ថិភាពរបស់អ្នកអាចត្រូវបានតាមដាន ឬកែសម្រួល។"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"បានដំឡើងអាជ្ញាធរវិញ្ញាបនបត្រនៅលើឧបករណ៍នេះ។ ចរាចរណ៍បណ្តាញដែលមានសុវត្ថិភាពរបស់អ្នកអាចត្រូវបានតាមដាន ឬកែសម្រួល។"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"អ្នកគ្រប់គ្រងរបស់អ្នកបានបើកការធ្វើកំណត់ហេតុបណ្តាញ ដែលនឹងតាមដានចរាចរណ៍នៅលើឧបករណ៍របស់អ្នក។"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"អ្នកគ្រប់គ្រងរបស់អ្នកបានបើកការធ្វើកំណត់ហេតុបណ្តាញ ដែលតាមដានចរាចរណ៍នៅក្នុងកម្រងព័ត៌មានការងាររបស់អ្នក ប៉ុន្តែមិនតាមដាននៅក្នុងកម្រងព័ត៌មានផ្ទាល់ខ្លួនរបស់អ្នកឡើយ។"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"អ្នកបានភ្ជាប់ទៅ <xliff:g id="VPN_APP">%1$s</xliff:g> ដែលអាចតាមដានសកម្មភាពក្នុងបណ្តាញរបស់អ្នក រួមទាំងអ៊ីមែល កម្មវិធី និងគេហទំព័រផងដែរ។"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"អ្នកបានភ្ជាប់ទៅ <xliff:g id="VPN_APP_0">%1$s</xliff:g> និង <xliff:g id="VPN_APP_1">%2$s</xliff:g> ដែលអាចតាមដានសកម្មភាពបណ្តាញរបស់អ្នក រួមទាំងអ៊ីមែល កម្មវិធី និងគេហទំព័រផងដែរ។"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"កម្រងព័ត៌មានការងាររបស់អ្នកត្រូវបានភ្ជាប់ទៅ <xliff:g id="VPN_APP">%1$s</xliff:g> ដែលអាចតាមដានសកម្មភាពបណ្តាញរបស់អ្នក រួមទាំងអ៊ីមែល កម្មវិធី និងគេហទំព័រផងដែរ។"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s។ ប៉ះដើម្បីបិទសំឡេង។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s ។ ចុចដើម្បីកំណត់ឲ្យញ័រ។"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s ។ ចុចដើម្បីបិទសំឡេង។"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"បិទសំឡេង"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"បើកសំឡេង"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ញ័រ"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ការជូនដំណឹងនេះត្រូវបាន<b>បញ្ចុះទៅស្ងាត់</b>ដោយប្រព័ន្ធដោយស្វ័យប្រវត្តិ។"</string> <string name="feedback_promoted" msgid="2125562787759780807">"ការជូនដំណឹងនេះត្រូវបាន<b>ចាត់ថ្នាក់ខ្ពស់ជាងមុន</b>ដោយស្វ័យប្រវត្តិ នៅក្នុងផ្ទាំងជូនដំណឹងរបស់អ្នក។"</string> <string name="feedback_demoted" msgid="951884763467110604">"ការជូនដំណឹងនេះត្រូវបាន<b>ចាត់ថ្នាក់ទាបជាងមុន</b>ដោយស្វ័យប្រវត្តិ នៅក្នុងផ្ទាំងជូនដំណឹងរបស់អ្នក។"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"តើវាត្រឹមត្រូវទេ?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"ប្រាប់ឱ្យអ្នកអភិវឌ្ឍន៍ដឹងអំពីមតិកែលម្អរបស់អ្នក។ តើវាត្រឹមត្រូវដែរទេ?"</string> <string name="feedback_response" msgid="4671729244976641339">"សូមអរគុណចំពោះមតិកែលម្អរបស់អ្នក!"</string> <string name="feedback_ok" msgid="6481426753298857144">"យល់ព្រម"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"ការគ្រប់គ្រងការជូនដំណឹងសម្រាប់ <xliff:g id="APP_NAME">%1$s</xliff:g> បានបើក"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ផ្លាស់ទីទៅ <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"បញ្ចូលទៅទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"បានបញ្ចូលប្រអប់"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"បានផ្លាស់ទីប្រអប់"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"កម្មវិធីកែការកំណត់រហ័ស"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ការជូនដំណឹង៖ <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"បើកការកំណត់"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> កំពុងប្រើ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> បានប្រើ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ថ្មីៗនេះ"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(សហគ្រាស)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ការហៅទូរសព្ទ"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ការហៅទូរសព្ទ"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(តាមរយៈ <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"កាមេរ៉ា"</string> <string name="privacy_type_location" msgid="7991481648444066703">"ទីតាំង"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"បិទឧបករណ៍ចាប់សញ្ញា"</string> <string name="device_services" msgid="1549944177856658705">"សេវាកម្មឧបករណ៍"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"គ្មានចំណងជើង"</string> - <string name="restart_button_description" msgid="6916116576177456480">"ចុចដើម្បីចាប់ផ្ដើមកម្មវិធីនេះឡើងវិញ រួចចូលប្រើពេញអេក្រង់។"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ផ្លាស់ទី"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"បានធ្វើបច្ចុប្បន្នភាពការរុករកក្នុងប្រព័ន្ធ។ ដើម្បីធ្វើការផ្លាស់ប្ដូរ សូមចូលទៅកាន់ការកំណត់។"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ចូលទៅកាន់ការកំណត់ ដើម្បីធ្វើបច្ចុប្បន្នភាពការរុករកក្នុងប្រព័ន្ធ"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"បញ្ចូល"</string> <string name="controls_dialog_message" msgid="342066938390663844">"បានណែនាំដោយ <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"បានធ្វើបច្ចុប្បន្នភាពការគ្រប់គ្រង"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"បានចាក់សោឧបករណ៍"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"កូដ PIN មានអក្សរ ឬនិមិត្តសញ្ញា"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"ផ្ទៀងផ្ទាត់ <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"កូដ PIN មិនត្រឹមត្រូវ"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index c920cc288bc2..15abcaa7d326 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಸ್ಕ್ರಾಲ್ ಮಾಡಿ"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಅನ್ನು ವಜಾಗೊಳಿಸಿ"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ಸ್ಕ್ರೀನ್ಶಾಟ್ನ ಪೂರ್ವವೀಕ್ಷಣೆ"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"ಮೇಲಿನ ಗಡಿರೇಖೆ"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"ಕೆಳಗಿನ ಗಡಿರೇಖೆ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡರ್"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಆಗುತ್ತಿದೆ"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಸೆಶನ್ಗಾಗಿ ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಅಧಿಸೂಚನೆ"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ಸೂರ್ಯೋದಯದವರೆಗೆ"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> ಸಮಯದಲ್ಲಿ"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> ವರೆಗೂ"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ಸಕ್ರಿಯಗೊಂಡಿದೆ"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ಬಳಸಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"ಈ ಸಾಧನವು ನಿಮ್ಮ ಸಂಸ್ಥೆಗೆ ಸೇರಿದೆ"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"ಈ ಸಾಧನವು <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ಗೆ ಸೇರಿದೆ"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ಫೋನ್ಗಾಗಿ ಐಕಾನ್ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string> <string name="voice_hint" msgid="7476017460191291417">"ಧ್ವನಿ ಸಹಾಯಕ್ಕಾಗಿ ಐಕಾನ್ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string> <string name="camera_hint" msgid="4519495795000658637">"ಕ್ಯಾಮರಾಗಾಗಿ ಐಕಾನ್ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ಪ್ರೊಫೈಲ್ ತೋರಿಸು"</string> <string name="user_add_user" msgid="4336657383006913022">"ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ಹೊಸ ಬಳಕೆದಾರರು"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ಅತಿಥಿ ಸೆಷನ್ ಅಂತ್ಯಗೊಳಿಸುವುದೇ?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"ಅತಿಥಿ ಸೆಷನ್ ಕೊನೆಗೊಳಿಸಿ"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಷನ್ನಲ್ಲಿನ ಎಲ್ಲ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ಸೆಷನ್ ಅಂತ್ಯಗೊಳಿಸಿ"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ತೆಗೆದುಹಾಕಿ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ಮತ್ತೆ ಸುಸ್ವಾಗತ, ಅತಿಥಿ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ನಿಮ್ಮ ಸೆಷನ್ ಮುಂದುವರಿಸಲು ಇಚ್ಚಿಸುವಿರಾ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ಪ್ರಾರಂಭಿಸಿ"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ನಲ್ಲಿ ಪ್ರಮಾಣಪತ್ರ ಅಂಗೀಕಾರವನ್ನು ಸ್ಥಾಪಿಸಿದೆ. ನಿಮ್ಮ ಸುರಕ್ಷಿತ ನೆಟ್ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಅಥವಾ ಮಾರ್ಪಡಿಸಬಹುದು."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ಈ ಸಾಧನದಲ್ಲಿ ಪ್ರಮಾಣಪತ್ರ ಅಂಗೀಕಾರವನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿದೆ. ನಿಮ್ಮ ಸುರಕ್ಷಿತ ನೆಟ್ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು ಅಥವಾ ಮಾರ್ಪಡಿಸಬಹುದು."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನೆಟ್ವರ್ಕ್ ಲಾಗಿಂಗ್ ಆನ್ ಮಾಡಿದ್ದಾರೆ. ಇದು ನಿಮ್ಮ ಸಾಧನದ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡುತ್ತದೆ."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನೆಟ್ವರ್ಕ್ ಲಾಗಿಂಗ್ ಆನ್ ಮಾಡಿದ್ದಾರೆ, ಅದು ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ ನಲ್ಲಿ ಇರುವ ಟ್ರಾಫಿಕ್ ಮೇಲೆ ನಿಗಾ ಇರಿಸುತ್ತದೆ ಆದರೆ ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಪ್ರೊಫೈಲ್ನಲ್ಲಿ ಇರುವ ಟ್ರಾಫಿಕ್ ಮೇಲೆ ಅಲ್ಲ."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"ನೀವು <xliff:g id="VPN_APP">%1$s</xliff:g> ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದ್ದೀರಿ. ಇದು ನಿಮ್ಮ ಇಮೇಲ್ಗಳು, ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ವೆಬ್ಸೈಟ್ಗಳೂ ಸೇರಿದಂತೆ ನಿಮ್ಮ ನೆಟ್ವರ್ಕ್ ಚಟುವಟಿಕೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"ನೀವು <xliff:g id="VPN_APP_0">%1$s</xliff:g> ಹಾಗೂ <xliff:g id="VPN_APP_1">%2$s</xliff:g> ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದ್ದೀರಿ. ಇವು ನಿಮ್ಮ ಇಮೇಲ್ಗಳು, ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ವೆಬ್ಸೈಟ್ಗಳೂ ಸೇರಿದಂತೆ ನೆಟ್ವರ್ಕ್ ಚಟುವಟಿಕೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ <xliff:g id="VPN_APP">%1$s</xliff:g> ಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ. ಇದು ನಿಮ್ಮ ಇಮೇಲ್ಗಳು, ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ವೆಬ್ಸೈಟ್ಗಳೂ ಸೇರಿದಂತೆ ನೆಟ್ವರ್ಕ್ ಚಟುವಟಿಕೆಯನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಬಹುದು."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಪ್ರವೇಶಿಸುವಿಕೆ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್ ಮಾಡಬಹುದು."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. ವೈಬ್ರೇಟ್ ಮಾಡಲು ಹೊಂದಿಸುವುದಕ್ಕಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ಮ್ಯೂಟ್ ಮಾಡಿ"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ಅನ್ಮ್ಯೂಟ್ ಮಾಡಿ"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ವೈಬ್ರೇಟ್"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ಈ ಅಧಿಸೂಚನೆಯು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಿಸ್ಟಂನಿಂದ <b>ಸೈಲೆಂಟ್ಗೆ ಹಿಂಬಡ್ತಿ ಹೊಂದಿದೆ</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"ಈ ಅಧಿಸೂಚನೆಯು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿಮ್ಮ ಶೇಡ್ನಲ್ಲಿ <b>ಉನ್ನತ ಸ್ಥಾನವನ್ನು ಹೊಂದಿದೆ</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"ಈ ಅಧಿಸೂಚನೆಯು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿಮ್ಮ ಶೇಡ್ನಲ್ಲಿ <b>ಕಡಿಮೆ ಸ್ಥಾನವನ್ನು ಹೊಂದಿದೆ</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"ಇದು ಸರಿಯಾಗಿತ್ತೇ?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"ನಿಮ್ಮ ಪ್ರತಿಕ್ರಿಯೆಯನ್ನು ಡೆವಲಪರ್ಗೆ ತಿಳಿಸಿ. ಇದು ಸರಿಯಾಗಿತ್ತೇ?"</string> <string name="feedback_response" msgid="4671729244976641339">"ನಿಮ್ಮ ಪ್ರತಿಕ್ರಿಯೆಗೆ ಧನ್ಯವಾದಗಳು!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ಸರಿ"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಅಧಿಸೂಚನೆ ನಿಯಂತ್ರಣಗಳನ್ನು ತೆರೆಯಲಾಗಿದೆ"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ಇಲ್ಲಿಗೆ ಸರಿಸಿ <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸೇರಿಸಿ"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ಸ್ಥಾನ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ಟೈಲ್ ಸೇರಿಸಲಾಗಿದೆ"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ಟೈಲ್ ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳ ಎಡಿಟರ್."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ಅಧಿಸೂಚನೆ: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಿರಿ."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ಅನ್ನು ಬಳಸುತ್ತಿದೆ"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ಇತ್ತೀಚೆಗೆ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ಅನ್ನು ಬಳಸಿದೆ"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ಎಂಟರ್ಪ್ರೈಸ್)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ಫೋನ್ ಕರೆ"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ಫೋನ್ ಕರೆ"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> ಮೂಲಕ)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"ಕ್ಯಾಮರಾ"</string> <string name="privacy_type_location" msgid="7991481648444066703">"ಸ್ಥಳ"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"ಸೆನ್ಸರ್ಗಳು ಆಫ್"</string> <string name="device_services" msgid="1549944177856658705">"ಸಾಧನ ಸೇವೆಗಳು"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"ಯಾವುದೇ ಶೀರ್ಷಿಕೆಯಿಲ್ಲ"</string> - <string name="restart_button_description" msgid="6916116576177456480">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಮತ್ತು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್ನಲ್ಲಿ ನೋಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ಸರಿಸಿ"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ಸಿಸ್ಟಂ ನ್ಯಾವಿಗೇಷನ ಅಪ್ಡೇಟ್ ಮಾಡಲಾಗಿದೆ ಬದಲಾವಣೆಗಳನ್ನು ಮಾಡಲು, ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಹೋಗಿ."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ಸಿಸ್ಟಂ ನ್ಯಾವಿಗೇಷನ್ ಅಪ್ಡೇಟ್ ಮಾಡಲು ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಹೋಗಿ"</string> @@ -1032,6 +1043,8 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"ಸೇರಿಸಿ"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ಆ್ಯಪ್ ಸೂಚಿಸಿದೆ"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"ನಿಯಂತ್ರಣಗಳನ್ನು ನವೀಕರಿಸಲಾಗಿದೆ"</string> + <!-- no translation found for controls_tile_locked (731547768182831938) --> + <skip /> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"ಪಿನ್ ಅಕ್ಷರಗಳು ಅಥವಾ ಸಂಕೇತಗಳನ್ನು ಒಳಗೊಂಡಿದೆ"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> ಅನ್ನು ಪರಿಶೀಲಿಸಿ"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"ತಪ್ಪಾದ ಪಿನ್"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 362c0bc08707..78c2958f017c 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"스크롤 스크린샷"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"스크린샷 닫기"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"스크린샷 미리보기"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"상단 경계"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"하단 경계"</string> <string name="screenrecord_name" msgid="2596401223859996572">"화면 녹화"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"화면 녹화 처리 중"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"화면 녹화 세션에 관한 지속적인 알림"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"일출까지"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>에 켜짐"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g>까지"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 사용 중지됨"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 사용 설정됨"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"잠금 해제하여 NFC 사용"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"내 조직에 속한 기기입니다."</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>에 속한 기기입니다."</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"전화 기능을 사용하려면 아이콘에서 스와이프하세요."</string> <string name="voice_hint" msgid="7476017460191291417">"음성 지원을 사용하려면 아이콘에서 스와이프하세요."</string> <string name="camera_hint" msgid="4519495795000658637">"카메라를 사용하려면 아이콘에서 스와이프하세요."</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"프로필 표시"</string> <string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string> <string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"게스트 세션을 종료하시겠습니까?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"게스트 세션 종료"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"게스트를 삭제하시겠습니까?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"세션 종료"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"삭제"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"게스트 세션 다시 시작"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"세션을 계속 진행하시겠습니까?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"다시 시작"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"조직에서 직장 프로필에 인증기관을 설치했습니다. 보안 네트워크 트래픽을 모니터링 또는 수정할 수 있습니다."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"이 기기에는 인증기관이 설치되어 있습니다. 보안 네트워크 트래픽을 모니터링 또는 수정할 수 있습니다."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"관리자가 기기에서 발생하는 트래픽을 모니터링하는 네트워크 로깅을 사용 설정했습니다."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"관리자가 직장 프로필에서 발생하는 트래픽을 모니터링하는 네트워크 로깅을 사용 설정했습니다. 하지만 개인 프로필은 모니터링되지 않습니다."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"<xliff:g id="VPN_APP">%1$s</xliff:g>에 연결되었습니다. 이 앱은 이메일, 앱, 웹사이트와 같은 내 네트워크 활동을 모니터링할 수 있습니다."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> 및 <xliff:g id="VPN_APP_1">%2$s</xliff:g>에 연결되었습니다. 이 앱은 이메일, 앱, 웹사이트와 같은 내 네트워크 활동을 모니터링할 수 있습니다."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"직장 프로필이 <xliff:g id="VPN_APP">%1$s</xliff:g>에 연결되었습니다. 이 앱은 이메일, 앱, 웹사이트와 같은 내 네트워크 활동을 모니터링할 수 있습니다."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. 탭하여 음소거로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. 탭하여 진동으로 설정하세요."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. 탭하여 음소거로 설정하세요."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"음소거"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"음소거 해제"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"진동"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"시스템에서 자동으로 이 알림의 순위를 <b>무음으로 낮췄</b>습니다."</string> <string name="feedback_promoted" msgid="2125562787759780807">"알림 창에서 자동으로 이 알림의 순위를 <b>높였</b>습니다."</string> <string name="feedback_demoted" msgid="951884763467110604">"알림 창에서 자동으로 이 알림의 순위를 <b>낮췄</b>습니다."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"맞나요?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"개발자에게 의견을 알려주세요. 정보가 정확했나요?"</string> <string name="feedback_response" msgid="4671729244976641339">"의견을 보내 주셔서 감사합니다."</string> <string name="feedback_ok" msgid="6481426753298857144">"확인"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> 알림 컨트롤을 열었습니다."</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> 위치로 이동"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> 위치에 추가"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> 위치"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"타일 추가됨"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"타일 삭제됨"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"빠른 설정 편집기"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 알림: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"설정 열기"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>에서 <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> 사용 중"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>에서 최근에 <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>을(를) 사용함"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(기업용)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"전화 통화"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"전화 통화"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> 사용)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"카메라"</string> <string name="privacy_type_location" msgid="7991481648444066703">"위치"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"센서 사용 안함"</string> <string name="device_services" msgid="1549944177856658705">"기기 서비스"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"제목 없음"</string> - <string name="restart_button_description" msgid="6916116576177456480">"탭하여 이 앱을 다시 시작하고 전체 화면으로 이동합니다."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"이동"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"시스템 탐색이 업데이트되었습니다. 변경하려면 설정으로 이동하세요."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"설정으로 이동하여 시스템 탐색을 업데이트하세요."</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"추가"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g>에서 제안"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"컨트롤 업데이트됨"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"기기 잠김"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN에 문자나 기호가 포함됨"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> 확인"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"잘못된 PIN"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index fe69443c069a..7a4d7d2caf57 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -93,11 +93,13 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Скриншотту сыдырып кароо"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Скриншотту четке кагуу"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотту алдын ала көрүү"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Жогорку чеги"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Төмөнкү чеги"</string> <string name="screenrecord_name" msgid="2596401223859996572">"экрандан видео жаздырып алуу"</string> <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> @@ -412,6 +414,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Күн чыкканга чейин"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Саат <xliff:g id="TIME">%s</xliff:g> күйөт"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> чейин"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC өчүрүлгөн"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC иштетилген"</string> @@ -421,7 +425,7 @@ <string name="sensor_privacy_start_use_mic_dialog_content" msgid="8643239110815357707">"Улантуу үчүн <b><xliff:g id="APP">%s</xliff:g></b> колдонмосуна түзмөгүңүздүн микрофонун пайдаланууга уруксат беришиңиз керек."</string> <string name="sensor_privacy_start_use_camera_dialog_content" msgid="7773612142162829116">"Улантуу үчүн <b><xliff:g id="APP">%s</xliff:g></b> колдонмосуна түзмөгүңүздүн камерасын пайдаланууга уруксат беришиңиз керек."</string> <string name="media_seamless_remote_device" msgid="177033467332920464">"Түзмөк"</string> - <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Башка колдонмого которулуу үчүн,, өйдө сүрүңүз"</string> + <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Башка колдонмого которулуу үчүн өйдө сүрүңүз"</string> <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Колдонмолорду тез которуштуруу үчүн, оңго сүйрөңүз"</string> <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Сереп салууну өчүрүү/күйгүзүү"</string> <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Кубатталды"</string> @@ -442,12 +446,14 @@ <string name="notification_tap_again" msgid="4477318164947497249">"Ачуу үчүн кайра таптап коюңуз"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Ачуу үчүн өйдө сүрүңүз"</string> <string name="keyguard_retry" msgid="886802522584053523">"Кайталоо үчүн экранды өйдө сүрүңүз"</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC технологиясын колдонуу үчүн кулпуcун ачыңыз"</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC колдонуу үчүн түзмөктүн кулпусун ачыңыз"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Бул түзмөк уюмуңузга таандык"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Бул түзмөк төмөнкүгө таандык: <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> - <string name="phone_hint" msgid="6682125338461375925">"Сүрөтчөнү серпип телефонго өтүңүз"</string> - <string name="voice_hint" msgid="7476017460191291417">"Сүрөтчөнү серпип үн жардамчысына өтүңүз"</string> - <string name="camera_hint" msgid="4519495795000658637">"Сүрөтчөнү серпип камерага өтүңүз"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> + <string name="phone_hint" msgid="6682125338461375925">"Сүрөтчөнү сүрүп телефонго өтүңүз"</string> + <string name="voice_hint" msgid="7476017460191291417">"Сүрөтчөнү сүрүп үн жардамчысына өтүңүз"</string> + <string name="camera_hint" msgid="4519495795000658637">"Сүрөтчөнү сүрүп камерага өтүңүз"</string> <string name="interruption_level_none_with_warning" msgid="8394434073508145437">"Толук жымжырттык талап кылынат. Бул экрандагыны окугучтарды да тынчтандырат."</string> <string name="interruption_level_none" msgid="219484038314193379">"Тымтырс"</string> <string name="interruption_level_priority" msgid="661294280016622209">"Шашылыш билдирүүлөр гана"</string> @@ -465,9 +471,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профилди көрсөтүү"</string> <string name="user_add_user" msgid="4336657383006913022">"Колдонуучу кошуу"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Жаңы колдонуучу"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Конок сеансы бүтүрүлсүнбү?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Конок сеансын бүтүрүү"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Конокту алып саласызбы?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана дайындар өчүрүлөт."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Сеансты бүтүрүү"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Алып салуу"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Кайтып келишиңиз менен, конок!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансыңызды улантасызбы?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Кайра баштоо"</string> @@ -542,6 +549,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ишканаңыз жумуш профилиңизге тастыктоочу борборду орнотту. Коопсуз тармагыңыздын трафиги көзөмөлдөнүп же өзгөртүлүшү мүмкүн."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Бул түзмөктө тастыктоочу борбор орнотулган. Коопсуз тармагыңыздын трафиги көзөмөлдөнүп же өзгөртүлүшү мүмкүн."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Администраторуңуз түзмөгүңүздөгү трафикти көзөмөлдөөчү тармактын таржымалын каттоо функциясын иштетти."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Администраторуңуз жумуш профилиндеги трафикти көзөмөлдөгөн тармакка кирүүнү күйгүздү. Буга жеке профилиңиз кирбейт."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Электрондук почта, колдонмолор жана вебсайттар сыяктуу тармактагы аракеттериңизди тескей турган <xliff:g id="VPN_APP">%1$s</xliff:g> колдонмосуна туташып турасыз."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Электрондук почта, колдонмолор жана вебсайттар сыяктуу тармактагы аракеттериңизди көзөмөлдөй турган <xliff:g id="VPN_APP_0">%1$s</xliff:g> жана <xliff:g id="VPN_APP_1">%2$s</xliff:g> колдонмолоруна туташып турасыз."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Жумуш профилиңиз электрондук почта, колдонмолор жана вебсайттар сыяктуу тармактык аракеттериңизди көзөмөлдөй турган <xliff:g id="VPN_APP">%1$s</xliff:g> колдонмосуна туташып турат."</string> @@ -622,6 +630,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Үнүн өчүрүү үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Дирилдөөгө коюу үчүн басыңыз."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Үнүн өчүрүү үчүн басыңыз."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"үнсүз"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"үнүн чыгаруу"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дирилдөө"</string> @@ -735,7 +745,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Бул билдирмени тутум автоматтык түрдө <b>Үнсүз абалга төмөндөттү</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Бул билдирменин панелиңиздеги абалы автоматтык түрдө <b>жогорулады</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Бул билдирменин панелиңиздеги абалы автоматтык түрдө <b>төмөндөдү</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Бул туурабы?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Иштеп чыгуучуга пикириңизди билдириңиз. Бул туурабы?"</string> <string name="feedback_response" msgid="4671729244976641339">"Пикириңиз үчүн рахмат!"</string> <string name="feedback_ok" msgid="6481426753298857144">"Жарайт"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу үчүн эскертмени көзөмөлдөө функциялары ачылды"</string> @@ -882,6 +892,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Төмөнкүгө жылдыруу: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>-позицияга кошуу"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>-позиция"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Карта кошулду"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Карта өчүрүлдү"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ыкчам жөндөөлөр түзөткүчү."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> эскертмеси: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Жөндөөлөрдү ачуу."</string> @@ -972,7 +984,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> колдонуп жатат"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Жакында <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> колдонулду"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративдик)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефон чалуу"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефон чалуу"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> аркылуу)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string> <string name="privacy_type_location" msgid="7991481648444066703">"жайгашкан жер"</string> @@ -980,7 +992,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Сенсорлорду өчүрүү"</string> <string name="device_services" msgid="1549944177856658705">"Түзмөк кызматтары"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Аталышы жок"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Бул колдонмону өчүрүп күйгүзүп, толук экранга өтүү үчүн, таптап коюңуз."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Жылдыруу"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Тутум чабыттоосу жаңырды. Өзгөртүү үчүн, Жөндөөлөргө өтүңүз."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Тутум чабыттоосун жаңыртуу үчүн Жөндөөлөргө өтүңүз"</string> @@ -1034,6 +1045,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Кошуу"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> сунуштайт"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Башкаруу элементтери жаңырды"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Түзмөк кулпуланды"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN код тамгалардан же символдордон турат"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> түзмөгүн ырастаңыз"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN код туура эмес"</string> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 24c7655e5ae4..51d7b8eff5fc 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -52,6 +52,4 @@ <!-- (footer_height -48dp)/2 --> <dimen name="controls_management_footer_top_margin">4dp</dimen> <dimen name="controls_management_favorites_top_margin">8dp</dimen> - - <dimen name="toast_y_offset">24dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index e5e89e44b4df..7070fa19802c 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"ເລື່ອນຮູບໜ້າຈໍ"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ປິດຮູບໜ້າຈໍ"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ຕົວຢ່າງຮູບໜ້າຈໍ"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"ຂອບເຂດທາງເທິງ"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"ຂອບເຂດທາງລຸ່ມ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ໂປຣແກຣມບັນທຶກໜ້າຈໍ"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ກຳລັງປະມວນຜົນການບັນທຶກໜ້າຈໍ"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"ການແຈ້ງເຕືອນສຳລັບເຊດຊັນການບັນທຶກໜ້າຈໍໃດໜຶ່ງ"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ຈົນກວ່າຕາເວັນຂຶ້ນ"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"ເປີດເວລາ <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"ຈົນຮອດ <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is disabled"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is enabled"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"ປົດລັອກເພື່ອໃຊ້ NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"ອຸປະກອນນີ້ເປັນຂອງອົງການທ່ານ"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"ອຸປະກອນນີ້ເປັນຂອງ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ປັດຈາກໄອຄອນສຳລັບໂທລະສັບ"</string> <string name="voice_hint" msgid="7476017460191291417">"ປັດຈາກໄອຄອນສຳລັບການຊ່ວຍທາງສຽງ"</string> <string name="camera_hint" msgid="4519495795000658637">"ປັດຈາກໄອຄອນສຳລັບກ້ອງຖ່າຍຮູບ"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ສະແດງໂປຣໄຟລ໌"</string> <string name="user_add_user" msgid="4336657383006913022">"ເພີ່ມຜູ້ໃຊ້"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ຜູ່ໃຊ້ໃໝ່"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ສິ້ນສຸດເຊດຊັນແຂກບໍ?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"ສິ້ນສຸດເຊດຊັນແຂກ"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ລຶບແຂກບໍ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯແລະຂໍ້ມູນທັງໝົດໃນເຊດຊັນນີ້ຈະຖືກລຶບອອກ."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ສິ້ນສຸດເຊດຊັນ"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ລຶບ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ຍິນດີຕ້ອນຮັບກັບມາ, ຜູ່ຢ້ຽມຢາມ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ທ່ານຕ້ອງການສືບຕໍ່ເຊດຊັນຂອງທ່ານບໍ່?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ເລີ່ມຕົ້ນໃຫມ່"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ອົງກອນຂອງທ່ານຕິດຕັ້ງອຳນາດໃບຮັບຮອງໄວ້ໃນໂປຣໄຟລ໌ບ່ອນເຮັດວຽກນີ້. ທຣາບຟິກເຄືອຂ່າຍທີ່ເຂົ້າລະຫັດໄວ້ຂອງທ່ານອາດຖືກຕິດຕາມ ຫຼື ແກ້ໄຂໄດ້."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ມີອຳນາດໃບຮັບຮອງຕິດຕັ້ງຢູ່ໃນອຸປະກອນນີ້. ທຣາບຟິກເຄືອຂ່າຍທີ່ເຂົ້າລະຫັດໄວ້ຂອງທ່ານອາດຖືກຕິດຕາມ ຫຼື ແກ້ໄຂໄດ້."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ຜູ້ເບິ່ງແຍງຂອງທ່ານໄດ້ເປີດໃຊ້ການບັນທຶກເຄືອຂ່າຍໄວ້, ເຊິ່ງຈະຕິດຕາມທຣາບຟິກໃນອຸປະກອນຂອງທ່ານ."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"ຜູ້ເບິ່ງແຍງຂອງທ່ານໄດ້ເປີດໃຊ້ການບັນທຶກເຄືອຂ່າຍໄວ້, ເຊິ່ງຈະຕິດຕາມທຣາບຟິກໃນໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານ ແຕ່ຈະບໍ່ຕິດຕາມໃນໂປຣໄຟລ໌ສ່ວນຕົວຂອງທ່ານ."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"ທ່ານເຊື່ອມຕໍ່ກັບ <xliff:g id="VPN_APP">%1$s</xliff:g> ແລ້ວ, ເຊິ່ງຈະສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍ, ຮວມທັງອີເມວ, ແອັບ ແລະ ເວັບໄຊຕ່າງໆຂອງທ່ານໄດ້."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"ທ່ານເຊື່ອມຕໍ່ກັບ <xliff:g id="VPN_APP_0">%1$s</xliff:g> ແລະ <xliff:g id="VPN_APP_1">%2$s</xliff:g> ແລ້ວ, ເຊິ່ງຈະສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍ, ຮວມທັງອີເມວ, ແອັບ ແລະ ເວັບໄຊຕ່າງໆຂອງທ່ານໄດ້."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານເຊື່ອມຕໍ່ຫາ <xliff:g id="VPN_APP">%1$s</xliff:g>, ເຊິ່ງສາມາດຕິດຕາມການເຄື່ອນໄຫວເຄືອຂ່າຍຂອງທ່ານ, ຮວມເຖິງອີເມວ, ແອັບ ແລະ ເວັບໄຊ."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ແຕະເພື່ອປິດສຽງ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. ແຕະເພື່ອຕັ້ງເປັນສັ່ນເຕືອນ."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. ແຕະເພື່ອປິດສຽງ."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ປິດສຽງ"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ເຊົາປິດສຽງ"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ສັ່ນເຕືອນ"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ການແຈ້ງເຕືອນນີ້ <b>ຖືກຫຼຸດລະດັບເປັນປິດສຽງອັດຕະໂນມັດແລ້ວ</b> ໂດຍລະບົບ."</string> <string name="feedback_promoted" msgid="2125562787759780807">"ການແຈ້ງເຕືອນນີ້ <b>ມີອັນດັບສູງຂຶ້ນໂດຍອັດຕະໂນມັດແລ້ວ</b> ໃນໜ້າການແຈ້ງເຕືອນຂອງທ່ານ."</string> <string name="feedback_demoted" msgid="951884763467110604">"ການແຈ້ງເຕືອນນີ້ <b>ມີອັນດັບຕ່ຳລົງໂດຍອັດຕະໂນມັດແລ້ວ</b> ໃນໜ້າການແຈ້ງເຕືອນຂອງທ່ານ."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"ສິ່ງນີ້ບໍ່ຖືກຕ້ອງບໍ?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"ສົ່ງຄຳຕິຊົມຂອງທ່ານໃຫ້ນັກພັດທະນາ. ສິ່ງນີ້ບໍ່ຖືກຕ້ອງບໍ?"</string> <string name="feedback_response" msgid="4671729244976641339">"ຂອບໃຈສຳລັບຄຳເຫັນຂອງທ່ານ!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ຕົກລົງ"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"ເປີດຕົວຄວບຄຸມການແຈ້ງເຕືອນສຳລັບ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ຍ້າຍໄປ <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ເພີ່ມໃສ່ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ເພີ່ມແຜ່ນແລ້ວ"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ລຶບແຜ່ນແລ້ວ"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ຕົວແກ້ໄຂການຕັ້ງຄ່າດ່ວນ"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"ການແຈ້ງເຕືອນ <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ເປີດການຕັ້ງຄ່າ."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ກຳລັງໃຊ້ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ຢູ່"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ໃຊ້ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ເມື່ອບໍ່ດົນມານີ້"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ອົງກອນ)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ໂທລະສັບ"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ໂທລະສັບ"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(ຜ່ານ <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"ກ້ອງຖ່າຍຮູບ"</string> <string name="privacy_type_location" msgid="7991481648444066703">"ສະຖານທີ່"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"ປິດການຮັບຮູ້ຢູ່"</string> <string name="device_services" msgid="1549944177856658705">"ບໍລິການອຸປະກອນ"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"ບໍ່ມີຊື່"</string> - <string name="restart_button_description" msgid="6916116576177456480">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ ແລະ ໃຊ້ແບບເຕັມຈໍ."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ຍ້າຍ"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ອັບເດດການນຳທາງລະບົບແລ້ວ. ເພື່ອປ່ຽນແປງ, ກະລຸນາໄປທີ່ການຕັ້ງຄ່າ."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ໄປທີ່ການຕັ້ງຄ່າເພື່ອອັບເດດການນຳທາງລະບົບ"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"ເພີ່ມ"</string> <string name="controls_dialog_message" msgid="342066938390663844">"ແນະນຳໂດຍ <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"ອັບເດດການຄວບຄຸມແລ້ວ"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"ອຸປະກອນຖືກລັອກໄວ້"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN ປະກອບມີຕົວອັກສອນ ຫຼື ສັນຍາລັກ"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"ຢັ້ງຢືນ <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN ບໍ່ຖືກຕ້ອງ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index a5de09533f23..ff69e056793b 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Viso puslapio ekrano kopija"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Praleisti ekrano kopiją"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrano kopijos peržiūra"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Viršutinė riba"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Apatinė riba"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Ekrano vaizdo įrašytuvas"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Apdorojam. ekrano vaizdo įraš."</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Šiuo metu rodomas ekrano įrašymo sesijos pranešimas"</string> @@ -414,6 +416,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Iki saulėtekio"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Iki <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"ALR"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"ALR išjungtas"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"ALR įjungtas"</string> @@ -447,6 +451,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Norėdami naudoti NFC, atrakinkite"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Šis įrenginys priklauso jūsų organizacijai"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Šis įrenginys priklauso „<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>“"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Perbraukite iš telefono piktogramos"</string> <string name="voice_hint" msgid="7476017460191291417">"Perbraukite iš „Voice Assist“ piktogramos"</string> <string name="camera_hint" msgid="4519495795000658637">"Perbraukite iš fotoaparato piktogramos"</string> @@ -467,9 +473,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Rodyti profilį"</string> <string name="user_add_user" msgid="4336657383006913022">"Pridėti naudotoją"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Naujas naudotojas"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Baigti svečio sesiją?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Baigti svečio sesiją"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Pašalinti svečią?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Baigti sesiją"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Pašalinti"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Sveiki sugrįžę, svety!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ar norite tęsti sesiją?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Pradėti iš naujo"</string> @@ -546,6 +553,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Jūsų organizacija įdiegė darbo profilyje sertifikato įgaliojimą. Jūsų saugaus tinklo srautas gali būti stebimas arba keičiamas."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Šiame įrenginyje įdiegtas sertifikato įgaliojimas. Jūsų saugaus tinklo srautas gali būti stebimas arba keičiamas."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratorius įjungė tinklo duomenų įrašymą į žurnalą. Įjungus šią funkciją stebimas srautas jūsų įrenginyje."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratorius įjungė tinklo duomenų įrašymą į žurnalą. Įjungus šią funkciją stebimas srautas jūsų darbo, bet ne asmeniniame profilyje."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Esate prisijungę prie programos „<xliff:g id="VPN_APP">%1$s</xliff:g>“, kuri gali stebėti tinklo veiklą, įskaitant el. laiškus, programas ir svetaines."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Esate prisijungę prie programų „<xliff:g id="VPN_APP_0">%1$s</xliff:g>“ ir „<xliff:g id="VPN_APP_1">%2$s</xliff:g>“, kurios gali stebėti tinklo veiklą, įskaitant el. laiškus, programas ir svetaines."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Darbo profilis susietas su programa „<xliff:g id="VPN_APP">%1$s</xliff:g>“, kuri gali stebėti tinklo veiklą, įskaitant el. laiškus, programas ir svetaines."</string> @@ -626,6 +634,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Palieskite, kad nutildytumėte. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Palieskite, kad nustatytumėte vibravimą."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Palieskite, kad nutildytumėte."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"nutildyti"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"įjungti garsą"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibruoti"</string> @@ -739,7 +749,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Šio pranešimo svarbą sistema automatiškai <b>sumažino iki begarsio lygio</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Šio pranešimo reitingas pranešimų skydelyje automatiškai <b>padidintas</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Šio pranešimo reitingas pranešimų skydelyje automatiškai <b>sumažintas</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Ar tai teisinga?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Pateikite savo atsiliepimą kūrėjui. Ar tai teisinga?"</string> <string name="feedback_response" msgid="4671729244976641339">"Dėkojame už atsiliepimą!"</string> <string name="feedback_ok" msgid="6481426753298857144">"Gerai"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ pranešimų valdikliai atidaryti"</string> @@ -890,6 +900,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Perkelkite į <xliff:g id="POSITION">%1$d</xliff:g> poziciją"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridėkite <xliff:g id="POSITION">%1$d</xliff:g> pozicijoje"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> pozicija"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Išklotinė pridėta"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Išklotinė pašalinta"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sparčiųjų nustatymų redagavimo priemonė."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"„<xliff:g id="ID_1">%1$s</xliff:g>“ pranešimas: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Atidaryti nustatymus."</string> @@ -980,7 +992,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Programa „<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>“ naudoja: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Programa „<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>“ neseniai naudojo: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(įmonės versija)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefono skambutis"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefono skambutis"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(naud. <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparatą"</string> <string name="privacy_type_location" msgid="7991481648444066703">"vietovę"</string> @@ -988,7 +1000,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Jutikliai išjungti"</string> <string name="device_services" msgid="1549944177856658705">"Įrenginio paslaugos"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Nėra pavadinimo"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Palieskite, kad paleistumėte iš naujo šią programą arba įjungtumėte viso ekrano režimą."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Perkelti"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Sistemos naršymo funkcijos atnaujintos. Jei norite pakeisti, eikite į skiltį „Nustatymai“."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Eikite į skiltį „Nustatymai“, kad atnaujintumėte sistemos naršymo funkcijas"</string> @@ -1044,6 +1055,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Pridėti"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Siūlo „<xliff:g id="APP">%s</xliff:g>“"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Valdikliai atnaujinti"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Įrenginys užrakintas"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN kodą sudaro raidės arba simboliai"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> patvirtinimas"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Netinkamas PIN kodas"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 019810fe9f84..18816ad0c913 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Ritināt ekrānuzņēmumu"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Nerādīt ekrānuzņēmumu"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrānuzņēmuma priekšskatījums"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Augšējā robeža"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Apakšējā robeža"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Ekrāna ierakstītājs"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekrāna ieraksta apstrāde"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Aktīvs paziņojums par ekrāna ierakstīšanas sesiju"</string> @@ -412,6 +414,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Līdz saullēktam"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Plkst. <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Līdz plkst. <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ir atspējoti"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ir iespējoti"</string> @@ -445,6 +449,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Atbloķējiet ierīci, lai izmantotu NFC."</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Šī ierīce pieder jūsu organizācijai."</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Šī ierīce pieder organizācijai <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Lai lietotu tālruni, velciet no ikonas"</string> <string name="voice_hint" msgid="7476017460191291417">"Lai lietotu balss palīgu, velciet no ikonas"</string> <string name="camera_hint" msgid="4519495795000658637">"Lai lietotu kameru, velciet no ikonas"</string> @@ -465,9 +471,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Parādīt profilu"</string> <string name="user_add_user" msgid="4336657383006913022">"Lietotāja pievienošana"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Jauns lietotājs"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vai beigt viesa sesiju?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Beigt viesa sesiju"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vai noņemt viesi?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Beigt sesiju"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Noņemt"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Laipni lūdzam atpakaļ, viesi!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vai vēlaties turpināt savu sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Sākt no sākuma"</string> @@ -543,6 +550,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Jūsu organizācija instalēja sertifikātu jūsu darba profilā. Jūsu drošā tīkla datplūsma var tikt uzraudzīta."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Šajā ierīcē ir instalēts sertifikāts. Drošā tīkla datplūsma var tikt uzraudzīta."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrators ieslēdza tīkla reģistrēšanu, kuru izmanto, lai pārraudzītu datplūsmu jūsu ierīcē."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administrators ir ieslēdzis tīkla reģistrēšanu, kuru izmanto, lai pārraudzītu datplūsmu jūsu darba profilā, bet ne personīgajā profilā."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Ir izveidots savienojums ar lietotni <xliff:g id="VPN_APP">%1$s</xliff:g>, kas var pārraudzīt jūsu darbības tīklā, tostarp e-pasta ziņojumus, lietotnes un vietnes."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Ir izveidots savienojums ar lietotnēm <xliff:g id="VPN_APP_0">%1$s</xliff:g> un <xliff:g id="VPN_APP_1">%2$s</xliff:g>, kas var pārraudzīt jūsu darbības tīklā, tostarp e-pasta ziņojumus, lietotnes un vietnes."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Jūsu darba profilam ir izveidots savienojums ar lietotni <xliff:g id="VPN_APP">%1$s</xliff:g>, kas var pārraudzīt jūsu darbības tīklā, tostarp saņemtos un nosūtītos e-pasta ziņojumus, instalētās lietotnes un apmeklētās tīmekļa vietnes."</string> @@ -623,6 +631,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Pieskarieties, lai izslēgtu skaņu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Pieskarieties, lai iestatītu vibrozvanu."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Pieskarieties, lai izslēgtu skaņu."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izslēgt skaņu"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ieslēgt skaņu"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrēt"</string> @@ -736,7 +746,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Šī paziņojuma svarīgums sistēmā tika automātiski <b>pazemināts, un paziņojums tiks rādīts bez skaņas</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Šī paziņojums rangs paziņojumu panelī tika automātiski <b>paaugstināts</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Šī paziņojuma rangs paziņojumu panelī tika automātiski <b>pazemināts</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Vai šī informācija ir pareiza?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Sniedziet atsauksmes izstrādātājam. Vai šī informācija ir pareiza?"</string> <string name="feedback_response" msgid="4671729244976641339">"Paldies par atsauksmēm!"</string> <string name="feedback_ok" msgid="6481426753298857144">"Labi"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> paziņojumu vadīklas ir atvērtas"</string> @@ -885,6 +895,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pārvietot uz pozīciju numur <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pievienot elementu pozīcijā numur <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozīcija numur <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Elements ir pievienots"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Elements ir noņemts"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ātro iestatījumu redaktors."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> paziņojums: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Atvērt iestatījumus."</string> @@ -975,7 +987,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> pašlaik izmanto šādu darbību: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> nesen izmantoja šādu darbību: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(uzņēmumiem)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Tālruņa zvaniem"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Tālruņa zvans"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(izmantojot lietotni <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"atrašanās vieta"</string> @@ -983,7 +995,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensori izslēgti"</string> <string name="device_services" msgid="1549944177856658705">"Ierīces pakalpojumi"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Nav nosaukuma"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Pieskarieties, lai restartētu šo lietotni un pārietu pilnekrāna režīmā."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Pārvietot"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Sistēmas navigācija ir atjaunināta. Lai veiktu izmaiņas, atveriet iestatījumus."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Atveriet iestatījumus, lai atjauninātu sistēmas navigāciju"</string> @@ -1038,6 +1049,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Pievienot"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Ieteica: <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Vadīklas atjauninātas"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Ierīce ir bloķēta"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN ietver burtus vai simbolus."</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verifikācija: <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Nepareizs PIN"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index c124eefb52cd..59fc2d4849c2 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Континуирана слика од екранот"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Отфрлете ја сликата од екранот"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед на слика од екранот"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Горна граница"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Долна граница"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Снимач на екран"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Се обработува снимка од екран"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Тековно известување за сесија за снимање на екранот"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До изгрејсонце"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Се вклучува во <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC е оневозможено"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC е овозможено"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Отклучете за да користите NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Уредов е во сопственост на организацијата"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Уредов е во сопственост на <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Повлечете од иконата за телефонот"</string> <string name="voice_hint" msgid="7476017460191291417">"Повлечете од иконата за гласовна помош"</string> <string name="camera_hint" msgid="4519495795000658637">"Повлечете од иконата за камерата"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Прикажи го профилот"</string> <string name="user_add_user" msgid="4336657383006913022">"Додај корисник"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нов корисник"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Да се заврши гостинската сесија?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Заврши ја гостинската сесија"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се отстрани гостинот?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијата ќе се избришат."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Заврши ја сесијата"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Отстрани"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дојде пак, гостине!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Дали сакате да продолжите со сесијата?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни одново"</string> @@ -501,7 +508,7 @@ <string name="notification_section_header_gentle" msgid="6804099527336337197">"Безгласно"</string> <string name="notification_section_header_alerting" msgid="5581175033680477651">"Известувања"</string> <string name="notification_section_header_conversations" msgid="821834744538345661">"Разговори"</string> - <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Избриши ги сите тивки известувања"</string> + <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Избриши ги сите бесчујни известувања"</string> <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Известувањата се паузирани од „Не вознемирувај“"</string> <string name="media_projection_action_text" msgid="3634906766918186440">"Започни сега"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Нема известувања"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Вашата организација инсталираше авторитет за сертификат на вашиот работен профил. Вашиот безбеден мрежен сообраќај можно е да се следи или изменува."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На уредов е инсталиран авторитет за сертификат. Вашиот безбеден мрежен сообраќај можно е да се следи или изменува."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Вашиот администратор вклучил евиденција на мрежата, што подразбира следење на сообраќајот на вашиот уред."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Вашиот администратор вклучил мрежна евиденција, што подразбира следење на сообраќајот во работниот, но не и во личниот профил."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Поврзани сте на <xliff:g id="VPN_APP">%1$s</xliff:g>, што може да ја следи вашата активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-сајтовите."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Поврзани сте на <xliff:g id="VPN_APP_0">%1$s</xliff:g> и <xliff:g id="VPN_APP_1">%2$s</xliff:g>, што може да ја следат вашата активност на мрежата, вклучувајќи ги е-пораките, апликациите и веб-сајтовите."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Вашиот работен профил е поврзан на <xliff:g id="VPN_APP">%1$s</xliff:g>, што може да ја следи вашата активност на мрежата, заедно со е-пораките, апликациите и веб-сајтовите."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Допрете за да исклучите звук. Можеби ќе се исклучи звукот на услугите за достапност."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Допрете за да се постави на вибрации."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Допрете за да се исклучи звукот."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"исклучен звук"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"вклучен звук"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрации"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Известувањево беше автоматски <b>намалено на „Тивко“</b> од страна на системот."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Известувањево беше автоматски <b>рангирано повисоко</b> во нијансите за известувања."</string> <string name="feedback_demoted" msgid="951884763467110604">"Известувањево беше автоматски <b>рангирано пониско</b> во нијансите за известувања."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Дали ова беше точно?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Дајте му повратни информации на програмерот. Дали ова беше точно?"</string> <string name="feedback_response" msgid="4671729244976641339">"Фала за повратните информации!"</string> <string name="feedback_ok" msgid="6481426753298857144">"Во ред"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Контролите за известувањата за <xliff:g id="APP_NAME">%1$s</xliff:g> се отворија"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместување на <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додавање на позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Додадена е плочка"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Отстранета е плочка"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уредник за брзи поставки."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известување од <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отворете ги поставките."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> користи <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> користеше <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> неодамна"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(претпријатие)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефонски повик"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефонски повик"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(преку <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string> <string name="privacy_type_location" msgid="7991481648444066703">"локација"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Сензорите се исклучени"</string> <string name="device_services" msgid="1549944177856658705">"Услуги за уредот"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Без наслов"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Допрете за да ја рестартирате апликацијава и да ја отворите на цел екран."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Премести"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Навигацијата на системот е ажурирана. За да извршите промени, одете во „Поставки“."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Одете во „Поставки“ за да ја ажурирате навигацијата на системот"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Додај"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Предложено од <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Контролите се ажурирани"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Уредот е заклучен"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN-кодот содржи букви или симболи"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Потврдете го <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Погрешен PIN"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 965f4df71d7f..44883d6dd797 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"സ്ക്രീൻഷോട്ട് സ്ക്രോൾ ചെയ്യുക"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"സ്ക്രീൻഷോട്ട് ഡിസ്മിസ് ചെയ്യുക"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"സ്ക്രീൻഷോട്ട് പ്രിവ്യു"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"മുകളിലുള്ള അതിർത്തി"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"ചുവടെയുള്ള അതിർത്തി"</string> <string name="screenrecord_name" msgid="2596401223859996572">"സ്ക്രീൻ റെക്കോർഡർ"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"സ്ക്രീൻ റെക്കോർഡിംഗ് പ്രോസസുചെയ്യുന്നു"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"ഒരു സ്ക്രീൻ റെക്കോർഡിംഗ് സെഷനായി നിലവിലുള്ള അറിയിപ്പ്"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"സൂര്യോദയം വരെ"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>-ന്"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> വരെ"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC പ്രവർത്തനരഹിതമാക്കി"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC പ്രവർത്തനക്ഷമമാക്കി"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ഉപയോഗിക്കാൻ അൺലോക്ക് ചെയ്യുക"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"ഈ ഉപകരണം നിങ്ങളുടെ സ്ഥാപനത്തിന്റേതാണ്"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"ഈ ഉപകരണം <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> എന്ന സ്ഥാപനത്തിന്റേതാണ്"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ഫോൺ ഐക്കണിൽ നിന്ന് സ്വൈപ്പുചെയ്യുക"</string> <string name="voice_hint" msgid="7476017460191291417">"വോയ്സ് അസിസ്റ്റിനായുള്ള ഐക്കണിൽ നിന്ന് സ്വൈപ്പുചെയ്യുക"</string> <string name="camera_hint" msgid="4519495795000658637">"ക്യാമറ ഐക്കണിൽ നിന്ന് സ്വൈപ്പുചെയ്യുക"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"പ്രൊഫൈൽ കാണിക്കുക"</string> <string name="user_add_user" msgid="4336657383006913022">"ഉപയോക്താവിനെ ചേര്ക്കുക"</string> <string name="user_new_user_name" msgid="2019166282704195789">"പുതിയ ഉപയോക്താവ്"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"അതിഥി സെഷൻ അവസാനിപ്പിക്കണോ?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"അതിഥി സെഷൻ അവസാനിപ്പിക്കുക"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"അതിഥിയെ നീക്കംചെയ്യണോ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ അപ്ലിക്കേഷനുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"സെഷൻ അവസാനിപ്പിക്കുക"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"നീക്കംചെയ്യുക"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"അതിഥിയ്ക്ക് വീണ്ടും സ്വാഗതം!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"നിങ്ങളുടെ സെഷൻ തുടരണോ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"പുനരാംരംഭിക്കുക"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിൽ നിങ്ങളുടെ സ്ഥാപനമൊരു സർട്ടിഫിക്കറ്റ് അതോറിറ്റി ഇൻസ്റ്റാൾ ചെയ്തിരിക്കുന്നു. നിങ്ങളുടെ സുരക്ഷിത നെറ്റ്വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കപ്പെടുകയോ പരിഷ്കരിക്കപ്പെടുയോ ചെയ്തേക്കാം."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"നിങ്ങളുടെ ഉപകരണത്തിൽ ഒരു സർട്ടിഫിക്കറ്റ് അതോറിറ്റി ഇൻസ്റ്റാൾ ചെയ്തിരിക്കുന്നു. നിങ്ങളുടെ സുരക്ഷിത നെറ്റ്വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കപ്പെടുകയോ പരിഷ്കരിക്കപ്പെടുയോ ചെയ്തേക്കാം."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"നിങ്ങളുടെ അഡ്മിൻ, നെറ്റ്വർക്ക് ലോഗിംഗ് ഓണാക്കിയിട്ടുണ്ട്, ഇതിന് നിങ്ങളുടെ ഉപകരണത്തിലെ ട്രാഫിക്ക് നിരീക്ഷിക്കാൻ കഴിയും."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"നിങ്ങളുടെ അഡ്മിൻ നെറ്റ്വർക്ക് ലോഗിംഗ് ഓണാക്കി, ഇത് നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിലെ ട്രാഫിക് നിരീക്ഷിക്കുന്നു എന്നാൽ വ്യക്തിപരമായ പ്രൊഫൈലിലെ ട്രാഫിക് നിരീക്ഷിക്കുന്നില്ല."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"നിങ്ങൾ <xliff:g id="VPN_APP">%1$s</xliff:g> എന്ന ആപ്പിലേക്ക് കണക്റ്റുചെയ്തിരിക്കുന്നു, ഇമെയിലുകൾ, ആപ്പുകൾ, വെബ്സൈറ്റുകൾ എന്നിവ ഉൾപ്പെടെ നിങ്ങളുടെ നെറ്റ്വർക്ക് ആക്റ്റിവിറ്റി നിരീക്ഷിക്കാൻ ഈ ആപ്പിന് കഴിയും."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"നിങ്ങൾ <xliff:g id="VPN_APP_0">%1$s</xliff:g>, <xliff:g id="VPN_APP_1">%2$s</xliff:g> എന്നീ ആപ്പുകളിലേക്ക് കണക്റ്റുചെയ്തിരിക്കുന്നു, ഇമെയിലുകൾ, ആപ്പുകൾ, വെബ്സൈറ്റുകൾ എന്നിവ ഉൾപ്പെടെ നിങ്ങളുടെ നെറ്റ്വർക്ക് ആക്റ്റിവിറ്റി നിരീക്ഷിക്കാൻ ഈ ആപ്പിന് കഴിയും."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"<xliff:g id="VPN_APP">%1$s</xliff:g> ആപ്പിലേക്ക് നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ കണക്റ്റുചെയ്തിരിക്കുന്നു, ഇമെയിലുകൾ, ആപ്സ്, വെബ്സൈറ്റുകൾ എന്നിവ ഉൾപ്പെടെ നിങ്ങളുടെ നെറ്റ്വർക്ക് ആക്റ്റിവിറ്റി നിരീക്ഷിക്കാൻ ഈ ആപ്പിന് കഴിയും."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. മ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക. ഉപയോഗസഹായി സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s വൈബ്രേറ്റിലേക്ക് സജ്ജമാക്കുന്നതിന് ടാപ്പുചെയ്യുക."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s മ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"മ്യൂട്ട് ചെയ്യുക"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"അൺമ്യൂട്ട് ചെയ്യുക"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"വൈബ്രേറ്റ് ചെയ്യുക"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ഈ അറിയിപ്പ്, സിസ്റ്റം സ്വയമേവ <b>നിശബ്ദമാക്കി തരം താഴ്ത്തി</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"ഈ അറിയിപ്പിന് നിങ്ങളുടെ ഷെയ്ഡിൽ സ്വയമേവ <b>ഉയർന്ന റാങ്കിംഗ് നൽകി</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"ഈ അറിയിപ്പിന് നിങ്ങളുടെ ഷെയ്ഡിൽ സ്വയമേവ <b>താഴ്ന്ന റാങ്കിംഗ് നൽകി</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"ഇത് ശരിയായിരുന്നോ?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"ഡെവലപ്പറിനെ നിങ്ങളുടെ ഫീഡ്ബാക്ക് അറിയിക്കൂ. ഇത് ശരിയായിരുന്നോ?"</string> <string name="feedback_response" msgid="4671729244976641339">"നിങ്ങളുടെ ഫീഡ്ബാക്കിന് നന്ദി!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ശരി"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> ആപ്പിന്റെ അറിയിപ്പ് നിയന്ത്രണങ്ങൾ തുറന്നു"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> എന്നതിലേക്ക് നീക്കുക"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> എന്ന സ്ഥാനത്തേക്ക് ചേർക്കുക"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ടൈൽ ചേർത്തു"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ടൈൽ നീക്കം ചെയ്തു"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ദ്രുത ക്രമീകരണ എഡിറ്റർ."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> അറിയിപ്പ്: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ക്രമീകരണം തുറക്കുക."</string> @@ -967,23 +979,17 @@ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ആപ്പുകൾ നിങ്ങളുടെ <xliff:g id="TYPES_LIST">%s</xliff:g> ഉപയോഗിക്കുന്നു."</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string> <string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" കൂടാതെ "</string> - <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) --> - <skip /> + <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ആപ്പ്, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ഉപയോഗിക്കുന്നു"</string> + <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> അടുത്തിടെ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ഉപയോഗിച്ചു"</string> + <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(എന്റർപ്രൈസ്)"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ഫോൺ കോൾ"</string> + <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> എന്നതിലൂടെ)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"ക്യാമറ"</string> <string name="privacy_type_location" msgid="7991481648444066703">"ലൊക്കേഷന്"</string> <string name="privacy_type_microphone" msgid="9136763906797732428">"മൈക്രോഫോൺ"</string> <string name="sensor_privacy_mode" msgid="4462866919026513692">"സെൻസറുകൾ ഓഫാണ്"</string> <string name="device_services" msgid="1549944177856658705">"ഉപകരണ സേവനങ്ങള്"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"പേരില്ല"</string> - <string name="restart_button_description" msgid="6916116576177456480">"ഈ ആപ്പ് റീസ്റ്റാർട്ട് ചെയ്യാനും പൂർണ്ണ സ്ക്രീനാവാനും ടാപ്പ് ചെയ്യുക."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"നീക്കുക"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"സിസ്റ്റം നാവിഗേഷൻ അപ്ഡേറ്റ് ചെയ്തു. മാറ്റങ്ങൾ വരുത്താൻ ക്രമീകരണത്തിലേക്ക് പോവുക."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"സിസ്റ്റം നാവിഗേഷൻ അപ്ഡേറ്റ് ചെയ്യാൻ ക്രമീകരണത്തിലേക്ക് പോവുക"</string> @@ -1037,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"ചേർക്കുക"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> നിർദ്ദേശിച്ചത്"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"നിയന്ത്രണങ്ങൾ അപ്ഡേറ്റ് ചെയ്തു"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"ഉപകരണം ലോക്ക് ചെയ്തു"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"പിന്നിൽ അക്ഷരങ്ങളോ ചിഹ്നങ്ങളോ അടങ്ങിയിരിക്കുന്നു"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> പരിശോധിച്ചുറപ്പിക്കുക"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"പിൻ തെറ്റാണ്"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 400352b6473a..efb27110abe6 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Бүхэлд нь багтаасан дэлгэцийн агшин"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Дэлгэцийн агшныг хаах"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Дэлгэцийн агшныг урьдчилан үзэх"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Дээд талын хязгаар"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Доод талын хязгаар"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Дэлгэцийн үйлдэл бичигч"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Дэлгэц бичлэг боловсруулж байна"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Дэлгэц бичих горимын үргэлжилж буй мэдэгдэл"</string> @@ -410,6 +412,7 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Нар мандах хүртэл"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>-д"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> хүртэл"</string> + <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Гэрэлтүүлгийг багасгах"</string> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC-г цуцалсан"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC-г идэвхжүүлсэн"</string> @@ -421,7 +424,7 @@ <string name="media_seamless_remote_device" msgid="177033467332920464">"Төхөөрөмж"</string> <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"Апп сэлгэхийн тулд дээш шударна уу"</string> <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"Аппуудыг хурдан сэлгэхийн тулд баруун тийш чирнэ үү"</string> - <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Тоймыг унтраах/асаах"</string> + <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Тоймыг асаах/унтраах"</string> <string name="expanded_header_battery_charged" msgid="5307907517976548448">"Цэнэглэгдсэн"</string> <string name="expanded_header_battery_charging" msgid="1717522253171025549">"Цэнэглэж байна"</string> <string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"дүүргэхэд <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string> @@ -443,6 +446,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC-г ашиглахын тулд түгжээг тайлна уу"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Энэ төхөөрөмж танай байгууллагад харьяалагддаг"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Энэ төхөөрөмж <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>-д харьяалагддаг"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Утсыг гаргахын тулд дүрс тэмдгээс шудрах"</string> <string name="voice_hint" msgid="7476017460191291417">"Дуут туслахыг нээхийн тулд дүрс тэмдгээс шудрах"</string> <string name="camera_hint" msgid="4519495795000658637">"Камер нээхийн тулд дүрс тэмдгийг шудрах"</string> @@ -463,9 +468,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профайлыг харуулах"</string> <string name="user_add_user" msgid="4336657383006913022">"Хэрэглэгч нэмэх"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Шинэ хэрэглэгч"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Зочны сургалтыг дуусгах уу?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Зочны сургалтыг дуусгах"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Зочныг хасах уу?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ сешний бүх апп болон дата устах болно."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Сургалтыг дуусгах"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Хасах"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Тавтай морилно уу!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Та үргэлжлүүлэхийг хүсэж байна уу?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Дахин эхлүүлэх"</string> @@ -540,6 +546,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Таны байгууллага таны ажлын профайлд сертификатын зөвшөөрөл суулгасан байна. Таны аюулгүй сүлжээний ачааллыг өөрчлөх эсвэл хянах боломжтой."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Сертификатын зөвшөөрлийг энэ төхөөрөмжид суулгасан байна. Таны аюулгүй сүлжээний ачааллыг өөрчлөх эсвэл хянах боломжтой."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Таны админ төхөөрөмжийн ачааллыг хянадаг сүлжээний логийг асаасан байна."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Таны админ ажлын профайлын тань ачааллыг хянадаг сүлжээний логийг асаасан бөгөөд энэ нь хувийн профайлын ачааллыг хянахгүй."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Та имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Та имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой <xliff:g id="VPN_APP_0">%1$s</xliff:g>, <xliff:g id="VPN_APP_1">%2$s</xliff:g>-д холбогдсон байна."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Таны ажлын профайл <xliff:g id="VPN_APP">%1$s</xliff:g>-д холбогдсон байна. Энэ нь таны имэйл, апп, веб хуудас зэрэг сүлжээний үйл ажиллагааг хянах боломжтой."</string> @@ -620,6 +627,7 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дууг нь хаахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Чичиргээнд тохируулахын тулд товшино уу."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Дууг хаахын тулд товшино уу."</string> + <string name="volume_ringer_change" msgid="3574969197796055532">"Хонхны горимыг өөрчлөхийн тулд товшино уу"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дууг хаах"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дууг нээх"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"чичрэх"</string> @@ -733,7 +741,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Энэ мэдэгдлийг систем автоматаар <b>Чимээгүй болгож зэрэглэлийг нь бууруулсан</b> байна."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Энэ мэдэгдлийг таны хураангуй самбарт автоматаар <b>дээгүүр зэрэглэл хийсэн</b> байна."</string> <string name="feedback_demoted" msgid="951884763467110604">"Энэ мэдэгдлийг таны хураангуй самбарт автоматаар <b>доогуур зэрэглэл хийсэн</b> байна."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Энэ зөв байсан уу?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Хөгжүүлэгчид санал хүсэлтээ мэдэгдээрэй. Энэ зөв байсан уу?"</string> <string name="feedback_response" msgid="4671729244976641339">"Санал хүсэлтээ илгээсэнд баярлалаа!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ОК"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н мэдэгдлийн хяналтыг нээсэн"</string> @@ -880,6 +888,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> руу зөөнө үү"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> байрлалд нэмнэ үү"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> байрлал"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Хавтан нэмсэн"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Хавтанг хассан"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Түргэн тохиргоо засварлагч."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> мэдэгдэл: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Тохиргоог нээнэ үү."</string> @@ -970,7 +980,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>-г ашиглаж байна"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>-г саяхан ашигласан"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(байгууллага)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Утасны дуудлага"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Утасны дуудлага"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g>-р)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"камер"</string> <string name="privacy_type_location" msgid="7991481648444066703">"байршил"</string> @@ -978,7 +988,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Мэдрэгчийг унтраах"</string> <string name="device_services" msgid="1549944177856658705">"Төхөөрөмжийн үйлчилгээ"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Гарчиггүй"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Энэ аппыг дахин эхлүүлж, бүтэн дэлгэцэд орохын тулд товшино уу."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Зөөх"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Системийн навигацыг шинэчиллээ. Өөрчлөхийн тулд Тохиргоо руу очно уу."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Системийн навигацыг шинэчлэхийн тулд Тохиргоо руу очно уу"</string> @@ -1032,6 +1041,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Нэмэх"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g>-н санал болгосон"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Хяналтуудыг шинэчиллээ"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Төхөөрөмжийг түгжсэн"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"ПИН нь үсэг эсвэл дүрс тэмдэгт агуулдаг"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g>-г бататгах"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"ПИН код буруу байна"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index fe1c910c74ed..fcff3dfea74e 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"स्क्रीनशॉटवर स्क्रोल करा"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"स्क्रीनशॉट डिसमिस करा"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉटचे पूर्वावलोकन"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"सर्वात वरची सीमा"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"तळाची सीमा"</string> <string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रेकॉर्डर"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रेकॉर्डिंग प्रोसेस सुरू"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रेकॉर्ड सत्रासाठी सुरू असलेली सूचना"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सूर्योदयापर्यंत"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> वाजता सुरू होते"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> पर्यंत"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC अक्षम केले आहे"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC सक्षम केले आहे"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC वापरण्यासाठी स्क्रीन अनलॉक करा"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"हे डिव्हाइस तुमच्या संस्थेचे आहे"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> चे आहे"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"फोनसाठी चिन्हावरून स्वाइप करा"</string> <string name="voice_hint" msgid="7476017460191291417">"व्हॉइस सहाय्यासाठी चिन्हावरून स्वाइप करा"</string> <string name="camera_hint" msgid="4519495795000658637">"कॅमेर्यासाठी चिन्हावरून स्वाइप करा"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफाईल दर्शवा"</string> <string name="user_add_user" msgid="4336657383006913022">"वापरकर्ता जोडा"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नवीन वापरकर्ता"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"अतिथी सत्र संपायचे का?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"अतिथी सत्र संपवा"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथी काढायचे?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अॅप्स आणि डेटा हटवला जाईल."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"सत्र संपवा"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"काढा"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथी, तुमचे पुन्हा स्वागत आहे!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"तुम्ही तुमचे सत्र सुरू ठेवू इच्छिता?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"येथून सुरू करा"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"आपल्या संस्थेने आपल्या कार्य प्रोफाइलवर प्रमाणपत्र अधिकार इंस्टॉल केला आहे. आपल्या सुरक्षित नेटवर्क रहदारीचे परीक्षण केले जाऊ शकते किंवा ती सुधारली जाऊ शकते."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"या डिव्हाइसवर प्रमाणपत्र अधिकार इंस्टॉल केला आहे. आपल्या सुरक्षित नेटवर्क रहदारीचे परीक्षण केले जाऊ शकते किंवा ती सुधारली जाऊ शकते."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"आपल्या प्रशासकाने नेटवर्क लॉगिंग सुरू केले आहे, जे आपल्या डिव्हाइसवरील रहदारीचे परीक्षण करते."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"तुमच्या ॲडमिनने नेटवर्क लॉग इन सुरू केले आहे, जे तुमच्या कार्य प्रोफाइलमधील रहदारीचे निरीक्षण करत असले तरी तुमच्या वैयक्तिक प्रोफाइलमधील रहदारीचे निरीक्षण करत नाही."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"तुम्ही <xliff:g id="VPN_APP">%1$s</xliff:g> शी कनेक्ट केले आहे, जे ईमेल, अॅप्स आणि वेबसाइटसहित आपल्या नेटवर्क क्रिया मॉनिटर करू शकते."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"तुम्ही <xliff:g id="VPN_APP_0">%1$s</xliff:g> आणि <xliff:g id="VPN_APP_1">%2$s</xliff:g> शी कनेक्ट केले आहे, जे ईमेल, अॅप्स आणि वेबसाइटसहित आपल्या नेटवर्क क्रिया मॉनिटर करू शकते."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"तुमचे कार्य प्रोफाइल <xliff:g id="VPN_APP">%1$s</xliff:g> शी कनेक्ट केले आहे, जे ईमेल, अॅप्स आणि वेबसाइटसह आपल्या नेटवर्क क्रियाकलापाचे परीक्षण करू शकते."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करण्यासाठी टॅप करा. प्रवेशक्षमता सेवा म्यूट केल्या जाऊ शकतात."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. म्यूट करण्यासाठी टॅप करा."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करा"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"म्यूट काढून टाका"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"व्हायब्रेट करा"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"सिस्टमद्वारे या सूचनेला आपोआप <b>सायलंट म्हणून डीमोट केले</b> गेले."</string> <string name="feedback_promoted" msgid="2125562787759780807">"तुमच्या रंगछटेमध्ये या सूचनेला आपोआप <b>थोडी जास्त</b> म्हणून रँक केले गेले."</string> <string name="feedback_demoted" msgid="951884763467110604">"तुमच्या रंगछटेमध्ये या सूचनेला आपोआप <b>थोडी कमी</b> म्हणून रँक केले गेले."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"हे बरोबर होते का?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"डेव्हलपरला तुमचा फीडबॅक कळवा. हे बरोबर होते का?"</string> <string name="feedback_response" msgid="4671729244976641339">"तुमच्या फीडबॅकबद्दल धन्यवाद!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ओके"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी सूचना नियंत्रणे खुली आहेत"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> यावर हलवा"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> स्थानावर जोडा"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थान <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"टाइल जोडली"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"टाइल काढून टाकली"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिंग्ज संपादक."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिंग्ज उघडा."</string> @@ -967,23 +979,17 @@ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ॲप्लिकेशन्स तुमचे <xliff:g id="TYPES_LIST">%s</xliff:g> वापरत आहे."</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string> <string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" आणि "</string> - <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) --> - <skip /> + <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> हे <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> वापरत आहे"</string> + <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ने अलीकडे <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> वापरले आहे"</string> + <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(एंटरप्राइझ)"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"फोन कॉल"</string> + <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> द्वारे)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"कॅमेरा"</string> <string name="privacy_type_location" msgid="7991481648444066703">"स्थान"</string> <string name="privacy_type_microphone" msgid="9136763906797732428">"मायक्रोफोन"</string> <string name="sensor_privacy_mode" msgid="4462866919026513692">"सेन्सर बंद आहेत"</string> <string name="device_services" msgid="1549944177856658705">"डिव्हाइस सेवा"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"शीर्षक नाही"</string> - <string name="restart_button_description" msgid="6916116576177456480">"हे अॅप रीस्टार्ट करण्यासाठी आणि फुल स्क्रीन करण्यासाठी टॅप करा."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"हलवा"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"सिस्टम नेव्हिगेशन अपडेट केले. बदल करण्यासाठी, सेटिंग्जवर जा."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"सिस्टम नेव्हिगेशन अपडेट करण्यासाठी सेटिंग्जवर जा"</string> @@ -1037,6 +1043,8 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"जोडा"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ने सुचवले आहे"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"नियंत्रणे अपडेट केली आहेत"</string> + <!-- no translation found for controls_tile_locked (731547768182831938) --> + <skip /> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"पिनमध्ये अक्षरे किंवा चिन्हे आहेत"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> ची पडताळणी करा"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"चुकीचा पिन"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index b1d0b4748b85..e723ec6f9035 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Penatalan tangkapan skrin"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ketepikan tangkapan skrin"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pratonton tangkapan skrin"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Sempadan atas"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Sempadan bawah"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Perakam Skrin"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses rakaman skrin"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pemberitahuan breterusan untuk sesi rakaman skrin"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hingga matahari trbt"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Dihidupkan pada <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hingga <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC dilumpuhkan"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC didayakan"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Buka kunci untuk menggunakan NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Peranti ini milik organisasi anda"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Peranti ini milik <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Leret dari ikon untuk telefon"</string> <string name="voice_hint" msgid="7476017460191291417">"Leret dari ikon untuk bantuan suara"</string> <string name="camera_hint" msgid="4519495795000658637">"Leret dari ikon untuk kamera"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Tunjuk profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Tambah pengguna"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baharu"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Tamatkan sesi tetamu?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Tamatkan sesi tetamu"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alih keluar tetamu?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Tamatkan sesi"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alih keluar"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat kembali, tetamu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Adakah anda ingin meneruskan sesi anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulakan semula"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisasi anda memasang sijil kuasa dalam profil kerja anda. Trafik rangkaian selamat anda mungkin dipantau atau diubah suai."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Sijil kuasa dipasang pada peranti ini. Trafik rangkaian selamat anda mungkin dipantau atau diubah suai."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Pentadbir anda telah menghidupkan pengelogan rangkaian yang memantau trafik pada peranti anda."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Pentadbir anda telah menghidupkan pengelogan rangkaian yang memantau trafik dalam profil kerja anda tetapi bukan dalam profil peribadi anda."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Anda dihubungkan ke <xliff:g id="VPN_APP">%1$s</xliff:g>, yang boleh memantau aktiviti rangkaian anda, termasuk e-mel, apl dan tapak web."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Anda dihubungkan ke <xliff:g id="VPN_APP_0">%1$s</xliff:g> dan <xliff:g id="VPN_APP_1">%2$s</xliff:g>, yang boleh memantau aktiviti rangkaian anda, termasuk e-mel, apl dan tapak web."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Profil kerja anda dihubungkan ke <xliff:g id="VPN_APP">%1$s</xliff:g>, yang dapat memantau aktiviti rangkaian anda, termasuk e-mel, apl dan tapak web."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ketik untuk meredam. Perkhidmatan kebolehaksesan mungkin diredamkan."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Ketik untuk menetapkan pada getar."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Ketik untuk meredam."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"redam"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"nyahredam"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Pemberitahuan ini secara automatik <b&gtditurunkan taraf kepada Senyap</b> oleh sistem."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Pemberitahuan ini secara automatik <b>dinilai lebih tinggi</b> dalam rona warna anda."</string> <string name="feedback_demoted" msgid="951884763467110604">"Pemberitahuan ini secara automatik <b>dinilai lebih rendah</b> dalam rona warna anda."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Adakah ini betul?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Beritahu pembangun tentang maklum balas anda. Adakah ini betul?"</string> <string name="feedback_response" msgid="4671729244976641339">"Terima kasih atas maklum balas anda!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kawalan pemberitahuan untuk <xliff:g id="APP_NAME">%1$s</xliff:g> dibuka"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Alih ke <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan pada kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Jubin ditambah"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Jubin dialih keluar"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor tetapan pantas."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Pemberitahuan <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buka tetapan."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> sedang menggunakan <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> menggunakan <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> baru-baru ini"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(perusahaan)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Panggilan telefon"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Panggilan telefon"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(melalui <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"lokasi"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Penderia dimatikan"</string> <string name="device_services" msgid="1549944177856658705">"Perkhidmatan Peranti"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Tiada tajuk"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Ketik untuk memulakan semula apl ini dan menggunakan skrin penuh."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Alih"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigasi sistem dikemas kini. Untuk membuat perubahan, pergi ke Tetapan."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Pergi ke Tetapan untuk mengemas kini navigasi sistem"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Tambah"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Dicadangkan oleh <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Kawalan dikemas kini"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Peranti dikunci"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN mengandungi huruf atau simbol"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Sahkan <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN salah"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 42b5b230eb6f..c57a94c8a7cc 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"ဖန်သားပြင်ဓာတ်ပုံကို လှိမ့်ရန်"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ဖန်သားပြင်ဓာတ်ပုံကို ပယ်သည်"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ဖန်သားပြင်ဓာတ်ပုံ အစမ်းကြည့်ရှုခြင်း"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"ထိပ်ပိုင်းအနားသတ်"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"အောက်ခြေအနားသတ်"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ဖန်သားပြင် ရိုက်ကူးမှု"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ဖန်သားပြင်ရိုက်ကူးနေသည်"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"ဖန်သားပြင် ရိုက်ကူးသည့် စက်ရှင်အတွက် ဆက်တိုက်လာနေသော အကြောင်းကြားချက်"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"နေထွက်ချိန် အထိ"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> တွင် ဖွင့်မည်"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> အထိ"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ကို ပိတ်ထားသည်"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ကို ဖွင့်ထားသည်"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ကို အသုံးပြုရန် လော့ခ်ဖွင့်ပါ"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"ဤစက်ကို သင့်အဖွဲ့အစည်းက ပိုင်ဆိုင်သည်"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"ဤစက်ကို <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> က ပိုင်ဆိုင်သည်"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ဖုန်းအတွက် သင်္ကေတပုံအား ပွတ်ဆွဲပါ"</string> <string name="voice_hint" msgid="7476017460191291417">"အသံအကူအညီအတွက် သင်္ကေတပုံအား ပွတ်ဆွဲပါ"</string> <string name="camera_hint" msgid="4519495795000658637">"ကင်မရာအတွက် သင်္ကေတပုံအား ပွတ်ဆွဲပါ"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ပရိုဖိုင်ကို ပြရန်"</string> <string name="user_add_user" msgid="4336657383006913022">"အသုံးပြုသူ ထည့်ရန်"</string> <string name="user_new_user_name" msgid="2019166282704195789">"အသုံးပြုသူ အသစ်"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ဧည့်သည်စက်ရှင်ကို အဆုံးသတ်မလား။"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"ဧည့်သည်ဆက်ရှင်ကို အဆုံးသတ်ရန်"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ဧည့်သည်ကို ဖယ်ထုတ်လိုက်ရမလား?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"သတ်မှတ်ပေးထားသည့်အချိန် ပြီးဆုံးပြီ"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ဖယ်ထုတ်ပါ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ပြန်လာတာ ကြိုဆိုပါသည်၊ ဧည့်သည်!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"သင်သည် သင်၏ ချိတ်ဆက်မှုကို ဆက်ပြုလုပ် လိုပါသလား?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"အစမှ ပြန်စပါ"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"သင်၏ အဖွဲ့အစည်းသည် သင်၏ အလုပ်ပရိုဖိုင်တွင် စီမံခန့်ခွဲမှုဆိုင်ရာ အသိအမှတ်ပြုလက်မှတ်ကို ထည့်သွင်းထားပါသည်။ လုံခြုံမှုရှိသော ကွန်ရက်ဒေတာစီးဆင်းမှုကို စောင့်ကြည့်ခြင်း သို့မဟုတ် ပြုပြင်ခြင်းများ ပြုလုပ်နိုင်ပါသည်။"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ဤစက်ပစ္စည်းတွင် စီမံခန့်ခွဲမှုဆိုင်ရာ အသိအမှတ်ပြုလက်မှတ်ကို ထည့်သွင်းထားပါသည်။ လုံခြုံမှုရှိသော ကွန်ရက်ဒေတာစီးဆင်းမှုကို စောင့်ကြည့်ခြင်း သို့မဟုတ် ပြုပြင်ခြင်းများ ပြုလုပ်နိုင်ပါသည်။"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"စက်ပစ္စည်းပေါ်ရှိ ဒေတာစီးဆင်းမှုများကို စောင့်ကြည့်နိုင်သည့် ကွန်ရက်မှတ်တမ်းတင်ခြင်းစနစ်ကို သင်၏ စီမံခန့်ခွဲသူက ဖွင့်ထားပါသည်။"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"သင်၏စီမံခန့်ခွဲသူက ကွန်ရက်မှတ်တမ်းတင်ခြင်းကို ဖွင့်လိုက်သည်။ ၎င်းသည် သင့်အလုပ်ပရိုဖိုင်ရှိ ဒေတာစီးဆင်းမှုကို စောင့်ကြည့်သော်လည်း ကိုယ်ပိုင်ပရိုဖိုင်တွင် မစောင့်ကြည့်ပါ။"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"သင်သည် သင်၏ အီးမေးလ်၊ အက်ပ်နှင့် ဝဘ်ဆိုက်များအပါအဝင် ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည့် <xliff:g id="VPN_APP">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားပါသည်။"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"သင်သည် သင်၏ အီးမေးလ်၊ အက်ပ်နှင့် ဝဘ်ဆိုက်များအပါအဝင် သင်၏ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည့် <xliff:g id="VPN_APP_0">%1$s</xliff:g> နှင့် <xliff:g id="VPN_APP_1">%2$s</xliff:g> သို့ ချိတ်ဆက်ထားပါသည်။"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"သင်၏အလုပ်ပရိုဖိုင်သည် အီးမေးလ်၊ အက်ပ်နှင့် ဝဘ်ဆိုက်များအပါအဝင် သင်၏ကွန်ရက်လုပ်ဆောင်ချက်ကို စောင့်ကြည့်နိုင်သည့် <xliff:g id="VPN_APP">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားပါသည်။"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s။ အသံပိတ်ရန် တို့ပါ။ အများသုံးစွဲနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s။ တုန်ခါခြင်းသို့ သတ်မှတ်ရန်တို့ပါ။"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s။ အသံတိတ်ရန် တို့ပါ။"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"အသံတိတ်ရန်"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"အသံဖွင့်ရန်"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"တုန်ခါမှု"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ဤအကြောင်းကြားချက်ကို စနစ်က အလိုအလျောက် <b>အသံတိတ်ခြင်းသို့ ပြန်ချိန်ညှိထားသည်</b>။"</string> <string name="feedback_promoted" msgid="2125562787759780807">"ဤအကြောင်းကြားချက်ကို သင့်အကြောင်းကြားစာအကွက်တွင် အလိုအလျောက် <b>အဆင့်တိုးထားသည်</b>။"</string> <string name="feedback_demoted" msgid="951884763467110604">"ဤအကြောင်းကြားချက်ကို သင့်အကြောင်းကြားစာအကွက်တွင် အလိုအလျောက် <b>အဆင့်လျှော့ထားသည်</b>။"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"ဤအရာက မှန်ပါသလား။"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"သင့်အကြံပြုချက်ကို ဆော့ဖ်ဝဲအင်ဂျင်နီယာအား အသိပေးပါ။ ဤအရာက မှန်ပါသလား။"</string> <string name="feedback_response" msgid="4671729244976641339">"အကြံပြုချက်အတွက် ကျေးဇူးတင်ပါသည်"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် အကြောင်းကြားချက်ထိန်းချုပ်မှုများကို ဖွင့်ထားသည်"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> သို့ ရွှေ့ရန်"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထားသို့ ပေါင်းထည့်ရန်"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထား"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"အကွက်ငယ်ကို ထည့်ပြီးပါပြီ"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"အကွက်ငယ်ကို ဖယ်ရှားပြီးပါပြီ"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"မြန်ဆန်သည့် ဆက်တင်တည်းဖြတ်စနစ်"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> အကြောင်းကြားချက် − <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ဆက်တင်များကို ဖွင့်ပါ။"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> က <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ကို အသုံးပြုနေသည်"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> က <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ကို မကြာသေးမီက အသုံးပြုထားသည်"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(လုပ်ငန်းသုံး)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ဖုန်းခေါ်ဆိုမှု"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ဖုန်းခေါ်ဆိုမှု"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> မှတစ်ဆင့်)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"ကင်မရာ"</string> <string name="privacy_type_location" msgid="7991481648444066703">"တည်နေရာ"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"အာရုံခံကိရိယာများ ပိတ်ထားသည်"</string> <string name="device_services" msgid="1549944177856658705">"စက်ပစ္စည်းဝန်ဆောင်မှုများ"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"ခေါင်းစဉ် မရှိပါ"</string> - <string name="restart_button_description" msgid="6916116576177456480">"ဤအက်ပ်ကို ပြန်စတင်ပြီး မျက်နှာပြင်အပြည့်လုပ်ရန် တို့ပါ။"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ရွှေ့ရန်"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"စနစ်လမ်းညွှန်ခြင်း အပ်ဒိတ်လုပ်ပြီးပါပြီ။ အပြောင်းအလဲများ ပြုလုပ်ရန် \'ဆက်တင်များ\' သို့သွားပါ။"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"စနစ်လမ်းညွှန်ခြင်း အပ်ဒိတ်လုပ်ရန် \'ဆက်တင်များ\' သို့သွားပါ"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"ထည့်ရန်"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> က အကြံပြုထားသည်"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"ထိန်းချုပ်မှု အပ်ဒိတ်လုပ်ပြီးပြီ"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"စက်ကိုလော့ခ်ချထားသည်"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"ပင်နံပါတ်တွင် စာလုံး သို့မဟုတ် သင်္ကေတများပါဝင်သည်"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> ကို အတည်ပြုခြင်း"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"ပင်နံပါတ် မှားနေသည်"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index ef48f8fb37c5..997147dd15f0 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Rull skjermdumpen"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Avvis skjermdumpen"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning av skjermdump"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Øvre grense"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Nedre grense"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Skjermopptaker"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skjermopptaket"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Vedvarende varsel for et skjermopptak"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Til soloppgang"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Slås på klokken <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Til <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC er slått av"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC er slått på"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Lås opp for å bruke NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Denne enheten tilhører organisasjonen din"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Denne enheten tilhører <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Sveip ikonet for å åpne telefon"</string> <string name="voice_hint" msgid="7476017460191291417">"Sveip fra ikonet for å åpne talehjelp"</string> <string name="camera_hint" msgid="4519495795000658637">"Sveip ikonet for å åpne kamera"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Vis profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Legg til brukere"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny bruker"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vil du avslutte gjesteøkten?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Avslutt gjesteøkten"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vil du fjerne gjesten?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle appene og all informasjon i denne økten slettes."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Avslutt økten"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjern"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbake, gjest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsette økten?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start på nytt"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisasjonen din installerte en sertifiseringsinstans i jobbprofilen din. Den sikre nettverkstrafikken din kan overvåkes eller endres."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"En sertifiseringsinstans er installert på denne enheten. Den sikre nettverkstrafikken din kan overvåkes eller endres."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratoren din har slått på loggføring av nettverk, som overvåker trafikken på enheten din."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratoren din har slått på loggføring av nettverk, som overvåker trafikken i jobbprofilen din, men ikke i den personlige profilen din."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Du er koblet til <xliff:g id="VPN_APP">%1$s</xliff:g>, som kan overvåke nettverksaktiviteten din, inkludert e-post, apper og nettsteder."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Du er koblet til <xliff:g id="VPN_APP_0">%1$s</xliff:g> og <xliff:g id="VPN_APP_1">%2$s</xliff:g>, som kan overvåke nettverksaktiviteten din, inkludert e-post, apper og nettsteder."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Jobbprofilen din er koblet til <xliff:g id="VPN_APP">%1$s</xliff:g>, som kan overvåke nettverksaktiviteten din, inkludert e-poster, apper og nettsteder."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Trykk for å slå av lyden. Lyden kan bli slått av for tilgjengelighetstjenestene."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Trykk for å angi vibrasjon."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Trykk for å slå av lyden."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"kutt lyden"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på lyden"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Dette varselet ble automatisk <b>nedgradert til lydløst</b> av systemet."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Dette varselet ble automatisk <b>rangert høyere</b> i panelet."</string> <string name="feedback_demoted" msgid="951884763467110604">"Dette varselet ble automatisk <b>rangert lavere</b> i panelet."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Var det riktig?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Gi utvikleren tilbakemeldingen din. Var det riktig?"</string> <string name="feedback_response" msgid="4671729244976641339">"Takk for tilbakemeldingen!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Varselinnstillingene for <xliff:g id="APP_NAME">%1$s</xliff:g> er åpnet"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytt til <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Legg til posisjonen <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisjon <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"En infobrikke er lagt til"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"En infobrikke er fjernet"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsvindu for hurtiginnstillinger."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-varsel: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Åpne innstillingene."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> bruker <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> har brukt <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> nylig"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonsamtale"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonsamtale"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(til og med <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"posisjon"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensorer er av"</string> <string name="device_services" msgid="1549944177856658705">"Enhetstjenester"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Ingen tittel"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Trykk for å starte denne appen på nytt og vise den i fullskjerm."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Flytt"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Systemnavigeringen er oppdatert. For å gjøre endringer, gå til Innstillinger."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Gå til Innstillinger for å oppdatere systemnavigeringen"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Legg til"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Foreslått av <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Kontrollene er oppdatert"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Enheten er låst"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN-koden inneholder bokstaver eller symboler"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Bekreft <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Feil PIN-kode"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 2594e8c923dd..e03ee5bfb240 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"स्क्रिनसट स्क्रोल गर्नुहोस्"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"स्क्रिनसट हटाउनुहोस्"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रिनसटको पूर्वावलोकन"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"सिरानको सीमा"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"फेदको सीमा"</string> <string name="screenrecord_name" msgid="2596401223859996572">"स्क्रिन रेकर्डर"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रिन रेकर्डिङको प्रक्रिया अघि बढाइँदै"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"कुनै स्क्रिन रेकर्ड गर्ने सत्रका लागि चलिरहेको सूचना"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"सूर्योदयसम्म"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> मा सक्रिय"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> सम्म"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC लाई असक्षम पारिएको छ"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC लाई सक्षम पारिएको छ"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC प्रयोग गर्न स्क्रिन अनलक गर्नुहोस्"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"यो यन्त्र तपाईंको सङ्गठनको स्वामित्वमा छ"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"यो यन्त्र <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> को स्वामित्वमा छ"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"फोनको लागि आइकनबाट स्वाइप गर्नुहोस्"</string> <string name="voice_hint" msgid="7476017460191291417">"आवाज सहायताका लागि आइकनबाट स्वाइप गर्नुहोस्"</string> <string name="camera_hint" msgid="4519495795000658637">"क्यामेराको लागि आइकनबाट स्वाइप गर्नुहोस्"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफाइल देखाउनुहोस्"</string> <string name="user_add_user" msgid="4336657383006913022">"प्रयोगकर्ता थप्नुहोस्"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नयाँ प्रयोगकर्ता"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"अतिथिको सत्र अन्त्य गर्ने हो?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"अतिथिको सत्र अन्त्य गर्नुहोस्"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथि हटाउने हो?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यस सत्रमा सबै एपहरू र डेटा मेटाइनेछ।"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"सत्र अन्त्य गर्नुहोस्"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"हटाउनुहोस्"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"पुनः स्वागत, अतिथि!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"तपाईं आफ्नो सत्र जारी गर्न चाहनुहुन्छ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"सुरु गर्नुहोस्"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"तपाईंको संगठनले तपाईंको कार्य प्रोफाइलमा एउटा प्रमाणपत्र सम्बन्धी अख्तियार सुविधा स्थापना गरेको छ। तपाईंको सुरक्षित नेटवर्क ट्राफिकको अनुगमन वा परिमार्जन हुनसक्छ।"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"यस यन्त्रमा एउटा प्रमाणपत्र सम्बन्धी अख्तियार सुविधा स्थापना गरिएको छ। तपाईंको सुरक्षित नेटवर्कको ट्राफिकको अनुगमन वा परिमार्जन हुनसक्छ।"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"तपाईंका प्रशासकले तपाईंको यन्त्रमा ट्राफिकको अनुगमन गर्ने नेटवर्क लग गर्ने प्रक्रियालाई सक्रिय गर्नुभएको छ।"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"तपाईंका एड्मिनले \'नेटवर्क लगिङ\' सुविधा अन गर्नुभएको छ। यो सुविधाले तपाईंको कार्य प्रोफाइलको ट्राफिक अनुगमन गर्छ तर व्यक्तिगत प्रोफाइलको ट्राफिक भने अनुगमन गर्दैन।"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"तपाईं इमेल, एप र वेबसाइटहरू लगायत तपाईंको नेटवर्कको गतिविधिको अनुगमन गर्नसक्ने <xliff:g id="VPN_APP">%1$s</xliff:g> मा जडान हुनुहुन्छ।"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"तपाईं इमेल, एप र वेबसाइटहरू लगायत तपाईंको नेटवर्कको गतिविधिको अनुगमन गर्नसक्ने <xliff:g id="VPN_APP_0">%1$s</xliff:g> र <xliff:g id="VPN_APP_1">%2$s</xliff:g> मा जडान हुनुहुन्छ।"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"तपाईंको कार्य प्रोफाइल तपाईंका इमेल, एप र वेबसाइटहरू लगायत तपाईंको नेटवर्कको गतिविधिको अनुगमन गर्नसक्ने <xliff:g id="VPN_APP">%1$s</xliff:g> मा जडान छ।"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। म्यूट गर्नाका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s। कम्पन मोडमा सेट गर्न ट्याप गर्नुहोस्।"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s। म्यूट गर्न ट्याप गर्नुहोस्।"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्युट गर्नुहोस्"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्युट गर्नुहोस्"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"कम्पन गर्नुहोस्"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"सिस्टमले स्वतः यस सूचनालाई <b>कम महत्त्वपूर्ण ठानी साइलेन्ट मोडमा</b> सेट गरिदियो।"</string> <string name="feedback_promoted" msgid="2125562787759780807">"तपाईंको सेडमा यो सूचना स्वतः <b>धेरै महत्त्वपूर्ण सूचनाका रूपमा</b> सेट गरियो।"</string> <string name="feedback_demoted" msgid="951884763467110604">"तपाईंको सेडमा यो सूचना स्वतः <b>कम महत्त्वपूर्ण सूचनाका रूपमा</b> सेट गरियो।"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"के यो सही थियो?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"विकासकर्तालाई तपाईंको प्रतिक्रिया थाहा पाउन दिनुहोस्। यो कुरा सही थियो?"</string> <string name="feedback_response" msgid="4671729244976641339">"तपाईंको प्रतिक्रियाका लागि धन्यवाद!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ठिक छ"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> का सूचना सम्बन्धी नियन्त्रणहरूलाई खोलियो"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल सारेर <xliff:g id="POSITION">%1$d</xliff:g> मा लैजानुहोस्"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल यो अवस्था <xliff:g id="POSITION">%1$d</xliff:g> मा हाल्नुहोस्"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"टाइल हालियो"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"टाइल हटाइयो"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिङ सम्पादक।"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> को सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिङहरूलाई खोल्नुहोस्।"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ले <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> प्रयोग गरिरहेको छ"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ले हालसालै <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> प्रयोग गरेको छ"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(इन्टरप्राइज)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"फोन कल"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"फोन कल"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> मार्फत)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"क्यामेरा"</string> <string name="privacy_type_location" msgid="7991481648444066703">"स्थान"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"सेन्सरहरू निष्क्रिय छन्"</string> <string name="device_services" msgid="1549944177856658705">"यन्त्रका सेवाहरू"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"शीर्षक छैन"</string> - <string name="restart_button_description" msgid="6916116576177456480">"यो एप पुनः सुरु गर्न ट्याप गर्नुहोस् र फुल स्क्रिन मोडमा जानुहोस्।"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"सार्नुहोस्"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"प्रणालीको नेभिगेसन अद्यावधिक गरियो। परिवर्तन गर्न सेटिङमा जानुहोस्।"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"प्रणालीको नेभिगेसन अद्यावधिक गर्न सेटिङमा जानुहोस्"</string> @@ -1032,6 +1043,8 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"थप्नुहोस्"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ले सिफारिस गरेको"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"नियन्त्रण सुविधाहरू अद्यावधिक गरिए"</string> + <!-- no translation found for controls_tile_locked (731547768182831938) --> + <skip /> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN मा अक्षर वा चिन्हहरू समाविष्ट हुन्छन्"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> पुष्टि गर्नुहोस्"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN मिलेन"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 02741322f5ac..8ffc3f415442 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Screenshot scrollen"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Screenshot sluiten"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Voorbeeld van screenshot"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Bovengrens"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Ondergrens"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Schermopname"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Schermopname verwerken"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Doorlopende melding voor een schermopname-sessie"</string> @@ -345,7 +347,7 @@ <string name="quick_settings_location_label" msgid="2621868789013389163">"Locatie"</string> <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Locatie uit"</string> <string name="quick_settings_camera_label" msgid="1367149596242401934">"Camera blokkeren"</string> - <string name="quick_settings_mic_label" msgid="8245831073612564953">"Microfoon dempen"</string> + <string name="quick_settings_mic_label" msgid="8245831073612564953">"Microfoon uitzetten"</string> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"Media-apparaat"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"Alleen noodoproepen"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Tot zonsopgang"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aan om <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Tot <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC is uitgeschakeld"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC is ingeschakeld"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ontgrendel het apparaat om NFC te gebruiken"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Dit apparaat is eigendom van je organisatie"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Dit apparaat is eigendom van <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Swipen voor telefoon"</string> <string name="voice_hint" msgid="7476017460191291417">"Swipen vanaf icoon voor spraakassistent"</string> <string name="camera_hint" msgid="4519495795000658637">"Vegen voor camera"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profiel weergeven"</string> <string name="user_add_user" msgid="4336657383006913022">"Gebruiker toevoegen"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nieuwe gebruiker"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Gastsessie beëindigen?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Gastsessie beëindigen"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast verwijderen?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Sessie beëindigen"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwijderen"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gast!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wil je doorgaan met je sessie?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Opnieuw starten"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Je organisatie heeft een certificeringsinstantie geïnstalleerd in je werkprofiel. Je beveiligde netwerkverkeer kan worden bijgehouden of aangepast."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Er is een certificeringsinstantie geïnstalleerd op dit apparaat. Je beveiligde netwerkverkeer kan worden bijgehouden of aangepast."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Je beheerder heeft netwerkregistratie ingeschakeld, waarmee het verkeer op je apparaat wordt bijgehouden."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Je beheerder heeft logboekregistratie voor het netwerk aangezet. Hiermee wordt verkeer in je werkprofiel bijgehouden, maar niet in je persoonlijke profiel."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Je bent verbonden met <xliff:g id="VPN_APP">%1$s</xliff:g>, waarmee je netwerkactiviteit (waaronder e-mails, apps en websites) kan worden gecontroleerd."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Je bent verbonden met <xliff:g id="VPN_APP_0">%1$s</xliff:g> en <xliff:g id="VPN_APP_1">%2$s</xliff:g>, waarmee je netwerkactiviteit (waaronder e-mails, apps en websites) kan worden bijgehouden."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Je werkprofiel is verbonden met <xliff:g id="VPN_APP">%1$s</xliff:g>, waarmee je netwerkactiviteit (waaronder e-mails, apps en websites) kan worden bijgehouden."</string> @@ -612,16 +620,18 @@ <string name="ring_toggle_title" msgid="5973120187287633224">"Gesprekken"</string> <string name="volume_ringer_status_normal" msgid="1339039682222461143">"Bellen"</string> <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Trillen"</string> - <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Dempen"</string> + <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Geluid staat uit"</string> <string name="qs_status_phone_vibrate" msgid="7055409506885541979">"Telefoon op trillen"</string> - <string name="qs_status_phone_muted" msgid="3763664791309544103">"Telefoon gedempt"</string> + <string name="qs_status_phone_muted" msgid="3763664791309544103">"Telefoongeluid staat uit"</string> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tik om dempen op te heffen."</string> - <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om in te stellen op trillen. Toegankelijkheidsservices kunnen zijn gedempt."</string> - <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te dempen. Toegankelijkheidsservices kunnen zijn gedempt."</string> + <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om in te stellen op trillen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string> + <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te dempen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tik om in te stellen op trillen."</string> - <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tik om te dempen."</string> - <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dempen"</string> - <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dempen opheffen"</string> + <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tik om geluid uit te zetten."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> + <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"geluid uit"</string> + <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"geluid aanzetten"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"trillen"</string> <string name="volume_dialog_title" msgid="6502703403483577940">"%s-volumeknoppen"</string> <string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Gesprekken en meldingen gaan over (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string> @@ -716,7 +726,7 @@ <string name="notification_priority_title" msgid="2079708866333537093">"Prioriteit"</string> <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ondersteunt geen gespreksfuncties"</string> <string name="notification_unblockable_desc" msgid="2073030886006190804">"Deze meldingen kunnen niet worden aangepast."</string> - <string name="notification_multichannel_desc" msgid="7414593090056236179">"Deze groep meldingen kan hier niet worden geconfigureerd"</string> + <string name="notification_multichannel_desc" msgid="7414593090056236179">"Deze groep meldingen kan hier niet worden ingesteld"</string> <string name="notification_delegate_header" msgid="1264510071031479920">"Melding via proxy"</string> <string name="notification_channel_dialog_title" msgid="6856514143093200019">"Alle meldingen van <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="see_more_title" msgid="7409317011708185729">"Meer weergeven"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Deze melding is automatisch <b>verlaagd naar Stil</b> door het systeem."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Deze melding is automatisch <b>hoger gerangschikt</b> in je meldingenpaneel."</string> <string name="feedback_demoted" msgid="951884763467110604">"Deze melding is automatisch <b>lager gerangschikt</b> in je meldingenpaneel."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Is dit juist?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Stuur de ontwikkelaar feedback. Was dit goed?"</string> <string name="feedback_response" msgid="4671729244976641339">"Bedankt voor je feedback."</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Beheeropties voor meldingen voor <xliff:g id="APP_NAME">%1$s</xliff:g> geopend"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Verplaatsen naar <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Toevoegen aan positie <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Positie <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tegel toegevoegd"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tegel verwijderd"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor voor \'Snelle instellingen\'."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-melding: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Instellingen openen."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> gebruikt de <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> heeft de <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recent gebruikt"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(zakelijke versie)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefoongesprek"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefoongesprek"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(via <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"locatie"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensoren uit"</string> <string name="device_services" msgid="1549944177856658705">"Apparaatservices"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Geen titel"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Tik om deze app opnieuw te starten en te openen op het volledige scherm."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Verplaatsen"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Systeemnavigatie geüpdatet. Als je wijzigingen wilt aanbrengen, ga je naar Instellingen."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Ga naar Instellingen om de systeemnavigatie te updaten"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Toevoegen"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Voorgesteld door <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Bedieningselementen geüpdated"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Apparaat vergrendeld"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Pincode bevat letters of symbolen"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> verifiëren"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Onjuiste pincode"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 16d8a2969e27..f03c31d313a6 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"ସ୍କ୍ରିନସଟ୍ ସ୍କ୍ରୋଲ୍ କରନ୍ତୁ"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ସ୍କ୍ରିନସଟ୍ ଖାରଜ କରନ୍ତୁ"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ସ୍କ୍ରିନସଟର ପ୍ରିଭ୍ୟୁ"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"ଶୀର୍ଷ ସୀମାରେଖା"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"ନିମ୍ନ ସୀମାରେଖା"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ସ୍କ୍ରିନ୍ ରେକର୍ଡର୍"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ସ୍କ୍ରିନ ରେକର୍ଡିଂର ପ୍ରକ୍ରିୟାକରଣ"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"ଏକ ସ୍କ୍ରିନ୍ ରେକର୍ଡ୍ ସେସନ୍ ପାଇଁ ଚାଲୁଥିବା ବିଜ୍ଞପ୍ତି"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ସକାଳ ପର୍ଯ୍ୟନ୍ତ"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ରେ ଚାଲୁ ହେବ"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ଅକ୍ଷମ କରାଯାଇଛି"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ସକ୍ଷମ କରାଯାଇଛି"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ବ୍ୟବହାର କରିବାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"ଏହି ଡିଭାଇସଟି ଆପଣଙ୍କ ସଂସ୍ଥାର ଅଟେ"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"ଏହି ଡିଭାଇସଟି <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>ର ଅଟେ"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ଫୋନ୍ ପାଇଁ ଆଇକନରୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string> <string name="voice_hint" msgid="7476017460191291417">"ଭଏସ୍ ସହାୟକ ପାଇଁ ଆଇକନରୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string> <string name="camera_hint" msgid="4519495795000658637">"କ୍ୟାମେରା ପାଇଁ ଆଇକନରୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ପ୍ରୋଫାଇଲ୍ ଦେଖାନ୍ତୁ"</string> <string name="user_add_user" msgid="4336657383006913022">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରନ୍ତୁ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ଅତିଥି ସେସନ୍ ଶେଷ କରିବେ?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"ଅତିଥି ସେସନ୍ ଶେଷ କରନ୍ତୁ"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ଅତିଥିଙ୍କୁ କାଢ଼ିଦେବେ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ଅବଧିର ସମସ୍ତ ଆପ୍ ଓ ଡାଟା ଡିଲିଟ୍ ହୋଇଯିବ।"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ସେସନ୍ ଶେଷ କରନ୍ତୁ"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"କାଢ଼ିଦିଅନ୍ତୁ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ପୁଣି ସ୍ୱାଗତ, ଅତିଥି!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ଆପଣ ନିଜର ଅବଧି ଜାରି ରଖିବାକୁ ଚାହାନ୍ତି କି?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ଆରମ୍ଭ କରନ୍ତୁ"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲରେ ଆପଣଙ୍କ ସଂସ୍ଥା ଏକ ସର୍ଟିଫିକେଟ୍ ଅଥରିଟି ଇନଷ୍ଟଲ୍ କରିଛନ୍ତି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ଏହି ଡିଭାଇସରେ ଏକ ସର୍ଟିଫିକେଟ୍ ଅଥରିଟି ଇନଷ୍ଟଲ୍ କରାଯାଇଛି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ଆପଣଙ୍କ ଆଡମିନ୍ ନେଟୱର୍କ ଲଗଇନ୍ କରିବା ଅନ୍ କରିଛନ୍ତି, ଯାହା ଆପଣଙ୍କ ଡିଭାଇସରେ ଟ୍ରାଫିକ୍ ନୀରିକ୍ଷଣ କରେ।"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"ଆପଣଙ୍କ ଆଡମିନ୍ ନେଟୱାର୍କ ଲଗିଂ ଚାଲୁ କରିଛନ୍ତି, ଯାହା ଆପଣଙ୍କ ୱାର୍କ ପ୍ରୋଫାଇଲରେ ଟ୍ରାଫିକ୍ ନିରୀକ୍ଷଣ କରେ କିନ୍ତୁ ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ପ୍ରୋଫାଇଲରେ ନୁହେଁ।"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"ଆପଣ <xliff:g id="VPN_APP">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ, ଯାହା ଇମେଲ୍, ଆପ୍ ଓ ୱେବସାଇଟ୍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"ଆପଣ <xliff:g id="VPN_APP_0">%1$s</xliff:g> ଏବଂ <xliff:g id="VPN_APP_1">%2$s</xliff:g>ରେ ସଂଯୁକ୍ତ, ଯାହା ଇମେଲ୍, ଆପ୍ ଓ ୱେବସାଇଟ୍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲ୍ <xliff:g id="VPN_APP">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ, ଯାହା ଇମେଲ୍, ଆପ୍ ଓ ୱେବସାଇଟ୍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। ମ୍ୟୁଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍ ମ୍ୟୁଟ୍ କରାଯାଇପାରେ।"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s। ଭାଇବ୍ରେଟରେ ସେଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s। ମ୍ୟୁଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ମ୍ୟୁଟ୍"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ଅନ୍-ମ୍ୟୁଟ୍ କରନ୍ତୁ"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ଭାଇବ୍ରେଟ୍"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ସିଷ୍ଟମ୍ ଏହି ବିଜ୍ଞପ୍ତିକୁ ସ୍ୱଚାଳିତ ଭାବେ <b>ନୀରବକୁ ଡିମୋଟ୍ କରିଛି</b>।"</string> <string name="feedback_promoted" msgid="2125562787759780807">"ଆପଣଙ୍କ ସେଡରେ ସ୍ୱଚାଳିତ ଭାବେ ଏହି ବିଜ୍ଞପ୍ତିର <b>ରେଙ୍କ ଉପରକୁ</b> କରାଯାଇଛି।"</string> <string name="feedback_demoted" msgid="951884763467110604">"ଆପଣଙ୍କ ସେଡରେ ସ୍ୱଚାଳିତ ଭାବେ ଏହି ବିଜ୍ଞପ୍ତିର <b>ରେଙ୍କ ତଳକୁ</b> କରାଯାଇଛି।"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"ଏହା ଠିକ୍ ଥିଲା କି?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"ଡେଭଲପରଙ୍କୁ ଆପଣଙ୍କ ମତାମତ ଜଣାନ୍ତୁ। ଏହା ଠିକ୍ ଥିଲା କି?"</string> <string name="feedback_response" msgid="4671729244976641339">"ଆପଣଙ୍କ ମତାମତ ପାଇଁ ଧନ୍ୟବାଦ!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ଠିକ୍ ଅଛି"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ବିଜ୍ଞପ୍ତି ନିୟନ୍ତ୍ରଣ ଖୋଲା ଯାଇଛି"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>କୁ ମୁଭ୍ କରନ୍ତୁ"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ଅବସ୍ଥିତିରେ ଯୋଗ କରନ୍ତୁ"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ଅବସ୍ଥିତି <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ଟାଇଲ୍ ଯୋଗ କରାଯାଇଛି"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ଟାଇଲ୍ କାଢ଼ି ଦିଆଯାଇଛି"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ଦ୍ରୁତ ସେଟିଙ୍ଗ ଏଡିଟର୍।"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ବିଜ୍ଞପ୍ତି: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ସେଟିଂସ୍ ଖୋଲନ୍ତୁ।"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ବ୍ୟବହାର କରୁଛି"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ଏବେ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ବ୍ୟବହାର କରିଛି"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ଏଣ୍ଟରପ୍ରାଇଜ୍)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"ଫୋନକଲ୍"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ଫୋନ୍ କଲ୍"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> ମାଧ୍ୟମରେ)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"କ୍ୟାମେରା"</string> <string name="privacy_type_location" msgid="7991481648444066703">"ଲୋକେସନ୍"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"ସେନ୍ସର୍ଗୁଡ଼ିକ ବନ୍ଦ ଅଛି"</string> <string name="device_services" msgid="1549944177856658705">"ଡିଭାଇସ୍ ସେବାଗୁଡିକ"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"କୌଣସି ଶୀର୍ଷକ ନାହିଁ"</string> - <string name="restart_button_description" msgid="6916116576177456480">"ଏହି ଆପ୍କୁ ରିଷ୍ଟାର୍ଟ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ ଏବଂ ଫୁଲ୍ସ୍କ୍ରିନ୍କୁ ଯାଆନ୍ତୁ।"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ନିଅନ୍ତୁ"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ସିଷ୍ଟମ୍ ନାଭିଗେସନ୍ ଅପ୍ଡେଟ୍ ହୋଇଛି। ପରିବର୍ତ୍ତନ କରିବା ପାଇଁ, ସେଟିଂସ୍କୁ ଯାଆନ୍ତୁ।"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ସିଷ୍ଟମ୍ ନାଭିଗେସନ୍ ଅପ୍ଡେଟ୍ କରିବା ପାଇଁ ସେଟିଂସ୍କୁ ଯାଆନ୍ତୁ"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"ଯୋଗ କରନ୍ତୁ"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ଦ୍ଵାରା ପ୍ରସ୍ତାବିତ"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ଅପଡେଟ୍ କରାଯାଇଛି"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"ଡିଭାଇସ୍ ଲକ୍ ହୋଇଯାଇଛି"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PINରେ ଅକ୍ଷର କିମ୍ୱା ପ୍ରତୀକଗୁଡ଼ିକ ଥାଏ"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> ଯାଞ୍ଚ କରନ୍ତୁ"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"ଭୁଲ PIN"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index d33455d2c82c..459b1631a373 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਨੂੰ ਸਕ੍ਰੋਲ ਕਰੋ"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਖਾਰਜ ਕਰੋ"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਪੂਰਵ-ਝਲਕ"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"ਉੱਪਰ ਦੀ ਸੀਮਾ"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"ਹੇਠਾਂ ਦੀ ਸੀਮਾ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਜਾਰੀ ਹੈ"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"ਕਿਸੇ ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ ਸੈਸ਼ਨ ਲਈ ਚੱਲ ਰਹੀ ਸੂਚਨਾ"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ਸੂਰਜ ਚੜ੍ਹਨ ਤੱਕ"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> ਵਜੇ ਚਾਲੂ"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> ਵਜੇ ਤੱਕ"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ਨੂੰ ਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ਵਰਤਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"ਇਹ ਡੀਵਾਈਸ ਤੁਹਾਡੀ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"ਇਹ ਡੀਵਾਈਸ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ਫ਼ੋਨ ਲਈ ਪ੍ਰਤੀਕ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string> <string name="voice_hint" msgid="7476017460191291417">"ਅਵਾਜ਼ੀ ਸਹਾਇਕ ਲਈ ਪ੍ਰਤੀਕ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string> <string name="camera_hint" msgid="4519495795000658637">"ਕੈਮਰੇ ਲਈ ਪ੍ਰਤੀਕ ਤੋਂ ਸਵਾਈਪ ਕਰੋ"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ਪ੍ਰੋਫਾਈਲ ਦਿਖਾਓ"</string> <string name="user_add_user" msgid="4336657383006913022">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ਕੀ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰਨਾ ਹੈ?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰੋ"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ਕੀ ਮਹਿਮਾਨ ਹਟਾਉਣਾ ਹੈ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿੱਚ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"ਸੈਸ਼ਨ ਸਮਾਪਤ ਕਰੋ"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ਹਟਾਓ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ਮਹਿਮਾਨ, ਫਿਰ ਤੁਹਾਡਾ ਸੁਆਗਤ ਹੈ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਸੈਸ਼ਨ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ਸ਼ੁਰੂ ਕਰੋ"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਇੱਕ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਅਥਾਰਟੀ ਸਥਾਪਤ ਕੀਤੀ ਗਈ ਹੈ। ਤੁਹਾਡੇ ਸੁਰੱਖਿਅਤ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ ਜਾਂ ਉਸਨੂੰ ਸੋਧਿਆ ਜਾ ਸਕਦਾ ਹੈ।"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ਇੱਕ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਅਥਾਰਟੀ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਥਾਪਤ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਤੁਹਾਡੇ ਸੁਰੱਖਿਅਤ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ ਜਾਂ ਉਸਨੂੰ ਸੋਧਿਆ ਜਾ ਸਕਦਾ ਹੈ।"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਨੇ ਨੈੱਟਵਰਕ ਲੌਗਿੰਗ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਹੋਇਆ ਹੈ, ਜੋ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰਦਾ ਹੈ।"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਨੇ ਨੈੱਟਵਰਕ ਲੌਗ-ਇਨ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਹੋਇਆ ਹੈ, ਜੋ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਟਰੈਫ਼ਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰਦਾ ਹੈ ਪਰ ਤੁਹਾਡੀ ਨਿੱਜੀ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਨਹੀਂ।"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"ਤੁਸੀਂ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੋ, ਜੋ ਈਮੇਲਾਂ, ਐਪਾਂ, ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ।"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"ਤੁਸੀਂ <xliff:g id="VPN_APP_0">%1$s</xliff:g> ਅਤੇ <xliff:g id="VPN_APP_1">%2$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੋ, ਜੋ ਈਮੇਲਾਂ, ਐਪਾਂ, ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀਆਂ ਹਨ।"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"ਤੁਹਾਡੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ <xliff:g id="VPN_APP">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੈ, ਜੋ ਈਮੇਲਾਂ, ਐਪਾਂ ਅਤੇ ਵੈੱਬਸਾਈਟਾਂ ਸਮੇਤ ਤੁਹਾਡੀ ਨੈੱਟਵਰਕ ਸਰਗਰਮੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ।"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। ਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s। ਥਰਥਰਾਹਟ \'ਤੇ ਸੈੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s। ਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ਮਿਊਟ ਕਰੋ"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ਅਣਮਿਊਟ ਕਰੋ"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ਥਰਥਰਾਹਟ"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ਸਿਸਟਮ ਨੇ ਇਸ ਸੂਚਨਾ ਦਾ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ <b>ਦਰਜਾ ਘਟਾ ਕੇ ਸ਼ਾਂਤ</b> \'ਤੇ ਸੈੱਟ ਕਰ ਦਿੱਤਾ ਗਿਆ ਸੀ।"</string> <string name="feedback_promoted" msgid="2125562787759780807">"ਤੁਹਾਡੇ ਸ਼ੇਡ ਵਿੱਚ ਇਸ ਸੂਚਨਾ ਦਾ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ <b>ਦਰਜਾ ਉੱਪਰ</b> ਕਰ ਦਿੱਤਾ ਗਿਆ ਸੀ।"</string> <string name="feedback_demoted" msgid="951884763467110604">"ਤੁਹਾਡੇ ਸ਼ੇਡ ਵਿੱਚ ਇਸ ਸੂਚਨਾ ਦਾ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ <b>ਦਰਜਾ ਹੇਠਾਂ</b> ਕਰ ਦਿੱਤਾ ਗਿਆ ਸੀ।"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"ਕੀ ਇਹ ਸਹੀ ਸੀ?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"ਵਿਕਾਸਕਾਰ ਨੂੰ ਆਪਣੇ ਵਿਚਾਰ ਦੱਸੋ। ਕੀ ਇਹ ਸਹੀ ਸੀ?"</string> <string name="feedback_response" msgid="4671729244976641339">"ਤੁਹਾਡੇ ਵਿਚਾਰ ਲਈ ਧੰਨਵਾਦ!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ਠੀਕ ਹੈ"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਸੂਚਨਾ ਕੰਟਰੋਲਾਂ ਨੂੰ ਖੋਲ੍ਹਿਆ ਗਿਆ"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> \'ਤੇ ਲਿਜਾਓ"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ਸਥਾਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ਸਥਾਨ <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ਟਾਇਲ ਨੂੰ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ਟਾਇਲ ਨੂੰ ਹਟਾ ਦਿੱਤਾ ਗਿਆ"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਸੰਪਾਦਕ।"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ਸੂਚਨਾ: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ।"</string> @@ -967,23 +979,17 @@ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"ਐਪਲੀਕੇਸ਼ਨਾਂ ਤੁਹਾਡੇ <xliff:g id="TYPES_LIST">%s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀਆਂ ਹਨ।"</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string> <string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" ਅਤੇ "</string> - <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) --> - <skip /> + <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ਐਪ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ"</string> + <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ਨੇ ਹਾਲ ਹੀ ਵਿੱਚ <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕੀਤੀ"</string> + <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ਐਂਟਰਪ੍ਰਾਈਜ਼)"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ਫ਼ੋਨ ਕਾਲ"</string> + <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> ਰਾਹੀਂ)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"ਕੈਮਰਾ"</string> <string name="privacy_type_location" msgid="7991481648444066703">"ਟਿਕਾਣਾ"</string> <string name="privacy_type_microphone" msgid="9136763906797732428">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string> <string name="sensor_privacy_mode" msgid="4462866919026513692">"ਸੈਂਸਰ ਬੰਦ ਕਰੋ"</string> <string name="device_services" msgid="1549944177856658705">"ਡੀਵਾਈਸ ਸੇਵਾਵਾਂ"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"ਕੋਈ ਸਿਰਲੇਖ ਨਹੀਂ"</string> - <string name="restart_button_description" msgid="6916116576177456480">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਪੂਰੀ-ਸਕ੍ਰੀਨ ਮੋਡ \'ਤੇ ਜਾਓ।"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ਲਿਜਾਓ"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"ਸਿਸਟਮ ਨੈਵੀਗੇਸ਼ਨ ਅੱਪਡੇਟ ਹੋ ਗਿਆ। ਤਬਦੀਲੀਆਂ ਕਰਨ ਲਈ, ਸੈਟਿੰਗਾਂ \'ਤੇ ਜਾਓ।"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ਸਿਸਟਮ ਨੈਵੀਗੇਸ਼ਨ ਨੂੰ ਅੱਪਡੇਟ ਕਰਨ ਲਈ ਸੈਟਿੰਗਾਂ \'ਤੇ ਜਾਓ"</string> @@ -1037,6 +1043,8 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ਵੱਲੋਂ ਸੁਝਾਇਆ ਗਿਆ"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"ਕੰਟਰੋਲ ਅੱਪਡੇਟ ਕੀਤੇ ਗਏ"</string> + <!-- no translation found for controls_tile_locked (731547768182831938) --> + <skip /> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"ਪਿੰਨ ਵਿੱਚ ਅੱਖਰ ਜਾਂ ਚਿੰਨ੍ਹ ਸ਼ਾਮਲ ਹਨ"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"ਗਲਤ ਪਿੰਨ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 6440a65a9eaf..e16b5ec73d15 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Przewiń zrzut ekranu"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Zamknij zrzut ekranu"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Podgląd zrzutu ekranu"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Górna granica"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Dolna granica"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Nagrywanie ekranu"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Przetwarzam nagrywanie ekranu"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Stałe powiadomienie o sesji rejestrowania zawartości ekranu"</string> @@ -414,6 +416,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do wschodu słońca"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Włącz o <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"Komunikacja NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Komunikacja NFC jest wyłączona"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Komunikacja NFC jest włączona"</string> @@ -444,9 +448,11 @@ <string name="notification_tap_again" msgid="4477318164947497249">"Kliknij ponownie, by otworzyć"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Przesuń w górę, by otworzyć"</string> <string name="keyguard_retry" msgid="886802522584053523">"Przesuń w górę, by spróbować ponownie"</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Odblokuj, by użyć NFC"</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Odblokuj, by użyć komunikacji NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"To urządzenie należy do Twojej organizacji"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Właściciel tego urządzenia: <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Aby włączyć telefon, przesuń palcem od ikony"</string> <string name="voice_hint" msgid="7476017460191291417">"Aby uzyskać pomoc głosową, przesuń palcem od ikony"</string> <string name="camera_hint" msgid="4519495795000658637">"Przesuń palcem od ikony, by włączyć aparat"</string> @@ -467,9 +473,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Pokaż profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodaj użytkownika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nowy użytkownik"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Zakończyć sesję gościa?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Zakończ sesję gościa"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Usunąć gościa?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Zakończ sesję"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Usuń"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Witaj ponownie, gościu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcesz kontynuować sesję?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Rozpocznij nową"</string> @@ -546,6 +553,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Twoja organizacja zainstalowała urząd certyfikacji w Twoim profilu służbowym. Zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Urząd certyfikacji zainstalowany na tym urządzeniu. Twój zabezpieczony ruch w sieci może być monitorowany i zmieniany."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrator włączył rejestrowanie sieciowe, które pozwala monitorować ruch na Twoim urządzeniu."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administrator włączył rejestrowanie sieciowe, które pozwala monitorować ruch na Twoim profilu służbowym, ale nie na profilu osobistym."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Łączysz się z aplikacją <xliff:g id="VPN_APP">%1$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Łączysz się z aplikacjami <xliff:g id="VPN_APP_0">%1$s</xliff:g> i <xliff:g id="VPN_APP_1">%2$s</xliff:g>, które mogą monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Twój profil służbowy jest połączony z aplikacją <xliff:g id="VPN_APP">%1$s</xliff:g>, która może monitorować Twoją aktywność w sieci, w tym e-maile, aplikacje i strony internetowe."</string> @@ -626,6 +634,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Kliknij, by wyciszyć. Ułatwienia dostępu mogą być wyciszone."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Kliknij, by włączyć wibracje."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Kliknij, by wyciszyć."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"wycisz"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"wyłącz wyciszenie"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"włącz wibracje"</string> @@ -739,7 +749,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"To powiadomienie zostało automatycznie <b>zmienione na Ciche</b> przez system."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Ważność tego powiadomienia została automatycznie <b>podniesiona</b> przez system."</string> <string name="feedback_demoted" msgid="951884763467110604">"Ważność tego powiadomienia została automatycznie <b>obniżona</b> przez system."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Czy to było prawidłowe?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Daj znać deweloperowi, co o tym sądzisz. Czy to było prawidłowe?"</string> <string name="feedback_response" msgid="4671729244976641339">"Dziękujemy za opinię"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Sterowanie powiadomieniami aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> otwarte"</string> @@ -890,6 +900,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Przenieś do pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodaj w pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozycja <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Dodano kartę"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Usunięto kartę"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Edytor szybkich ustawień."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Powiadomienie z aplikacji <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otwórz ustawienia."</string> @@ -980,7 +992,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacja <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> używa: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacja <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> używała ostatnio: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(wersja firmowa)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Rozmowa telefoniczna"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Rozmowa telefoniczna"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(przez: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"aparat"</string> <string name="privacy_type_location" msgid="7991481648444066703">"lokalizacja"</string> @@ -988,7 +1000,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Wyłącz czujniki"</string> <string name="device_services" msgid="1549944177856658705">"Usługi urządzenia"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Bez tytułu"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Kliknij, by uruchomić tę aplikację ponownie i przejść w tryb pełnoekranowy."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Przenieś"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Nawigacja w systemie została zaktualizowana. Aby wprowadzić zmiany, otwórz Ustawienia."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Otwórz Ustawienia, by zaktualizować nawigację w systemie"</string> @@ -1044,6 +1055,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Dodaj"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Sugestia: <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Zaktualizowano elementy sterujące"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Urządzenie zablokowane"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Kod PIN zawiera litery lub symbole"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Sprawdź urządzenie <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Nieprawidłowy kod PIN"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 40dc2de30ac4..efe0f064c350 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Captura de tela da página inteira"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dispensar captura de tela"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Visualização de captura de tela"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Limite superior"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Limite inferior"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Até o nascer do sol"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ativar: <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Até: <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"A NFC está desativada"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"A NFC está ativada"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloqueie para usar a NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence à sua organização"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertence à organização <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Deslize a partir do ícone do telefone"</string> <string name="voice_hint" msgid="7476017460191291417">"Deslize a partir do ícone de assistência de voz"</string> <string name="camera_hint" msgid="4519495795000658637">"Deslize a partir do ícone da câmera"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Encerrar sessão de visitante?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Encerrar sessão de visitante"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover convidado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Encerrar sessão"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo, convidado."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Sua organização instalou uma autoridade de certificação no seu perfil de trabalho. É possível monitorar ou modificar seu tráfego de rede seguro."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Uma autoridade de certificação foi instalada neste dispositivo. É possível monitorar ou modificar seu tráfego de rede seguro."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Seu administrador ativou o registro de rede, que monitora o tráfego no seu dispositivo."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Seu administrador ativou o registro de rede, que monitora o tráfego no seu perfil de trabalho, mas não no perfil pessoal."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Você está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorar sua atividade na rede, incluindo e-mails, apps e websites."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Você está conectado a <xliff:g id="VPN_APP_0">%1$s</xliff:g> e <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que podem monitorar sua atividade de rede, incluindo e-mails, apps e websites."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Seu perfil de trabalho está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorar sua atividade de rede, incluindo e-mails, apps e websites."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Toque para configurar para vibrar."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toque para silenciar."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Esta notificação foi automaticamente <b>rebaixada para Silenciosa</b> pelo sistema."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Esta notificação foi automaticamente <b>classificada com maior prioridade</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Esta notificação foi automaticamente <b>classificada com menor prioridade</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Isso está correto?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Envie seu feedback ao desenvolvedor. Isso está correto?"</string> <string name="feedback_response" msgid="4671729244976641339">"Agradecemos seu feedback."</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string> @@ -786,7 +796,7 @@ <string name="keyboard_key_media_stop" msgid="1509943745250377699">"Parar"</string> <string name="keyboard_key_media_next" msgid="8502476691227914952">"Avançar"</string> <string name="keyboard_key_media_previous" msgid="5637875709190955351">"Anterior"</string> - <string name="keyboard_key_media_rewind" msgid="3450387734224327577">"Retroceder"</string> + <string name="keyboard_key_media_rewind" msgid="3450387734224327577">"Voltar"</string> <string name="keyboard_key_media_fast_forward" msgid="3572444327046911822">"Avançar rapidamente"</string> <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string> <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Bloco adicionado"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Bloco removido"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configurações."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"O app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está usando <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"O app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> usou <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recentemente"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresarial)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Chamada telefônica"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Chamada telefônica"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(pelo app <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"câmera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"localização"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensores desativados"</string> <string name="device_services" msgid="1549944177856658705">"Serviços do dispositivo"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Sem título"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Toque para reiniciar o app e usar tela cheia."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mover"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navegação no sistema atualizada. Se quiser alterá-la, acesse as configurações."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Acesse as configurações para atualizar a navegação no sistema"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Adicionar"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Sugerido por <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Controles atualizados"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Dispositivo bloq."</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"O PIN contém letras ou símbolos"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verificar <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN incorreto"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 22241f8fc280..3a82febd79b7 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Deslocar captura de ecrã"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ignorar captura de ecrã"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pré-visualização da captura de ecrã"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Limite superior"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Limite inferior"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de ecrã"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"A processar a gravação de ecrã"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação persistente de uma sessão de gravação de ecrã"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Até ao amanhecer"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ativado à(s) <xliff:g id="TIME">%s</xliff:g>."</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Até à(s) <xliff:g id="TIME">%s</xliff:g>."</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"O NFC está desativado"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"O NFC está ativado"</string> @@ -440,9 +444,11 @@ <string name="notification_tap_again" msgid="4477318164947497249">"Toque novamente para abrir"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Deslize rapidamente para cima para abrir"</string> <string name="keyguard_retry" msgid="886802522584053523">"Deslize rapidamente para cima para tentar novamente."</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloqueie para utilizar o NFC"</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquear para utilizar o NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence à sua entidade."</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertence à entidade <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Deslize rapid. a partir do ícone para aceder ao telemóvel"</string> <string name="voice_hint" msgid="7476017460191291417">"Deslize rapid. a partir do ícone para aceder ao assist. voz"</string> <string name="camera_hint" msgid="4519495795000658637">"Deslize rapidamente a partir do ícone para aceder à câmara"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Adicionar utilizador"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo utilizador"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Pretende terminar a sessão de convidado?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Terminar sessão de convidado"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover o convidado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as aplicações e dados desta sessão serão eliminados."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Terminar sessão"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo de volta, caro(a) convidado(a)!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Pretende continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"A sua entidade instalou uma autoridade de certificação no seu perfil de trabalho. O tráfego da sua rede segura pode ser monitorizado ou alterado."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Está instalada uma autoridade de certificação neste dispositivo. O tráfego da sua rede segura pode ser monitorizado ou alterado."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"O gestor ativou os registos de rede, que monitorizam o tráfego no seu dispositivo."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"O seu administrador ativou os registos de rede, que monitorizam o tráfego no seu perfil de trabalho, mas não no seu perfil pessoal."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Está ligado à rede <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorizar a sua atividade de rede, incluindo emails, aplicações e Sites."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Está ligado às redes <xliff:g id="VPN_APP_0">%1$s</xliff:g> e <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que podem monitorizar a sua atividade de rede, incluindo emails, aplicações e Sites."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"O seu perfil de trabalho está ligado à rede <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorizar a sua atividade de rede, incluindo emails, aplicações e Sites."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para desativar o som. Os serviços de acessibilidade podem ser silenciados."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Toque para ativar a vibração."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toque para desativar o som."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar som"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"reativar som"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Esta notificação foi automaticamente <b>despromovida para Silenciosa</b> pelo sistema."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Esta notificação passou automaticamente para uma <b>classificação superior</b> no seu painel."</string> <string name="feedback_demoted" msgid="951884763467110604">"Esta notificação passou automaticamente para uma <b>classificação inferior</b> no seu painel."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Estava correto?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Partilhe o seu feedback com o programador. Estava correto?"</string> <string name="feedback_response" msgid="4671729244976641339">"Obrigado pelo seu feedback!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Controlos de notificações da app <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mova para <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicione à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Cartão adicionado"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Cartão removido"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de definições rápidas."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir as definições."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"A app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está a utilizar a app <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>."</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Recentemente, a app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> utilizou a app <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>."</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresarial)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Chamada telefónica"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Chamada"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(através de <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"câmara"</string> <string name="privacy_type_location" msgid="7991481648444066703">"localização"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensores desativados"</string> <string name="device_services" msgid="1549944177856658705">"Serviços do dispositivo"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Sem título"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Toque para reiniciar esta app e ficar em ecrã inteiro."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mover"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"A navegação no sistema foi atualizada. Para efetuar alterações, aceda às Definições."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Aceda às Definições para atualizar a navegação no sistema."</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Adicionar"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Sugerido por <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Controlos atualizados"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Dispositivo bloq."</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"O PIN contém letras ou símbolos."</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Validar <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN incorreto"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 40dc2de30ac4..efe0f064c350 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Captura de tela da página inteira"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Dispensar captura de tela"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Visualização de captura de tela"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Limite superior"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Limite inferior"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Até o nascer do sol"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ativar: <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Até: <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"A NFC está desativada"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"A NFC está ativada"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloqueie para usar a NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence à sua organização"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertence à organização <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Deslize a partir do ícone do telefone"</string> <string name="voice_hint" msgid="7476017460191291417">"Deslize a partir do ícone de assistência de voz"</string> <string name="camera_hint" msgid="4519495795000658637">"Deslize a partir do ícone da câmera"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string> <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Encerrar sessão de visitante?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Encerrar sessão de visitante"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover convidado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Encerrar sessão"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo, convidado."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Sua organização instalou uma autoridade de certificação no seu perfil de trabalho. É possível monitorar ou modificar seu tráfego de rede seguro."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Uma autoridade de certificação foi instalada neste dispositivo. É possível monitorar ou modificar seu tráfego de rede seguro."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Seu administrador ativou o registro de rede, que monitora o tráfego no seu dispositivo."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Seu administrador ativou o registro de rede, que monitora o tráfego no seu perfil de trabalho, mas não no perfil pessoal."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Você está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorar sua atividade na rede, incluindo e-mails, apps e websites."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Você está conectado a <xliff:g id="VPN_APP_0">%1$s</xliff:g> e <xliff:g id="VPN_APP_1">%2$s</xliff:g>, que podem monitorar sua atividade de rede, incluindo e-mails, apps e websites."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Seu perfil de trabalho está conectado a <xliff:g id="VPN_APP">%1$s</xliff:g>, que pode monitorar sua atividade de rede, incluindo e-mails, apps e websites."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Toque para configurar para vibrar."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toque para silenciar."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Esta notificação foi automaticamente <b>rebaixada para Silenciosa</b> pelo sistema."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Esta notificação foi automaticamente <b>classificada com maior prioridade</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Esta notificação foi automaticamente <b>classificada com menor prioridade</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Isso está correto?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Envie seu feedback ao desenvolvedor. Isso está correto?"</string> <string name="feedback_response" msgid="4671729244976641339">"Agradecemos seu feedback."</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Controles de notificação de <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string> @@ -786,7 +796,7 @@ <string name="keyboard_key_media_stop" msgid="1509943745250377699">"Parar"</string> <string name="keyboard_key_media_next" msgid="8502476691227914952">"Avançar"</string> <string name="keyboard_key_media_previous" msgid="5637875709190955351">"Anterior"</string> - <string name="keyboard_key_media_rewind" msgid="3450387734224327577">"Retroceder"</string> + <string name="keyboard_key_media_rewind" msgid="3450387734224327577">"Voltar"</string> <string name="keyboard_key_media_fast_forward" msgid="3572444327046911822">"Avançar rapidamente"</string> <string name="keyboard_key_page_up" msgid="173914303254199845">"Page Up"</string> <string name="keyboard_key_page_down" msgid="9035902490071829731">"Page Down"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Bloco adicionado"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Bloco removido"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configurações."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"O app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> está usando <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"O app <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> usou <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> recentemente"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(empresarial)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Chamada telefônica"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Chamada telefônica"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(pelo app <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"câmera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"localização"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensores desativados"</string> <string name="device_services" msgid="1549944177856658705">"Serviços do dispositivo"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Sem título"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Toque para reiniciar o app e usar tela cheia."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mover"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navegação no sistema atualizada. Se quiser alterá-la, acesse as configurações."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Acesse as configurações para atualizar a navegação no sistema"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Adicionar"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Sugerido por <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Controles atualizados"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Dispositivo bloq."</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"O PIN contém letras ou símbolos"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verificar <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN incorreto"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index f32453b9a4d8..18093b4e5422 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Derulați captura de ecran"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Închideți captura de ecran"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Previzualizare a capturii de ecran"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Marginea superioară"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Marginea inferioară"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Recorder pentru ecran"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Se procesează înregistrarea"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificare în curs pentru o sesiune de înregistrare a ecranului"</string> @@ -412,6 +414,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Până la răsărit"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Activată la <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Până la <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Serviciul NFC este dezactivat"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Serviciul NFC este activat"</string> @@ -445,6 +449,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Deblocați pentru a folosi NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Dispozitivul aparține organizației dvs."</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Acest dispozitiv aparține organizației <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Glisați dinspre telefon"</string> <string name="voice_hint" msgid="7476017460191291417">"Glisați dinspre pictogramă pentru asistentul vocal"</string> <string name="camera_hint" msgid="4519495795000658637">"Glisați pentru a fotografia"</string> @@ -465,9 +471,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afișați profilul"</string> <string name="user_add_user" msgid="4336657383006913022">"Adăugați un utilizator"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Utilizator nou"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Încheiați sesiunea pentru invitați?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Încheiați sesiunea pentru invitați"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ștergeți invitatul?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Încheiați sesiunea"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ștergeți"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bine ați revenit în sesiunea pentru invitați!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vreți să continuați sesiunea?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începeți din nou"</string> @@ -543,6 +550,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizația dvs. a instalat un certificat CA în profilul dvs. de serviciu. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Pe acest dispozitiv este instalat un certificat CA. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratorul dvs. a activat înregistrarea în jurnal pentru rețea, funcție ce monitorizează traficul de pe dispozitivul dvs."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratorul a activat înregistrarea în jurnal pentru rețea, funcție ce monitorizează traficul în profilul dvs. de serviciu, dar nu și în profilul personal."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"V-ați conectat la aplicația <xliff:g id="VPN_APP">%1$s</xliff:g>, care vă poate monitoriza activitatea în rețea, inclusiv e-mailurile, aplicațiile și site-urile accesate."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"V-ați conectat la <xliff:g id="VPN_APP_0">%1$s</xliff:g> și la <xliff:g id="VPN_APP_1">%2$s</xliff:g>, care vă pot monitoriza activitatea în rețea, inclusiv e-mailurile, aplicațiile și site-urile accesate."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Profilul dvs. de serviciu este conectat la <xliff:g id="VPN_APP">%1$s</xliff:g>, care vă poate monitoriza activitatea în rețea, inclusiv e-mailurile, aplicațiile și site-urile accesate."</string> @@ -623,6 +631,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Atingeți pentru a dezactiva sunetul. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Atingeți pentru a seta pe vibrații."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Atingeți pentru a dezactiva sunetul."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dezactivați sunetul"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activați sunetul"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrații"</string> @@ -736,7 +746,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Notificarea a fost <b>setată automat ca Silențioasă</b> de sistem."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Notificarea a fost <b>clasificată automat mai sus</b> în umbră."</string> <string name="feedback_demoted" msgid="951884763467110604">"Notificarea a fost <b>clasificată automat mai jos</b> în umbră."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Este corect?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Trimiteți feedback dezvoltatorului. Este corect?"</string> <string name="feedback_response" msgid="4671729244976641339">"Mulțumim pentru feedback!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Opțiunile privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g> sunt afișate"</string> @@ -885,6 +895,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mutați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adăugați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Cardul a fost adăugat"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Cardul a fost eliminat"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editorul pentru setări rapide."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificare <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Deschideți setările."</string> @@ -975,7 +987,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> folosește <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> a folosit recent <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Apel telefonic"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(prin <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"cameră foto"</string> <string name="privacy_type_location" msgid="7991481648444066703">"locație"</string> @@ -983,7 +995,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Senzori dezactivați"</string> <string name="device_services" msgid="1549944177856658705">"Servicii pentru dispozitiv"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Fără titlu"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Atingeți ca să reporniți aplicația și să treceți în modul ecran complet."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Mutați"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigarea în sistem a fost actualizată. Pentru a face modificări, accesați Setările."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Accesați Setările pentru a actualiza navigarea în sistem"</string> @@ -1038,6 +1049,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Adăugați"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Sugerat de <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"S-au actualizat comenzile"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Dispozitiv blocat"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Codul PIN conține litere sau simboluri"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verificați <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Cod PIN greșit"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 40fb25865a7c..a9f7c02c4bde 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Прокрутить скриншот"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Закрыть скриншот"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Предварительный просмотр скриншота"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Верхняя граница"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Нижняя граница"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Запись видео с экрана"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обработка записи с экрана…"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущее уведомление для записи видео с экрана"</string> @@ -414,6 +416,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До рассвета"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Включить в <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"Модуль NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Модуль NFC отключен"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Модуль NFC включен"</string> @@ -447,6 +451,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Чтобы использовать NFC, разблокируйте устройство."</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Это устройство принадлежит вашей организации"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Этим устройством владеет организация \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\""</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Телефон: проведите от значка"</string> <string name="voice_hint" msgid="7476017460191291417">"Аудиоподсказки: проведите от значка"</string> <string name="camera_hint" msgid="4519495795000658637">"Камера: проведите от значка"</string> @@ -467,9 +473,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показать профиль."</string> <string name="user_add_user" msgid="4336657383006913022">"Добавить пользователя"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новый пользователь"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Завершить гостевой сеанс?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Завершить гостевой сеанс"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Удалить аккаунт гостя?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Завершить сеанс"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Удалить"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Рады видеть вас снова!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продолжить сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Начать заново"</string> @@ -546,6 +553,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Ваша организация установила сертификат ЦС в рабочем профиле. Она может отслеживать и изменять защищенный сетевой трафик."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На устройстве установлен сертификат ЦС. Ваш защищенный сетевой трафик могут отслеживать и изменять."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Администратор включил ведение сетевого журнала, чтобы отслеживать трафик на вашем устройстве."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Администратор включил ведение сетевого журнала, чтобы отслеживать трафик в вашем рабочем профиле (информация из личного профиля не собирается)."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Запущено приложение \"<xliff:g id="VPN_APP">%1$s</xliff:g>\". Оно может отслеживать ваши действия в сети, включая работу с электронной почтой, приложениями и сайтами."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Запущены приложения \"<xliff:g id="VPN_APP_0">%1$s</xliff:g>\" и \"<xliff:g id="VPN_APP_1">%2$s</xliff:g>\". Они могут отслеживать ваши действия в сети, включая работу с электронной почтой, приложениями и сайтами."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"В рабочем профиле запущено приложение \"<xliff:g id="VPN_APP">%1$s</xliff:g>\", которое может отслеживать ваши действия в сети, включая работу с электронной почтой, приложениями и веб-сайтами."</string> @@ -626,6 +634,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Нажмите, чтобы выключить звук. Специальные возможности могут прекратить работу."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Нажмите, чтобы включить вибрацию."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Нажмите, чтобы выключить звук."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"отключить звук"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"включить звук"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"включить вибрацию"</string> @@ -739,7 +749,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Уровень важности этого уведомления был автоматически </b>понижен до \"Без звука\"</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Уровень важности этого уведомления на панели был автоматически <b>повышен</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Уровень важности этого уведомления на панели был автоматически <b>понижен</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Все верно?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Все верно? Поделитесь своим мнением с разработчиком."</string> <string name="feedback_response" msgid="4671729244976641339">"Спасибо!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ОК"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Настройки уведомлений для приложения <xliff:g id="APP_NAME">%1$s</xliff:g> открыты"</string> @@ -890,6 +900,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Переместить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Панель добавлена"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Панель удалена"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор быстрых настроек."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Уведомление <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Открыть настройки."</string> @@ -980,7 +992,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Приложение \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\" использует другое (<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>)."</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Приложение \"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>\" недавно использовало другое (<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>)."</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративная версия)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Приложение для звонков"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефонный звонок"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(через приложение \"<xliff:g id="ATTRIBUTION">%s</xliff:g>\")"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"камера"</string> <string name="privacy_type_location" msgid="7991481648444066703">"местоположение"</string> @@ -988,7 +1000,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Датчики отключены"</string> <string name="device_services" msgid="1549944177856658705">"Сервисы устройства"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Без названия"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Нажмите, чтобы перезапустить приложение и перейти в полноэкранный режим."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Перенести"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Параметры навигации в системе обновлены. Чтобы изменить их, перейдите в настройки."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Чтобы обновить параметры навигации в системе, перейдите в настройки."</string> @@ -1044,6 +1055,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Добавить"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Предложено приложением \"<xliff:g id="APP">%s</xliff:g>\""</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Элементы управления обновлены."</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Устройство заблокировано"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN-код содержит буквы или символы"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Подтвердите устройство \"<xliff:g id="DEVICE">%s</xliff:g>\""</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Неверный PIN-код"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index f3d04091f096..5134e04d72d0 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"තිර රුව අනුචලනය කරන්න"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"තිර රුව ඉවත ලන්න"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"තිර රූ පෙර දසුන"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"ඉහළම මායිම"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"පහළම මායිම"</string> <string name="screenrecord_name" msgid="2596401223859996572">"තිර රෙකෝඩරය"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"තිර පටිගත කිරීම සකසමින්"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"තිර පටිගත කිරීමේ සැසියක් සඳහා කෙරෙන දැනුම් දීම"</string> @@ -410,6 +412,7 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"හිරු නගින තෙක්"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ට ක්රියාත්මකයි"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> තෙක්"</string> + <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"දීප්තිය අඩු කරන්න"</string> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC අබලයි"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC සබලයි"</string> @@ -443,6 +446,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC භාවිත කිරීමට අගුලු හරින්න"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"මෙම උපාංගය ඔබේ සංවිධානයට අයිතිය"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"මෙම උපාංගය <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> සංවිධානයට අයිතිය"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"දුරකථනය සඳහා නිරූපකය වෙතින් ස්වයිප් කරන්න"</string> <string name="voice_hint" msgid="7476017460191291417">"හඬ සහාය සඳහා නිරූපකය වෙතින් ස්වයිප් කරන්න"</string> <string name="camera_hint" msgid="4519495795000658637">"කැමරාව සඳහා නිරූපකය වෙතින් ස්වයිප් කරන්න"</string> @@ -463,9 +468,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"පැතිකඩ පෙන්වන්න"</string> <string name="user_add_user" msgid="4336657383006913022">"පරිශීලකයෙක් එක් කරන්න"</string> <string name="user_new_user_name" msgid="2019166282704195789">"නව පරිශීලකයා"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"ආරාධිත සැසිය අවසන් කරන්නද?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"ආරාධිත සැසිය අවසන් කරන්න"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"අමුත්තාන් ඉවත් කරන්නද?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"සැසිය අවසන් කරන්න"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ඉවත් කරන්න"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"නැවත සාදරයෙන් පිළිගනිමු, අමුත්තා!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ඔබගේ සැසිය දිගටම කරගෙන යෑමට ඔබට අවශ්යද?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"යළි මුල සිට අරඹන්න"</string> @@ -540,6 +546,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ඔබගේ සංවිධානය ඔබගේ කාර්යාල පැතිකඩ තුළ සහතික අධිකාරියක් ස්ථාපනය කර තිබේ. ඔබගේ ආරක්ෂක ජාල තදබදය නිරීක්ෂණය හෝ වෙනස් කිරීමට පුළුවනි."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"මෙම උපාංගය තුළ සහතික අධිකාරියක් ස්ථාපනය කර තිබේ. ඔබගේ ආරක්ෂක ජාල තදබදය නිරීක්ෂණය හෝ වෙනස් කිරීමට පුළුවනි."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ඔබගේ පරිපාලක ඔබගේ උපාංගය මත තදබදය නිරීක්ෂණය කරන ජාල ලොග් කිරීම ක්රියාත්මක කර ඇත."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"ඔබගේ පරිපාලක ඔබගේ පුද්ගලික පැතිකඩෙහි නොව කාර්යාල පැතිකඩෙහි තදබදය නිරීක්ෂණය කරන, ජාල පිරීම ක්රියාත්මක කර ඇත."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"ඊ-තැපැල්, යෙදුම් සහ වෙබ් අඩවි ඇතුළු ඔබේ ජාල ක්රියාකාරකම් නිරීක්ෂණය කළ හැකි <xliff:g id="VPN_APP">%1$s</xliff:g>, වෙත ඔබ සම්බන්ධ වී ඇත."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"ඊ-තැපැල්, යෙදුම් සහ වෙබ් අඩවි ඇතුළු ඔබේ ජාල ක්රියාකාරකම් නිරීක්ෂණය කළ හැකි <xliff:g id="VPN_APP_0">%1$s</xliff:g> සහ <xliff:g id="VPN_APP_1">%2$s</xliff:g> වෙත ඔබ සම්බන්ධ වී ඇත."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"ඊ-තැපැල්, යෙදුම් සහ වෙබ් අඩවි ඇතුළු ඔබේ ජාල ක්රියාකාරකම් නිරීක්ෂණය කළ හැකි <xliff:g id="VPN_APP">%1$s</xliff:g>, වෙත ඔබේ කාර්යාල පැතිකඩ සම්බන්ධ වී ඇත."</string> @@ -620,6 +627,7 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. නිහඬ කිරීමට තට්ටු කරන්න. ප්රවේශ්යතා සේවා නිහඬ කළ හැකිය."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. කම්පනය කිරීමට සකස් කිරීමට තට්ටු කරන්න."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. නිහඬ කිරීමට තට්ටු කරන්න."</string> + <string name="volume_ringer_change" msgid="3574969197796055532">"නාදකය වෙනස් කිරීමට තට්ටු කරන්න"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"නිහඬ කරන්න"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"නිශ්ශබ්දතාවය ඉවත් කරන්න"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"කම්පනය"</string> @@ -733,7 +741,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"පද්ධතිය මගින් මෙම දැනුම්දීම ස්වයංක්රියව <b>නිශ්ශබ්ද වෙත පහත දමන ලදි</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"ඔබගේ වැස්ම තුළ මෙම දැනුම්දීම ස්වයංක්රියව <b>ඉහළට ශ්රේණිගත කරන ලදි</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"ඔබගේ වැස්ම තුළ මෙම දැනුම්දීම ස්වයංක්රියව <b>පහළට ශ්රේණිගත කරන ලදි</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"මෙය නිවැරදි වුයේද?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"ඔබගේ ප්රතිපෝෂණය සංවර්ධකට දන්වන්න. මෙය නිවැරදි වුයේද?"</string> <string name="feedback_response" msgid="4671729244976641339">"ඔබේ ප්රතිපෝෂණයට ස්තූතියි!"</string> <string name="feedback_ok" msgid="6481426753298857144">"හරි"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා දැනුම්දීම් පාලන විවෘත කරන ලදී"</string> @@ -880,6 +888,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> වෙත ගෙන යන්න"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ස්ථානයට එක් කරන්න"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ස්ථානය <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ටයිල් එක එක් කරන ලදි"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ටයිල් ඉවත් කරන ලදි"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ඉක්මන් සැකසුම් සංස්කාරකය."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> දැනුම්දීම: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"සැකසීම් විවෘත කරන්න."</string> @@ -970,7 +980,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> භාවිත කරමින් ඇත"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> මෑතකදී <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> භාවිත කළේය"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ව්යවසාය)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"දුරකථන ඇමතුම"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"දුරකථන ඇමතුම"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> හරහා)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"කැමරාව"</string> <string name="privacy_type_location" msgid="7991481648444066703">"ස්ථානය"</string> @@ -978,7 +988,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"සංවේදක ක්රියාවිරහිතයි"</string> <string name="device_services" msgid="1549944177856658705">"උපාංග සේවා"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"මාතෘකාවක් නැත"</string> - <string name="restart_button_description" msgid="6916116576177456480">"මෙම යෙදුම යළි ඇරඹීමට සහ පූර්ණ තිරයට යාමට තට්ටු කරන්න"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ගෙන යන්න"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"පද්ධති සංචලනය යාවත්කාලීන කළා. වෙනස්කම් සිදු කිරීමට, සැකසීම් වෙත යන්න."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"පද්ධති සංචලනය යාවත්කාලීන කිරීමට සැකසීම් වෙත යන්න"</string> @@ -1032,6 +1041,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"එක් කරන්න"</string> <string name="controls_dialog_message" msgid="342066938390663844">"යෝජනා කළේ <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"පාලන යාවත්කාලීනයි"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"උපාංගය අගුලු දමා ඇත"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN හි අකුරු හෝ සංකේත අඩංගු වේ"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> සත්යාපනය කරන්න"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"වැරදි PIN එකකි"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 7e3f3c010d68..d514b66e30ad 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Posúvať snímku obrazovky"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Zavrieť snímku obrazovky"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ukážka snímky obrazovky"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Horná hranica"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Dolná hranica"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Spracúva sa záznam obrazovky"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Zobrazuje sa upozornenie týkajúce sa relácie záznamu obrazovky"</string> @@ -414,6 +416,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do východu slnka"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Zapne sa o <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC je deaktivované"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC je aktivované"</string> @@ -447,6 +451,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ak chcete použiť NFC, odomknite"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Toto zariadenie patrí vašej organizácii"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Toto zariadení patrí organizácii <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Telefón otvoríte prejdením prstom od ikony"</string> <string name="voice_hint" msgid="7476017460191291417">"Hlasového asistenta otvoríte prejdením prstom od ikony"</string> <string name="camera_hint" msgid="4519495795000658637">"Fotoaparát otvoríte prejdením prstom od ikony"</string> @@ -467,9 +473,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Zobraziť profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Pridať používateľa"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nový používateľ"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Chcete ukončiť reláciu hosťa?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Ukončiť reláciu hosťa"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Odstrániť hosťa?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ukončiť reláciu"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstrániť"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Hosť, vitajte späť!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relácii pokračovať?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začať odznova"</string> @@ -546,6 +553,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizácia nainštalovala pre váš pracovný profil certifikačnú autoritu. Zabezpečená sieťová premávka môže byť sledovaná či upravená."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"V tomto zariadení je nainštalovaná certifikačná autorita. Zabezpečená sieťová premávka môže byť sledovaná či upravená."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Správca aktivoval zapisovanie do denníka siete, ktoré sleduje premávku na vašom zariadení."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Správca aktivoval zapisovanie do denníka siete, ktoré sleduje premávku vo vašom pracovnom profile, ale nie osobnom."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Pripojili ste sa k aplikácii <xliff:g id="VPN_APP">%1$s</xliff:g>, ktorá môže sledovať vašu aktivitu v sieti, vrátane správ, aplikácií a webových stránok."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Pripojili ste sa k aplikáciám <xliff:g id="VPN_APP_0">%1$s</xliff:g> a <xliff:g id="VPN_APP_1">%2$s</xliff:g>, ktoré môžu sledovať vašu aktivitu v sieti, vrátane správ, aplikácií a webových stránok."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Váš pracovný profil je pripojený k aplikácii <xliff:g id="VPN_APP">%1$s</xliff:g>, ktorá môže sledovať vašu aktivitu v sieti vrátane správ, aplikácií a webových stránok."</string> @@ -626,6 +634,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Klepnutím vypnite zvuk. Služby dostupnosti je možné stlmiť."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Klepnutím nastavíte vibrovanie."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Klepnutím vypnete zvuk."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnite zvuk"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnite zvuk"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"zapnite vibrovanie"</string> @@ -739,7 +749,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Systém toto upozornenie automaticky <b>preradil nižšie do kategórie Tiché</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Toto upozornenie bolo na vašom paneli automaticky <b>preradené vyššie</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Toto upozornenie bolo na vašom paneli automaticky <b>preradené nižšie</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Bolo toto správne?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Pošlite vývojárovi svoju spätnú väzbu. Bolo toto správne?"</string> <string name="feedback_response" msgid="4671729244976641339">"Ďakujeme za váš názor."</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Ovládanie upozornení pre aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g> je otvorené"</string> @@ -890,6 +900,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Presunúť na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridať na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozícia"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Karta bola pridaná"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Karta bola odstránená"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rýchlych nastavení"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Upozornenie <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvoriť nastavenia"</string> @@ -980,7 +992,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> používa aplikáciu <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikácia <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> použila nedávno aplikáciu <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(podniková verzia)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonický hovor"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonický hovor"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(prostredníctvom aplikácie <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparát"</string> <string name="privacy_type_location" msgid="7991481648444066703">"poloha"</string> @@ -988,7 +1000,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Senzory sú vypnuté"</string> <string name="device_services" msgid="1549944177856658705">"Služby zariadenia"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Bez názvu"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Klepnutím reštartujete túto aplikáciu a prejdete do režimu celej obrazovky."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Presunúť"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigácia v systéme bola aktualizovaná. Ak chcete vykonať zmeny, prejdite do Nastavení."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Prejdite do Nastavení a aktualizujte navigáciu v systéme"</string> @@ -1044,6 +1055,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Pridať"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Navrhuje <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Ovládanie bolo aktualizované"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Uzamknuté zariadenie"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN obsahuje písmená či symboly"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g>, overenie"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Nesprávny PIN"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index b7ebea9ebb19..b7b2dd0e4784 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Drseče pomikanje po posnetku zaslona"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Opusti posnetek zaslona"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Predogled posnetka zaslona"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Zgornji rob"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Spodnji rob"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Snemalnik zaslona"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obdelava videoposnetka zaslona"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Nenehno obveščanje o seji snemanja zaslona"</string> @@ -414,6 +416,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Do sončnega vzhoda"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Vklop ob <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Do <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Tehnologija NFC je onemogočena"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Tehnologija NFC je omogočena"</string> @@ -444,9 +448,11 @@ <string name="notification_tap_again" msgid="4477318164947497249">"Znova se dotaknite, da odprete"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Povlecite navzgor, da odprete"</string> <string name="keyguard_retry" msgid="886802522584053523">"Povlecite navzgor za vnovičen poskus"</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Odklenite napravo, če želite uporabljati vmesnik NFC."</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Odklenite napravo, če želite uporabljati NFC."</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Ta naprava pripada vaši organizaciji"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Ta naprava pripada organizaciji <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Povlecite z ikone za telefon"</string> <string name="voice_hint" msgid="7476017460191291417">"Povlecite z ikone za glasovnega pomočnika"</string> <string name="camera_hint" msgid="4519495795000658637">"Povlecite z ikone za fotoaparat"</string> @@ -467,9 +473,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaz profila"</string> <string name="user_add_user" msgid="4336657383006913022">"Dodajanje uporabnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nov uporabnik"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Želite končati sejo gosta?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Končaj sejo gosta"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite odstraniti gosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Končaj sejo"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstrani"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Znova pozdravljeni, gost!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite nadaljevati sejo?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začni znova"</string> @@ -546,6 +553,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Vaša organizacija je v vaš delovni profil namestila overitelja potrdil. Varni omrežni promet se lahko nadzira ali spreminja."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"V tej napravi je nameščen overitelj potrdil. Varni omrežni promet se lahko nadzira ali spreminja."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Skrbnik je vklopil beleženje omrežnega prometa, ki nadzira promet v napravi."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Skrbnik je vklopil beleženje omrežnega prometa, ki nadzoruje samo promet v delovnem profilu, tistega v osebnem profilu pa ne."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Povezani ste z aplikacijo <xliff:g id="VPN_APP">%1$s</xliff:g>, ki lahko nadzira omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Povezani ste z aplikacijama <xliff:g id="VPN_APP_0">%1$s</xliff:g> in <xliff:g id="VPN_APP_1">%2$s</xliff:g>, ki lahko nadzirata omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Delovni profil je povezan z aplikacijo <xliff:g id="VPN_APP">%1$s</xliff:g>, ki lahko nadzira omrežno dejavnost, vključno z e-pošto, aplikacijami in spletnimi mesti."</string> @@ -626,6 +634,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dotaknite se, če želite izklopiti zvok. V storitvah za ljudi s posebnimi potrebami bo morda izklopljen zvok."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Dotaknite se, če želite nastaviti vibriranje."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Dotaknite se, če želite izklopiti zvok."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izklop zvoka"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vklop zvoka"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string> @@ -739,7 +749,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Sistem je to obvestilo samodejno <b>uvrstil nižje – med obvestila brez zvoka</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"To obvestilo je bilo samodejno <b>uvrščeno višje</b> na zaslonu z obvestili."</string> <string name="feedback_demoted" msgid="951884763467110604">"To obvestilo je bilo samodejno <b>uvrščeno nižje</b> na zaslonu z obvestili."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Je bilo to prav?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Posredujte povratne informacije razvijalcu. Je bilo to prav?"</string> <string name="feedback_response" msgid="4671729244976641339">"Hvala za povratne informacije."</string> <string name="feedback_ok" msgid="6481426753298857144">"V redu"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kontrolniki obvestil za aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g> so odprti"</string> @@ -890,6 +900,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premik na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Ploščica je bila dodana"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Ploščica je bila odstranjena"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Urejevalnik hitrih nastavitev."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obvestilo za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Odpri nastavitve."</string> @@ -980,7 +992,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> uporablja: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Aplikacija <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> je nedavno uporabila: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(za podjetja)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonski klici"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonski klic"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(prek aplikacije <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"fotoaparat"</string> <string name="privacy_type_location" msgid="7991481648444066703">"lokacijo"</string> @@ -988,7 +1000,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Izklop za tipala"</string> <string name="device_services" msgid="1549944177856658705">"Storitve naprave"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Brez naslova"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Dotaknite se za vnovični zagon te aplikacije in preklop v celozaslonski način."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Premakni"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Krmarjenje po sistemu je posodobljeno. Če želite opraviti spremembe, odprite nastavitve."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Če želite posodobiti krmarjenje po sistemu, odprite nastavitve"</string> @@ -1044,6 +1055,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Dodaj"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Predlagala aplikacija <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Kontrolniki so posodobljeni"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Naprava je zaklenjena"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Koda PIN vsebuje črke ali simbole"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Preverjanje naprave <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Napačna koda PIN"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index fede55cd86d8..9f4c1e183e79 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Lëviz në pamjen e ekranit"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Hiq pamjen e ekranit"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pamja paraprake e imazhit"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Kufiri i sipërm"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Kufiri i poshtëm"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Regjistruesi i ekranit"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Regjistrimi i ekranit po përpunohet"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Njoftim i vazhdueshëm për një seancë regjistrimi të ekranit"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Deri në lindje të diellit"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktiv në <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Deri në <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC është çaktivizuar"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC është aktivizuar"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Shkyçe për të përdorur NFC-në"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Kjo pajisje i përket organizatës sate"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Kjo pajisje i përket <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Rrëshqit për të hapur telefonin"</string> <string name="voice_hint" msgid="7476017460191291417">"Rrëshqit për të hapur ndihmën zanore"</string> <string name="camera_hint" msgid="4519495795000658637">"Rrëshqit për të hapur kamerën"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Shfaq profilin"</string> <string name="user_add_user" msgid="4336657383006913022">"Shto përdorues"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Përdorues i ri"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Dëshiron t\'i japësh fund sesionit të vizitorit?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Jepi fund sesionit të vizitorit"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Të hiqet i ftuari?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Jepi fund sesionit"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hiq"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Mirë se erdhe, i ftuar!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Dëshiron ta vazhdosh sesionin tënd?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Fillo nga e para"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizata jote instaloi një autoritet certifikate në profilin tënd të punës. Trafiku i rrjetit tënd të sigurt mund të monitorohet ose modifikohet."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Në këtë pajisje është instaluar një autoritet certifikate. Trafiku i rrjetit tënd të sigurt mund të monitorohet ose modifikohet."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratori ka aktivizuar regjistrimin e rrjetit, i cili monitoron trafikun në pajisjen tënde."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratori yt ka aktivizuar regjistrimin e rrjetit, i cili monitoron trafikun në profilin tënd të punës, por jo në profilin tënd personal."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Je lidhur me aplikacionin <xliff:g id="VPN_APP">%1$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd në rrjet, duke përfshirë mail-et, aplikacionet dhe sajtet e uebit."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Je lidhur me aplikacionet <xliff:g id="VPN_APP_0">%1$s</xliff:g> dhe <xliff:g id="VPN_APP_1">%2$s</xliff:g>, të cilat mund të monitorojnë aktivitetin tënd në rrjet, duke përfshirë mail-et, aplikacionet dhe sajtet e uebit."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Profili yt i punës është i lidhur me <xliff:g id="VPN_APP">%1$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd në rrjet, duke përfshirë mail-et, aplikacionet dhe sajtet e uebit."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Trokit për të çaktivizuar. Shërbimet e qasshmërisë mund të çaktivizohen."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Trokit për ta vendosur në dridhje."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Trokit për ta çaktivizuar."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"çaktivizo audion"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktivizo audion"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"lësho dridhje"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Ky njoftim është <b>ulur automatikisht në nivel si në heshtje</b> nga sistemi."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Ky njoftim është <b>renditur automatikisht më lart</b> në strehën e njoftimeve."</string> <string name="feedback_demoted" msgid="951884763467110604">"Ky njoftim është <b>renditur automatikisht më poshtë</b> në strehën e njoftimeve."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"A ishte e saktë kjo?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Tregoji zhvilluesit komentet e tua. A ishte e saktë kjo?"</string> <string name="feedback_response" msgid="4671729244976641339">"Faleminderit për komentin!"</string> <string name="feedback_ok" msgid="6481426753298857144">"Në rregull"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Kontrollet e njoftimeve për <xliff:g id="APP_NAME">%1$s</xliff:g> janë hapur"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Zhvendos te <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Shto te pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Pllakëza u shtua"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Pllakëza u hoq"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redaktori i cilësimeve të shpejta."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Njoftim nga <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Hap cilësimet."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> po përdor <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ka përdorur <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> së fundi"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ndërmarrje)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Phonecall"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonata"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(nëpërmjet <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamerën"</string> <string name="privacy_type_location" msgid="7991481648444066703">"vendndodhjen"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensorët joaktivë"</string> <string name="device_services" msgid="1549944177856658705">"Shërbimet e pajisjes"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Pa titull"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Trokit për ta rinisur këtë aplikacion dhe për të kaluar në ekranin e plotë."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Zhvendos"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Navigimi i sistemit u përditësua. Për të bërë ndryshime, shko te \"Cilësimet\"."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Shko te \"Cilësimet\" për të përditësuar navigimin e sistemit"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Shto"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Sugjeruar nga <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Kontrollet u përditësuan"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Pajisja është e kyçur"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Kodi PIN përmban shkronja ose simbole"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verifiko <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Kod PIN i gabuar"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 0d822464611a..5c2fbac29e2d 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Померајте снимак екрана"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Одбаците снимак екрана"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед снимка екрана"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Горња граница"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Доња граница"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Снимач екрана"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обрађујемо видео снимка екрана"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Обавештење о сесији снимања екрана је активно"</string> @@ -412,6 +414,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До изласка сунца"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Укључује се у <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC је онемогућен"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC је омогућен"</string> @@ -445,6 +449,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Откључајте да бисте користили NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Овај уређај припада организацији"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Овај уређај припада организацији <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Превуците од иконе за телефон"</string> <string name="voice_hint" msgid="7476017460191291417">"Превуците од иконе за гласовну помоћ"</string> <string name="camera_hint" msgid="4519495795000658637">"Превуците од иконе за камеру"</string> @@ -465,9 +471,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Прикажи профил"</string> <string name="user_add_user" msgid="4336657383006913022">"Додај корисника"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нови корисник"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Желите да завршите сесију госта?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Заврши сесију госта"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Желите ли да уклоните госта?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Заврши сесију"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Уклони"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добро дошли назад, госте!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Желите ли да наставите сесију?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни из почетка"</string> @@ -543,6 +550,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Организација је на пословном профилу инсталирала ауторитет за издавање сертификата. Безбедни мрежни саобраћај може да се прати или мења."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На овом уређају је инсталиран ауторитет за издавање сертификата. Безбедни мрежни саобраћај може да се прати или мења."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Администратор је укључио евидентирање мреже, које прати саобраћај на уређају."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Администратор је укључио евидентирање мреже, које прати саобраћај на пословном профилу, али не и на личном профилу."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Повезани сте са апликацијом <xliff:g id="VPN_APP">%1$s</xliff:g>, која може да надгледа активности на мрежи, укључујући имејлове, апликације и веб-сајтове."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Повезани сте са апликацијама <xliff:g id="VPN_APP_0">%1$s</xliff:g> и <xliff:g id="VPN_APP_1">%2$s</xliff:g>, које могу да надгледају активности на мрежи, укључујући имејлове, апликације и веб-сајтове."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Пословни профил је повезан са апликацијом <xliff:g id="VPN_APP">%1$s</xliff:g>, која може да надгледа активности на мрежи, укључујући имејлове, апликације и веб-сајтове."</string> @@ -623,6 +631,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Додирните да бисте искључили звук. Звук услуга приступачности ће можда бити искључен."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Додирните да бисте подесили на вибрацију."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Додирните да бисте искључили звук."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"искључите звук"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"укључите звук"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрација"</string> @@ -736,7 +746,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Систем је ово обавештење аутоматски <b>деградирао у Нечујно</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Ово обавештење је аутоматски <b>рангирано више</b> на траци са обавештењима."</string> <string name="feedback_demoted" msgid="951884763467110604">"Ово обавештење је аутоматски <b>рангирано ниже</b> на траци са обавештењима."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Да ли је то тачно?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Пошаљите програмеру повратне информације. Да ли је то тачно?"</string> <string name="feedback_response" msgid="4671729244976641339">"Хвала вам на повратним информацијама!"</string> <string name="feedback_ok" msgid="6481426753298857144">"Потврди"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Контроле обавештења за отварање апликације <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -885,6 +895,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместите на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додајте на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. позиција"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Плочица је додата"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Плочица је уклоњена"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уређивач за Брза подешавања."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Обавештења за <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отвори Подешавања."</string> @@ -975,7 +987,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Апликација <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> користи: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Апликација <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> је недавно користила: <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(за предузећа)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Телефонски позив"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефонски позив"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(преко: <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"камеру"</string> <string name="privacy_type_location" msgid="7991481648444066703">"локацију"</string> @@ -983,7 +995,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Сензори су искључени"</string> <string name="device_services" msgid="1549944177856658705">"Услуге за уређаје"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Без наслова"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Додирните да бисте рестартовали апликацију и прешли у режим целог екрана."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Премести"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Навигација система је ажурирана. Да бисте унели измене, идите у Подешавања."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Идите у Подешавања да бисте ажурирали навигацију система"</string> @@ -1038,6 +1049,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Додај"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Предлаже <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Контроле су ажуриране"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Уређај је закључан"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN садржи слова или симболе"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Верификујте: <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Погрешан PIN"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 969bdcd7eb17..5ca41ed1c7d7 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Rullande skärmbild"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Stäng skärmbild"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Förhandsgranskning av skärmbild"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Övre gräns"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Nedre gräns"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Skärminspelare"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandlar skärminspelning"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Avisering om att skärminspelning pågår"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Till soluppgången"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Aktivera kl. <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Till <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC är inaktiverat"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC är aktiverat"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Lås upp om du vill använda NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Den här enheten tillhör organisationen"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Den här enheten tillhör <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Svep från ikonen och öppna telefonen"</string> <string name="voice_hint" msgid="7476017460191291417">"Svep från ikonen och öppna röstassistenten"</string> <string name="camera_hint" msgid="4519495795000658637">"Svep från ikonen och öppna kameran"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Visa profil"</string> <string name="user_add_user" msgid="4336657383006913022">"Lägg till användare"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny användare"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Vill du avsluta gästsessionen?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Avsluta gästsession"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vill du ta bort gästen?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Avsluta session"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ta bort"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Välkommen tillbaka gäst!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vill du fortsätta sessionen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Börja om"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organisationen har installerat en certifikatutfärdare i jobbprofilen. Din säkra nätverkstrafik kan övervakas och ändras."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"En certifikatutfärdare är installerad på enheten. Din säkra nätverkstrafik kan övervakas och ändras."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratören har aktiverat nätverksloggning som övervakar trafik på enheten."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratören har aktiverat nätverksloggning som övervakar trafik i jobbprofilen men inte den privata profilen."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Du är ansluten till <xliff:g id="VPN_APP">%1$s</xliff:g> som kan övervaka din nätverksaktivitet, inklusive e-postmeddelanden, appar och webbplatser."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Du är ansluten till <xliff:g id="VPN_APP_0">%1$s</xliff:g> och <xliff:g id="VPN_APP_1">%2$s</xliff:g> som kan övervaka din nätverksaktivitet, inklusive e-postmeddelanden, appar och webbplatser."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Jobbprofilen är ansluten till <xliff:g id="VPN_APP">%1$s</xliff:g> som kan övervaka din nätverksaktivitet, exempelvis e-post, appar och webbplatser."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tryck här om du vill stänga av ljudet. Tillgänglighetstjänsterna kanske inaktiveras."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tryck här om du vill aktivera vibrationsläget."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Tryck här om du vill stänga av ljudet."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"stänga av ljudet"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på ljudet"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Aviseringens relevans <b>ändrades till Tyst</b> automatiskt av systemet."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Aviseringens relevans i meddelandepanelen <b>höjdes</b> automatiskt."</string> <string name="feedback_demoted" msgid="951884763467110604">"Aviseringens relevans i meddelandepanelen <b>sänktes</b> automatiskt."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Stämmer detta?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Ge utvecklaren din feedback. Stämmer detta?"</string> <string name="feedback_response" msgid="4671729244976641339">"Tack för din feedback!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Aviseringsinställningarna för <xliff:g id="APP_NAME">%1$s</xliff:g> är öppna"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytta till <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lägg till på position <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kortet har lagts till"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kortet har tagits bort"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigerare för snabbinställningar."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-avisering: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Öppna inställningarna."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> använder <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> använde <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> nyligen"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(företag)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefonsamtal"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefonsamtal"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(genom <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"plats"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensorer har inaktiverats"</string> <string name="device_services" msgid="1549944177856658705">"Enhetstjänster"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Ingen titel"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Tryck för att starta om appen i helskärmsläge."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Flytta"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Systemnavigeringen har uppdaterats. Öppna inställningarna om du vill ändra något."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Öppna inställningarna och uppdatera systemnavigeringen"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Lägg till"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Förslag från <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Snabbkontroller uppdaterade"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Enheten är låst"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Pinkoden innehåller bokstäver eller symboler"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Verifiera <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Fel pinkod"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 4055e8da981d..768e10ccf881 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Fanya picha ya skrini iwe ndefu"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ondoa picha ya skrini"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Onyesho la kukagua picha ya skrini"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Mpaka wa sehemu ya juu"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Mpaka wa sehemu ya chini"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Kinasa Skrini"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Inachakata rekodi ya skrini"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Arifa inayoendelea ya kipindi cha kurekodi skrini"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hadi macheo"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Itawashwa saa <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hadi saa <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC imezimwa"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC imewashwa"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Fungua ili utumie NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Kifaa hiki kinamilikiwa na shirika lako"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Kifaa hiki kinamilikiwa na <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Telezesha kidole kutoka kwa aikoni ili ufikie simu"</string> <string name="voice_hint" msgid="7476017460191291417">"Telezesha kidole kutoka aikoni ili upate mapendekezo ya sauti"</string> <string name="camera_hint" msgid="4519495795000658637">"Telezesha kidole kutoka aikoni ili ufikie kamera"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Onyesha wasifu"</string> <string name="user_add_user" msgid="4336657383006913022">"Ongeza mtumiaji"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Mtumiaji mpya"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ungependa kumaliza kipindi cha mgeni?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Maliza kipindi cha mgeni"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ungependa kumwondoa mgeni?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Maliza kipindi"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ondoa"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Karibu tena, mwalikwa!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Je, unataka kuendelea na kipindi chako?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Anza tena"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Shirika lako limesakinisha mamlaka ya cheti katika wasifu wako wa kazini. Huenda shughuli kwenye mtandao wako salama zikafuatiliwa au kubadilishwa."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Mamlaka ya cheti imesakinishwa kwenye kifaa hiki. Huenda shughuli kwenye mtandao wako salama zikafuatiliwa au kubadilishwa."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Msimamizi wako amewasha kumbukumbu ya kuingia mtandaoni, ambayo hufuatilia shughuli kwenye kifaa chako."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Msimamizi wako amewasha kumbukumbu ya kuingia mtandaoni ambayo hufuatilia shughuli kwenye wasifu wako wa kazini ila si kwenye wasifu wako wa binafsi."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Umeunganishwa kwenye <xliff:g id="VPN_APP">%1$s</xliff:g>, ambayo inaweza kufuatilia shughuli za mtandao wako, ikiwa ni pamoja na barua pepe, programu na tovuti."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Umeunganishwa kwenye <xliff:g id="VPN_APP_0">%1$s</xliff:g> na <xliff:g id="VPN_APP_1">%2$s</xliff:g>, ambazo zinaweza kufuatilia shughuli za mtandao wako, ikiwa ni pamoja na barua pepe, programu na tovuti."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Wasifu wako wa kazini umeunganishwa kwenye <xliff:g id="VPN_APP">%1$s</xliff:g>, ambayo inaweza kufuatilia shughuli za mtandao wako, ikiwa ni pamoja na barua pepe, programu na tovuti."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Gusa ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Gusa ili uweke mtetemo."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Gusa ili usitishe."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"zima sauti"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"washa sauti"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tetema"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Arifa hii <b>imeshushwa hadhi kiotomatiki na mfumo kuwa Kimya</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Arifa hii <b>imeorodheshwa kiotomatiki katika nafasi ya juu</b> katika kiwango chako."</string> <string name="feedback_demoted" msgid="951884763467110604">"Arifa hii <b>imeorodheshwa kiotomatiki katika nafasi ya chini</b> katika kiwango chako."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Je, hatua hii ilikuwa sahihi?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Mpe msanidi programu maoni yako. Je, hatua hii ilikuwa sahihi?"</string> <string name="feedback_response" msgid="4671729244976641339">"Asante kwa maoni yako!"</string> <string name="feedback_ok" msgid="6481426753298857144">"Sawa"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Vidhibiti vya arifa <xliff:g id="APP_NAME">%1$s</xliff:g> vimefunguliwa"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hamishia kwenye <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ongeza kwenye nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kigae kimewekwa"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Kigae kimeondolewa"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kihariri cha Mipangilio ya haraka."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Arifa kutoka <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Fungua mipangilio."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> inatumia <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ilitumia <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> hivi majuzi"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(biashara)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Simu"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Simu"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(kupitia <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"mahali"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Umezima vitambuzi"</string> <string name="device_services" msgid="1549944177856658705">"Huduma za Kifaa"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Wimbo hauna jina"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Gusa ili uzime na uwashe upya programu hii kisha nenda kwenye skrini nzima."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Sogeza"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Umesasisha usogezaji kwenye mfumo. Ili ubadilishe, nenda kwenye Mipangilio."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Nenda kwenye mipangilio ili usasishe usogezaji kwenye mfumo"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Weka"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Kimependekezwa na <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Umesasisha vidhibiti"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Kifaa kimefungwa"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN ina herufi au alama"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Thibitisha <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Nambari ya PIN si sahihi"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index cbac87e6390a..2ea5a6a35c2f 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"ஸ்கிரீன்ஷாட்டைப் பெரிதாக்கும்"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ஸ்கிரீன்ஷாட்டை நிராகரிக்கும்"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ஸ்கிரீன்ஷாட்டின் மாதிரிக்காட்சி"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"மேற்புற எல்லை"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"கீழ்ப்புற எல்லை"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ஸ்கிரீன் ரெக்கார்டர்"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ஸ்க்ரீன் ரெக்கார்டிங் செயலாக்கப்படுகிறது"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"திரை ரெக்கார்டிங் அமர்விற்கான தொடர் அறிவிப்பு"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"காலை வரை"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>க்கு ஆன் செய்"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> வரை"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC முடக்கப்பட்டது"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC இயக்கப்பட்டது"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCயைப் பயன்படுத்த அன்லாக் செய்யவும்"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"இந்த சாதனம் உங்கள் நிறுவனத்துக்கு சொந்தமானது"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"இந்த சாதனம் <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> நிறுவனத்துக்கு சொந்தமானது"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ஃபோனிற்கு ஐகானிலிருந்து ஸ்வைப் செய்யவும்"</string> <string name="voice_hint" msgid="7476017460191291417">"குரல் உதவிக்கு ஐகானிலிருந்து ஸ்வைப் செய்யவும்"</string> <string name="camera_hint" msgid="4519495795000658637">"கேமராவிற்கு ஐகானிலிருந்து ஸ்வைப் செய்யவும்"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"சுயவிவரத்தைக் காட்டு"</string> <string name="user_add_user" msgid="4336657383006913022">"பயனரைச் சேர்"</string> <string name="user_new_user_name" msgid="2019166282704195789">"புதியவர்"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"விருந்தினர் அமர்வை நிறைவுசெய்யவா?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"விருந்தினர் அமர்வை நிறைவுசெய்"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"கெஸ்ட்டை அகற்றவா?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா பயன்பாடுகளும், தரவும் நீக்கப்படும்."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"அமர்வை நிறைவுசெய்"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"அகற்று"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"நல்வரவு!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"உங்கள் அமர்வைத் தொடர விருப்பமா?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"மீண்டும் தொடங்கு"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"உங்கள் நிறுவனம், பணிக் கணக்கில் சான்றிதழ் அங்கீகாரத்தை நிறுவியுள்ளது. உங்களின் பாதுகாப்பான நெட்வொர்க் ட்ராஃபிக் கண்காணிக்கப்படலாம் அல்லது மாற்றப்படலாம்."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"இந்தச் சாதனத்தில் சான்றிதழ் அங்கீகாரம் நிறுவப்பட்டுள்ளது. உங்களின் பாதுகாப்பான நெட்வொர்க் ட்ராஃபிக் கண்காணிக்கப்படலாம் அல்லது மாற்றப்படலாம்."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"உங்கள் நிர்வாகி, நெட்வொர்க் பதிவெடுத்தலை இயக்கியுள்ளார். இது சாதனத்தில் ட்ராஃபிக்கைக் கண்காணிக்கும்."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"உங்கள் நிர்வாகி \'நெட்வொர்க் பதிவெடுத்தலை\' இயக்கியுள்ளார், இது உங்கள் பணிக் கணக்கில் டிராஃபிக்கைக் கண்காணிக்கும். ஆனால் தனிப்பட்ட கணக்கில் கண்காணிக்காது."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள்."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP_0">%1$s</xliff:g> மற்றும் <xliff:g id="VPN_APP_1">%2$s</xliff:g> உடன் இணைக்கப்பட்டுள்ளீர்கள்."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"மின்னஞ்சல்கள், ஆப்ஸ், இணையதளங்கள் உட்பட உங்கள் நெட்வொர்க் செயல்பாட்டைக் கண்காணிக்கக்கூடிய <xliff:g id="VPN_APP">%1$s</xliff:g> உடன் உங்கள் பணிக் கணக்கு இணைக்கப்பட்டுள்ளது."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ஒலியடக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. அதிர்விற்கு அமைக்க, தட்டவும்."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. ஒலியடக்க, தட்டவும்."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ஒலியடக்கும்"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ஒலி இயக்கும்"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"அதிர்வுறும்"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"சிஸ்டத்தால் தானாகவே இந்த அறிவிப்பு <b>நிசப்த நிலைக்குக் குறைத்து அமைக்கப்பட்டது</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"அறிவிப்பு விவரத்தில் தானாகவே இந்த அறிவிப்பின் <b>முக்கியத்துவம் உயர்த்தப்பட்டது</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"அறிவிப்பு விவரத்தில் தானாகவே இந்த அறிவிப்பின் <b>முக்கியத்துவம் குறைக்கப்பட்டது</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"இது சரியானதா?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"உங்கள் கருத்தை டெவெலப்பருக்குத் தெரியப்படுத்துங்கள். இது சரியானதா?"</string> <string name="feedback_response" msgid="4671729244976641339">"உங்கள் கருத்துக்கு நன்றி!"</string> <string name="feedback_ok" msgid="6481426753298857144">"சரி"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g>க்கான அறிவிப்புக் கட்டுப்பாடுகள் திறக்கப்பட்டன"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>க்கு நகர்த்தும்"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>ல் சேர்க்கும்"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"இடம்: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"கட்டம் சேர்க்கப்பட்டது"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"கட்டம் அகற்றப்பட்டது"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"விரைவு அமைப்புகள் திருத்தி."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> அறிவிப்பு: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"அமைப்புகளைத் திற."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ஆப்ஸ் <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> பயன்படுத்துகிறது"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"சமீபத்தில் <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ஆப்ஸ் <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> பயன்படுத்தியுள்ளது"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(நிறுவனப் பதிப்பு)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"மொபைல் அழைப்பு"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"மொபைல் அழைப்பு"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> மூலம்)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"கேமரா"</string> <string name="privacy_type_location" msgid="7991481648444066703">"இருப்பிடம்"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"சென்சார்களை ஆஃப் செய்தல்"</string> <string name="device_services" msgid="1549944177856658705">"சாதன சேவைகள்"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"தலைப்பு இல்லை"</string> - <string name="restart_button_description" msgid="6916116576177456480">"தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கலாம், முழுத்திரையில் பார்க்கலாம்."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"நகர்த்து"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"சிஸ்டம் நேவிகேஷன் மாற்றப்பட்டது. மாற்றங்களைச் செய்ய ‘அமைப்புகளுக்குச்’ செல்லவும்."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"சிஸ்டம் நேவிகேஷனை மாற்ற ’அமைப்புகளுக்குச்’ செல்லவும்"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"சேர்"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ஆப்ஸால் பரிந்துரைக்கப்பட்டது"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"கட்டுப்பாடுகள் மாற்றப்பட்டன"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"சாதனம் பூட்டப்பட்டது"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"பின்னில் எழுத்துகள் அல்லது குறிகள் உள்ளன"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> ஐச் சரிபார்த்தல்"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"தவறான பின்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 45ef33900ccd..03af2a0e3725 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"స్క్రీన్షాట్కు స్క్రోల్ చేయండి"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"స్క్రీన్షాట్ను విస్మరించు"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"స్క్రీన్షాట్ ప్రివ్యూ"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"ఎగువ సరిహద్దు"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"దిగువ సరిహద్దు"</string> <string name="screenrecord_name" msgid="2596401223859996572">"స్క్రీన్ రికార్డర్"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"స్క్రీన్ రికార్డింగ్ అవుతోంది"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"స్క్రీన్ రికార్డ్ సెషన్ కోసం ఆన్గోయింగ్ నోటిఫికేషన్"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"సూర్యోదయం వరకు"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> కు ఆన్ అవుతుంది"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> వరకు"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC నిలిపివేయబడింది"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ప్రారంభించబడింది"</string> @@ -440,9 +444,11 @@ <string name="notification_tap_again" msgid="4477318164947497249">"తెరవడానికి మళ్లీ నొక్కండి"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"తెరవడానికి, పైకి స్వైప్ చేయండి"</string> <string name="keyguard_retry" msgid="886802522584053523">"మళ్ళీ ప్రయత్నించడానికి పైకి స్వైప్ చేయండి"</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCను ఉపయోగించడానికి అన్లాక్ చేయండి"</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCని ఉపయోగించడానికి అన్లాక్ చేయండి"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"ఈ పరికరం మీ సంస్థకు చెందినది"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>కు చెందినది"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"ఫోన్ కోసం చిహ్నాన్ని స్వైప్ చేయండి"</string> <string name="voice_hint" msgid="7476017460191291417">"వాయిస్ అసిస్టెంట్ చిహ్నం నుండి స్వైప్"</string> <string name="camera_hint" msgid="4519495795000658637">"కెమెరా కోసం చిహ్నాన్ని స్వైప్ చేయండి"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ప్రొఫైల్ని చూపు"</string> <string name="user_add_user" msgid="4336657383006913022">"వినియోగదారుని జోడించండి"</string> <string name="user_new_user_name" msgid="2019166282704195789">"కొత్త వినియోగదారు"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"గెస్ట్ సెషన్ను ముగించాలా?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"గెస్ట్ సెషన్ను ముగించు"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"అతిథిని తీసివేయాలా?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్లోని అన్ని యాప్లు మరియు డేటా తొలగించబడతాయి."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"సెషన్ను ముగించు"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"తీసివేయి"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"పునఃస్వాగతం, అతిథి!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"మీరు మీ సెషన్ని కొనసాగించాలనుకుంటున్నారా?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించు"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"మీ కార్యాలయ ప్రొఫైల్లో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్స్టాల్ చేసింది. మీ సురక్షిత నెట్వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ఈ పరికరంలో ప్రమాణపత్ర అధికారం ఇన్స్టాల్ చేయబడింది. మీ సురక్షిత నెట్వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్ని పర్యవేక్షించగల నెట్వర్క్ లాగింగ్ని ఆన్ చేసారు."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"మీ అడ్మిన్ నెట్వర్క్ లాగింగ్ను ఆన్ చేశారు, ఇది మీ వర్క్ ప్రొఫైల్లోని ట్రాఫిక్ను పర్యవేక్షిస్తుంది కానీ మీ వ్యక్తిగత ప్రొఫైల్లో కాదు."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్లు, యాప్లు మరియు వెబ్సైట్లతో సహా మీ నెట్వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"మీరు ఇమెయిల్లు, యాప్లు మరియు వెబ్సైట్లతో సహా మీ నెట్వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP_0">%1$s</xliff:g> మరియు <xliff:g id="VPN_APP_1">%2$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"మీ కార్యాలయ ప్రొఫైల్ ఇమెయిల్లు, యాప్లు మరియు వెబ్సైట్లతో సహా మీ నెట్వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. మ్యూట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. వైబ్రేట్ అయ్యేలా సెట్ చేయడం కోసం నొక్కండి."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. మ్యూట్ చేయడానికి నొక్కండి."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"మ్యూట్ చేయి"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"అన్మ్యూట్ చేయి"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"వైబ్రేట్"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ఈ నోటిఫికేషన్, సిస్టమ్ ద్వారా ఆటోమేటిక్గా <b>నిశ్శబ్దం స్థాయికి తగ్గించబడింది</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"మీ నోటిఫికేషన్ షేడ్లో ఈ నోటిఫికేషన్ ఆటోమేటిక్గా <b>ఎక్కువ ర్యాంక్</b>కు సర్దుబాటు చేయబడింది."</string> <string name="feedback_demoted" msgid="951884763467110604">"మీ నోటిఫికేషన్ షేడ్లో ఈ నోటిఫికేషన్ ఆటోమేటిక్గా <b>తక్కువ ర్యాంక్</b>కు సర్దుబాటు చేయబడింది."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"ఇది సరైనదేనా?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"మీ ఫీడ్బ్యాక్ను డెవలపర్కు తెలియజేయండి. ఇది సరైనదేనా?"</string> <string name="feedback_response" msgid="4671729244976641339">"మీ ఫీడ్బ్యాక్ను అందించినందుకు ధన్యవాదాలు!"</string> <string name="feedback_ok" msgid="6481426753298857144">"సరే"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> యొక్క నోటిఫికేషన్ నియంత్రణలు తెరవబడ్డాయి"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>కు తరలించండి"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> స్థానానికి జోడించండి"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"స్థానం <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"టైల్ జోడించబడింది"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"టైల్ తీసివేయబడింది"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"శీఘ్ర సెట్టింగ్ల ఎడిటర్."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> నోటిఫికేషన్: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"సెట్టింగ్లను తెరవండి."</string> @@ -967,23 +979,17 @@ <string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"అప్లికేషన్లు మీ <xliff:g id="TYPES_LIST">%s</xliff:g>ని ఉపయోగిస్తున్నాయి."</string> <string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string> <string name="ongoing_privacy_dialog_last_separator" msgid="5615876114268009767">" మరియు "</string> - <!-- no translation found for ongoing_privacy_dialog_using_op (4125175620929701569) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_recent_op (4255923947334262404) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_enterprise (4082735415905550729) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_phonecall (3526223335298089311) --> - <skip /> - <!-- no translation found for ongoing_privacy_dialog_attribution_text (9186683306719924646) --> - <skip /> + <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>ను ఉపయోగిస్తోంది"</string> + <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, ఇటీవల <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>ను ఉపయోగించింది"</string> + <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ఎంటర్ప్రైజ్)"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"ఫోన్ కాల్"</string> + <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> ద్వారా)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"కెమెరా"</string> <string name="privacy_type_location" msgid="7991481648444066703">"లొకేషన్"</string> <string name="privacy_type_microphone" msgid="9136763906797732428">"మైక్రోఫోన్"</string> <string name="sensor_privacy_mode" msgid="4462866919026513692">"సెన్సార్లు ఆఫ్"</string> <string name="device_services" msgid="1549944177856658705">"పరికర సేవలు"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"శీర్షిక లేదు"</string> - <string name="restart_button_description" msgid="6916116576177456480">"ఈ యాప్ను పునఃప్రారంభించేలా నొక్కి, ఆపై పూర్తి స్క్రీన్లోకి వెళ్లండి."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"తరలించు"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"సిస్టమ్ నావిగేషన్ అప్డేట్ చేయబడింది. మార్పులు చేయడానికి, సెట్టింగ్లకు వెళ్లండి."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"సిస్టమ్ నావిగేషన్ను అప్డేట్ చేయడానికి సెట్టింగ్లకు వెళ్లండి"</string> @@ -1037,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"జోడించండి"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> ద్వారా సూచించబడింది"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"నియంత్రణలు అప్డేట్ అయ్యాయి"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"పరికరంలాక్ చేయబడింది"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"పిన్ అక్షరాలను లేదా చిహ్నాలను కలిగి ఉంది"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g>ను వెరిఫై చేయండి"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"పిన్ తప్పు"</string> diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 722f1480463d..b02d8b8cf7e4 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -28,6 +28,7 @@ <string-array name="config_systemUIServiceComponents" translatable="false"> <item>com.android.systemui.util.NotificationChannels</item> <item>com.android.systemui.volume.VolumeUI</item> + <item>com.android.systemui.privacy.television.TvOngoingPrivacyChip</item> <item>com.android.systemui.statusbar.tv.TvStatusBar</item> <item>com.android.systemui.statusbar.tv.notifications.TvNotificationPanel</item> <item>com.android.systemui.statusbar.tv.notifications.TvNotificationHandler</item> diff --git a/packages/SystemUI/res/values-television/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml index 6da0c693f389..7626db93dd76 100644 --- a/packages/SystemUI/res/values-television/dimens.xml +++ b/packages/SystemUI/res/values-television/dimens.xml @@ -17,4 +17,8 @@ <resources> <!-- Opacity at which the background for the shutdown UI will be drawn. --> <item name="shutdown_scrim_behind_alpha" format="float" type="dimen">1.0</item> + + <dimen name="privacy_chip_icon_margin">3dp</dimen> + <dimen name="privacy_chip_icon_padding">8dp</dimen> + <dimen name="privacy_chip_icon_size">13dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 8b197acb1958..5e37ed8a0fe8 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"เลื่อนจับภาพหน้าจอ"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ปิดภาพหน้าจอ"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ตัวอย่างภาพหน้าจอ"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"ขอบเขตด้านบน"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"ขอบเขตด้านล่าง"</string> <string name="screenrecord_name" msgid="2596401223859996572">"โปรแกรมบันทึกหน้าจอ"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"กำลังประมวลผลการอัดหน้าจอ"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการบันทึกหน้าจอ"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"จนพระอาทิตย์ขึ้น"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"เปิดเวลา <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"จนถึง <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC ถูกปิดใช้งาน"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"เปิดใช้งาน NFC แล้ว"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"ปลดล็อกเพื่อใช้ NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"องค์กรของคุณเป็นเจ้าของอุปกรณ์นี้"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g> เป็นเจ้าของอุปกรณ์นี้"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"เลื่อนไอคอนโทรศัพท์"</string> <string name="voice_hint" msgid="7476017460191291417">"เลื่อนไอคอนตัวช่วยเสียง"</string> <string name="camera_hint" msgid="4519495795000658637">"เลื่อนไอคอนกล้อง"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"แสดงโปรไฟล์"</string> <string name="user_add_user" msgid="4336657383006913022">"เพิ่มผู้ใช้"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ผู้ใช้ใหม่"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"จบเซสชันผู้เยี่ยมชมใช่ไหม"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"จบเซสชันผู้เยี่ยมชม"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ต้องการนำผู้เข้าร่วมออกไหม"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"จบเซสชัน"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"นำออก"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ยินดีต้อนรับท่านผู้เยี่ยมชมกลับมาอีกครั้ง!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"คุณต้องการอยู่ในเซสชันต่อไปไหม"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"เริ่มต้นใหม่"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"องค์กรของคุณติดตั้งผู้ออกใบรับรองในโปรไฟล์งาน อาจมีการตรวจสอบหรือแก้ไขการจราจรของข้อมูลในเครือข่ายที่ปลอดภัยของคุณ"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"มีการติดตั้งผู้ออกใบรับรองในอุปกรณ์นี้ อาจมีการตรวจสอบหรือแก้ไขการจราจรของข้อมูลในเครือข่ายที่ปลอดภัยของคุณ"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ผู้ดูแลระบบได้เปิดการบันทึกเครือข่าย ซึ่งจะตรวจสอบการจราจรของข้อมูลในอุปกรณ์ของคุณ"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"ผู้ดูแลระบบได้เปิดการบันทึกเครือข่าย ซึ่งจะตรวจสอบการรับส่งข้อมูลในโปรไฟล์งาน แต่ไม่ตรวจสอบในโปรไฟล์ส่วนตัวของคุณ"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"คุณเชื่อมต่ออยู่กับ <xliff:g id="VPN_APP">%1$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายของคุณ รวมถึงอีเมล แอป และเว็บไซต์"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"คุณเชื่อมต่ออยู่กับ <xliff:g id="VPN_APP_0">%1$s</xliff:g> และ <xliff:g id="VPN_APP_1">%2$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายของคุณ รวมถึงอีเมล แอป และเว็บไซต์"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"โปรไฟล์งานของคุณเชื่อมต่ออยู่กับ <xliff:g id="VPN_APP">%1$s</xliff:g> ซึ่งสามารถตรวจสอบกิจกรรมในเครือข่ายของคุณ รวมถึงอีเมล แอป และเว็บไซต์"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s แตะเพื่อปิดเสียง อาจมีการปิดเสียงบริการการเข้าถึง"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s แตะเพื่อตั้งค่าให้สั่น"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s แตะเพื่อปิดเสียง"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ปิดเสียง"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"เปิดเสียง"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"สั่น"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"ระบบ<b>ลดระดับการแจ้งเตือนนี้เป็นปิดเสียง</b>โดยอัตโนมัติ"</string> <string name="feedback_promoted" msgid="2125562787759780807">"การแจ้งเตือนนี้<b>มีอันดับสูงขึ้น</b>ในหน้าต่างแจ้งเตือนโดยอัตโนมัติ"</string> <string name="feedback_demoted" msgid="951884763467110604">"การแจ้งเตือนนี้<b>มีอันดับต่ำลง</b>ในหน้าต่างแจ้งเตือนโดยอัตโนมัติ"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"การดำเนินการนี้ถูกต้องไหม"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"บอกความคิดเห็นให้นักพัฒนาแอปทราบ ถูกต้องไหม"</string> <string name="feedback_response" msgid="4671729244976641339">"ขอบคุณที่แสดงความคิดเห็น"</string> <string name="feedback_ok" msgid="6481426753298857144">"ตกลง"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"ส่วนควบคุมการแจ้งเตือนของ <xliff:g id="APP_NAME">%1$s</xliff:g> เปิดอยู่"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ย้ายไปที่ <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"เพิ่มไปยังตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"เพิ่มชิ้นส่วนแล้ว"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"นำชิ้นส่วนออกแล้ว"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ตัวแก้ไขการตั้งค่าด่วน"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> การแจ้งเตือน: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"เปิดการตั้งค่า"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> กำลังใช้<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ใช้<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>เมื่อเร็วๆ นี้"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(องค์กร)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"โทรศัพท์"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"การโทร"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(ผ่านทาง <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"กล้องถ่ายรูป"</string> <string name="privacy_type_location" msgid="7991481648444066703">"ตำแหน่ง"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"ปิดเซ็นเซอร์"</string> <string name="device_services" msgid="1549944177856658705">"บริการของอุปกรณ์"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"ไม่มีชื่อ"</string> - <string name="restart_button_description" msgid="6916116576177456480">"แตะเพื่อรีสตาร์ทแอปนี้และแสดงแบบเต็มหน้าจอ"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"ย้าย"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"อัปเดตการไปยังส่วนต่างๆ ของระบบแล้ว หากต้องการเปลี่ยนแปลง ให้ไปที่การตั้งค่า"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"ไปที่การตั้งค่าเพื่ออัปเดตการไปยังส่วนต่างๆ ของระบบ"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"เพิ่ม"</string> <string name="controls_dialog_message" msgid="342066938390663844">"แนะนำโดย <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"อัปเดตตัวควบคุมแล้ว"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"อุปกรณ์ถูกล็อก"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN ประกอบด้วยตัวอักษรหรือสัญลักษณ์"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"ยืนยัน <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN ไม่ถูกต้อง"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index a4a768bdc959..18313e405f98 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"I-scroll ang screenshot"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"I-dismiss ang screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Preview ng screenshot"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Pinakamataas na limitasyon"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Pinakamababang limitasyon"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Recorder ng Screen"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pinoproseso screen recording"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Kasalukuyang notification para sa session ng pag-record ng screen"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Hanggang sunrise"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Ma-o-on nang <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Hanggang <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Naka-disable ang NFC"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Naka-enable ang NFC"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"I-unlock para magamit ang NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Pagmamay-ari ng iyong organisasyon ang device na ito"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Pagmamay-ari ng <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ang device na ito"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Mag-swipe mula sa icon para sa telepono"</string> <string name="voice_hint" msgid="7476017460191291417">"Mag-swipe mula sa icon para sa voice assist"</string> <string name="camera_hint" msgid="4519495795000658637">"Mag-swipe mula sa icon para sa camera"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Ipakita ang profile"</string> <string name="user_add_user" msgid="4336657383006913022">"Magdagdag ng user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Bagong user"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Tapusin ang session ng bisita?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Tapusin ang session ng bisita"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alisin ang bisita?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Tapusin ang session"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alisin"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Maligayang pagbabalik, bisita!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Gusto mo bang ipagpatuloy ang iyong session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Magsimulang muli"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Nag-install ang iyong organisasyon ng awtoridad sa certificate sa iyong profile sa trabaho. Maaaring subaybayan o baguhin ang iyong ligtas na trapiko sa network."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"May naka-install sa device na ito na isang awtoridad sa certificate. Maaaring subaybayan o baguhin ang iyong ligtas na trapiko sa network."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Na-on ng iyong admin ang pag-log sa network, na sumusubaybay sa trapiko sa device mo."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Na-on ng iyong admin ang pag-log sa network, na sumusubaybay sa trapiko sa profile mo sa trabaho pero hindi sa iyong personal na profile."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Nakakonekta ka sa <xliff:g id="VPN_APP">%1$s</xliff:g>, na maaaring sumubaybay sa iyong aktibidad sa network, kabilang ang mga email, app, at website."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Nakakonekta ka sa <xliff:g id="VPN_APP_0">%1$s</xliff:g> at <xliff:g id="VPN_APP_1">%2$s</xliff:g>, na maaaring sumubaybay sa iyong aktibidad sa network, kabilang ang mga email, app, at website."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Nakakonekta sa <xliff:g id="VPN_APP">%1$s</xliff:g> ang iyong profile sa trabaho, na maaaring sumubaybay sa aktibidad sa iyong network, kasama ang mga email, app, at website."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. I-tap upang i-mute. Maaaring i-mute ang mga serbisyo sa Accessibility."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. I-tap upang itakda na mag-vibrate."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. I-tap upang i-mute."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"i-mute"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"i-unmute"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"i-vibrate"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Awtomatikong <b>na-demote sa Naka-silent</b> ng system ang notification na ito."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Awtomatikong <b>na-rank nang mas mataas</b> ang notification na ito sa iyong shade."</string> <string name="feedback_demoted" msgid="951884763467110604">"Awtomatikong <b>na-rank nang mas mababa</b> ang notification na ito sa iyong shade."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Tama ba ito?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Ipaalam sa developer ang iyong feedback. Tama ba ito?"</string> <string name="feedback_response" msgid="4671729244976641339">"Salamat sa iyong feedback!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Binuksan ang mga kontrol sa notification para sa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Ilipat sa <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Idagdag sa posisyong <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisyon <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Idinagdag ang tile"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Inalis ang tile"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor ng Mga mabilisang setting."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification sa <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buksan ang mga setting."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Ginagamit ng <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ang <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Kamakailang ginamit ng <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> ang <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(enterprise)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Tawag sa telepono"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Tawag sa telepono"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(sa pamamagitan ng <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"camera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"lokasyon"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Naka-off ang mga sensor"</string> <string name="device_services" msgid="1549944177856658705">"Mga Serbisyo ng Device"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Walang pamagat"</string> - <string name="restart_button_description" msgid="6916116576177456480">"I-tap para i-restart ang app na ito at mag-full screen."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Ilipat"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Na-update na ang pag-navigate ng system. Para gumawa ng mga pagbabago, pumunta sa Mga Setting."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Pumunta sa Mga Setting para i-update ang pag-navigate sa system"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Idagdag"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Iminungkahi ng <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Na-update na ang mga kontrol"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Naka-lock ang device"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"May mga titik o simbolo ang PIN"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"I-verify ang <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Maling PIN"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 3040ea0a2c3d..6b3015c899c4 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Kayan ekran görüntüsü"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ekran görüntüsünü kapat"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran görüntüsü önizlemesi"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Üst sınır"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Alt sınır"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Ekran Kaydedicisi"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran kaydı işleniyor"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekran kaydı oturumu için devam eden bildirim"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Sabaha kadar"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Açılacağı saat: <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Şu saate kadar: <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC devre dışı"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC etkin"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC\'yi kullanmak için kilidi açın"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Bu cihaz, kuruluşunuza ait"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Bu cihaz <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> adlı kuruluşa ait"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Telefon için, simgeden hızlıca kaydırın"</string> <string name="voice_hint" msgid="7476017460191291417">"Sesli yardım için, simgeden hızlıca kaydırın"</string> <string name="camera_hint" msgid="4519495795000658637">"Kamera için, simgeden hızlıca kaydırın"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profili göster"</string> <string name="user_add_user" msgid="4336657383006913022">"Kullanıcı ekle"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yeni kullanıcı"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Misafir oturumu sonlandırılsın mı?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Misafir oturumunu sonlandır"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Misafir oturumu kaldırılsın mı?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Oturumu sonlandır"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kaldır"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tekrar hoş geldiniz sayın misafir!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Oturumunuza devam etmek istiyor musunuz?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Baştan başla"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Kuruluşunuz iş profilinize bir sertifika yetkilisi yükledi. Güvenli ağ trafiğiniz izlenebilir veya değiştirilebilir."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Bu cihazda bir sertifika yetkilisi yüklü. Güvenli ağ trafiğiniz izlenebilir veya değiştirilebilir."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Yöneticiniz,cihazınızdaki trafiği izleyen ağ günlük kaydını açtı."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Yöneticiniz, iş profilinizdeki trafiği izleyen ancak kişisel profilinizdeki trafiği izlemeyen ağ günlük kaydını açtı."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"E-postalarınız, uygulamalarınız ve web siteleriniz de dahil olmak üzere ağ etkinliğinizi takip edebilen <xliff:g id="VPN_APP">%1$s</xliff:g> ağına bağlısınız."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"E-postalar, uygulamalar ve web siteleri de dahil olmak üzere ağ etkinliğinizi izleyebilen <xliff:g id="VPN_APP_0">%1$s</xliff:g> ve <xliff:g id="VPN_APP_1">%2$s</xliff:g> uygulamalarına bağlısınız."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"İş profiliniz, e-postalar, uygulamalar ve web siteleri dahil olmak üzere ağ etkinliğinizi izleyebilen <xliff:g id="VPN_APP">%1$s</xliff:g> uygulamasına bağlı."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sesi kapatmak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Titreşime ayarlamak için dokunun."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Sesi kapatmak için dokunun."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"sesi kapat"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"sesi aç"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titreşim"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Bu bildirim, sistem tarafından otomatik olarak <b>Sessize düşürüldü</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Bu bildirim, gölgenizde otomatik olarak <b>daha yüksek sıralandı</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Bu bildirim, gölgenizde otomatik olarak <b>daha düşük sıralandı</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Bu doğru muydu?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Görüşlerinizi geliştiriciye bildirin. Bu doğru muydu?"</string> <string name="feedback_response" msgid="4671729244976641339">"Geri bildiriminiz için teşekkürler!"</string> <string name="feedback_ok" msgid="6481426753298857144">"Tamam"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> için bildirim kontrolleri açıldı"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna taşı"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna ekle"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Konum: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Kart eklendi"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Parça kaldırıldı"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Hızlı ayar düzenleyicisi."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirimi: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ayarları aç."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> uygulamasını kullanıyor"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>, yakın zamanda <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> uygulamasını kullandı"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(kurumsal)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefon çağrısı"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Sesli arama"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> aracılığıyla)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"konum"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensörler kapalı"</string> <string name="device_services" msgid="1549944177856658705">"Cihaz Hizmetleri"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Başlıksız"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Bu uygulamayı yeniden başlatmak ve tam ekrana geçmek için dokunun."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Taşı"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Sistemde gezinme yöntemi güncellendi. Değişiklik yapmak için Ayarlar\'a gidin."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Sistemde gezinme yöntemini güncellemek için Ayarlar\'a gidin"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Ekle"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> tarafından önerildi"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Denetimler güncellendi"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Cihaz kilitlendi"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN, harf veya simge içerir"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> cihazını doğrulayın"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Yanlış PIN"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index b6b7cee5c4f9..12b48306a175 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Прокрутити знімок екрана"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Закрити знімок екрана"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Перегляд знімка екрана"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Верхня межа"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Нижня межа"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Відеозапис екрана"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обробка записування екрана"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Сповіщення про сеанс запису екрана"</string> @@ -414,6 +416,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"До сходу сонця"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Вмикається о <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"До <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC вимкнено"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC ввімкнено"</string> @@ -447,6 +451,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Розблокуйте екран, щоб скористатись NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Цей пристрій належить вашій організації"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Цей пристрій належить організації \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\""</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Телефон: проведіть пальцем від значка"</string> <string name="voice_hint" msgid="7476017460191291417">"Голосові підказки: проведіть пальцем від значка"</string> <string name="camera_hint" msgid="4519495795000658637">"Камера: проведіть пальцем від значка"</string> @@ -467,9 +473,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показати профіль"</string> <string name="user_add_user" msgid="4336657383006913022">"Додати користувача"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новий користувач"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Завершити сеанс у режимі \"Гість\"?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Завершити сеанс у режимі \"Гість\""</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Вийти з режиму гостя?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Завершити сеанс"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Вийти"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"З поверненням!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продовжити сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почати знову"</string> @@ -546,6 +553,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Адміністратор організації встановив центр сертифікації у вашому робочому профілі. Захищений мережевий трафік може відстежуватися або змінюватися."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"На цьому пристрої встановлено центр сертифікації. Захищений мережевий трафік може відстежуватися або змінюватися."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Ваш адміністратор увімкнув реєстрацію в мережі, під час якої на вашому пристрої відстежується трафік."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Ваш адміністратор увімкнув реєстрацію в журналі мережі, щоб відстежувати трафік вашого робочого профілю (не особистого)."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Під’єднано додаток <xliff:g id="VPN_APP">%1$s</xliff:g>, який може відстежувати вашу активність у мережі, як-от відкривання електронних листів, додатків і веб-сайтів."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Під’єднано додатки <xliff:g id="VPN_APP_1">%2$s</xliff:g> та <xliff:g id="VPN_APP_0">%1$s</xliff:g>, які можуть відстежувати вашу активність у мережі, як-от відкривання електронних листів, додатків і веб-сайтів."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Ваш робочий профіль під’єднано до додатка <xliff:g id="VPN_APP">%1$s</xliff:g>, який може відстежувати вашу активність у мережі, зокрема в електронній пошті, додатках і на веб-сайтах."</string> @@ -626,6 +634,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Торкніться, щоб вимкнути звук. Спеціальні можливості може бути вимкнено."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Торкніться, щоб налаштувати вібросигнал."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Торкніться, щоб вимкнути звук."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"вимкнути звук"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"увімкнути звук"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"увімкнути вібросигнал"</string> @@ -739,7 +749,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Пріоритет цього сповіщення автоматично <b>знижено до \"Без звуку\"</b> в системі."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Пріоритет цього сповіщення на панелі сповіщень автоматично <b>підвищено</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Пріоритет цього сповіщення на панелі сповіщень автоматично <b>знижено</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Правильно?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Надішліть розробнику свій відгук. Усе правильно?"</string> <string name="feedback_response" msgid="4671729244976641339">"Дякуємо за відгук!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Елементи керування сповіщеннями для додатка <xliff:g id="APP_NAME">%1$s</xliff:g> відкрито"</string> @@ -890,6 +900,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перемістити на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додати на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Опцію додано"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Опцію вилучено"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор швидких налаштувань."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Сповіщення <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Відкрити налаштування."</string> @@ -980,7 +992,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"Додаток <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> використовує функцію \"<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>\""</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Додаток <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> нещодавно використав функцію \"<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>\""</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(корпоративний додаток)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Додаток для телефонних дзвінків"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Телефонний дзвінок"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(через <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"камеру"</string> <string name="privacy_type_location" msgid="7991481648444066703">"місце"</string> @@ -988,7 +1000,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Датчики вимкнено"</string> <string name="device_services" msgid="1549944177856658705">"Сервіси на пристрої"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Без назви"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Натисніть, щоб перезапустити додаток і перейти в повноекранний режим."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Перемістити"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Навігацію в системі оновлено. Щоб внести зміни, перейдіть у налаштування."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Перейдіть у налаштування, щоб оновити навігацію в системі"</string> @@ -1044,6 +1055,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Додати"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Запропоновано додатком <xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Елементи керування оновлено"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Пристрій заблоковано"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN-код містить літери чи символи"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Неправильний PIN-код"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 15c9607aa78f..3668f7e430c8 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"اسکرین شاٹ پر اسکرول کریں"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"اسکرین شاٹ برخاست کریں"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"اسکرین شاٹ کا پیش منظر"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"اوپر کا احاطہ"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"نیچے کا احاطہ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"سکرین ریکارڈر"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"سکرین ریکارڈنگ پروسیس ہورہی ہے"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"اسکرین ریکارڈ سیشن کیلئے جاری اطلاع"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"طلوع آفتاب تک"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"آن ہوگی بوقت <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> تک"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC غیر فعال ہے"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC فعال ہے"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC استعمال کرنے کیلئے غیر مقفل کریں"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"یہ آلہ آپ کی تنظیم کا ہے"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"یہ آلہ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> کا ہے"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"فون کیلئے آئیکن سے سوائپ کریں"</string> <string name="voice_hint" msgid="7476017460191291417">"صوتی معاون کیلئے آئیکن سے سوائپ کریں"</string> <string name="camera_hint" msgid="4519495795000658637">"کیمرہ کیلئے آئیکن سے سوائپ کریں"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"پروفائل دکھائیں"</string> <string name="user_add_user" msgid="4336657383006913022">"صارف کو شامل کریں"</string> <string name="user_new_user_name" msgid="2019166282704195789">"نیا صارف"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"مہمان سیشن ختم کریں؟"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"مہمان سیشن ختم کریں"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"مہمان کو ہٹائیں؟"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"سیشن ختم کریں"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ہٹائیں"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مہمان، پھر سے خوش آمدید!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"کیا آپ اپنا سیشن جاری رکھنا چاہتے ہیں؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"دوبارہ شروع کریں"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"آپ کی تنظیم نے آپ کے دفتری پروفائل میں ایک سرٹیفکیٹ کی اتھارٹی کو انسٹال کیا ہے۔ آپ کا محفوظ نیٹ ورک ٹریفک مانیٹر ہو سکتا ہے یا اس میں ترمیم کی جا سکتی ہے۔"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ایک سرٹیفکیٹ کی اتھارٹی اس آلہ پر انسٹال ہے۔ آپ کا محفوظ نیٹ ورک ٹریفک مانیٹر ہو سکتا ہے یا اس میں ترمیم کی جا سکتی ہے۔"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"آپ کے منتظم نے نیٹ ورک لاگنگ کو آن کر دیا ہے، جو آپ کے آلے پر ٹریفک مانیٹر کرتی ہے۔"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"آپ کے منتظم نے نیٹ ورک لاگنگ آن کر دی ہے، جو آپ کے ذاتی پروفائل پر نہیں بلکہ دفتری پروفائل پر ٹریفک کو مانیٹر کرتی ہے۔"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"آپ <xliff:g id="VPN_APP">%1$s</xliff:g> سے منسلک ہیں جو ای میلز، ایپس اور ویب سائٹس سمیت آپ کے نیٹ ورک کی سرگرمی مانیٹر کر سکتی ہے۔"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"آپ <xliff:g id="VPN_APP_0">%1$s</xliff:g> اور <xliff:g id="VPN_APP_1">%2$s</xliff:g> سے منسلک ہیں، جو ای میلز، ایپس اور ویب سائٹس سمیت آپ کے نیٹ ورک کی سرگرمی مانیٹر کر سکتی ہیں۔"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"آپ کا دفتری پروفائل <xliff:g id="VPN_APP">%1$s</xliff:g> سے منسلک ہے، جو ای میلز، ایپس اور ویب سائٹس سمیت آپ کے نیٹ ورک کی سرگرمی مانیٹر کر سکتی ہے۔"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s۔ خاموش کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s۔ ارتعاش پر سیٹ کرنے کیلئے تھپتھپائیں۔"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s۔ خاموش کرنے کیلئے تھپتھپائیں۔"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"خاموش کریں"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"غیر خاموش کریں"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"وائبریٹ"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"اس اطلاع کو خودکار طور پر سسٹم کے ذریعے <b>خاموش پر درجہ بند<b> کیا گیا۔"</string> <string name="feedback_promoted" msgid="2125562787759780807">"اس اطلاع کو آپ کے شیڈ میں خودکار طور پر <b>اعلی درجہ</b> دیا گیا۔"</string> <string name="feedback_demoted" msgid="951884763467110604">"اس اطلاع کو آپ کے شیڈ میں خودکار طور پر <b>کم درجہ</b> دیا گیا۔"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"کیا یہ درست تھا؟"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"ڈویلپر کو اپنے تاثرات فراہم کریں کیا یہ درست تھا؟"</string> <string name="feedback_response" msgid="4671729244976641339">"آپ کے تاثرات کا شکریہ!"</string> <string name="feedback_ok" msgid="6481426753298857144">"ٹھیک ہے"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> کیلئے اطلاعی کنٹرولز کھلے ہیں"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> میں منتقل کریں"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g> میں شامل کریں"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"ٹائل کو شامل کیا گیا"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"ٹائل کو ہٹا دیا گیا"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"فوری ترتیبات کا ایڈیٹر۔"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> اطلاع: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ترتیبات کھولیں۔"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> کا استعمال کر رہی ہے"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> نے حال ہی میں <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> کا استعمال کیا"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(انٹرپرائز)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"فون کال"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"فون کال"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> کے ذریعے)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"کیمرا"</string> <string name="privacy_type_location" msgid="7991481648444066703">"مقام"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"سینسرز آف ہیں"</string> <string name="device_services" msgid="1549944177856658705">"آلہ کی سروس"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"کوئی عنوان نہیں ہے"</string> - <string name="restart_button_description" msgid="6916116576177456480">"یہ ایپ دوبارہ شروع کرنے کے لیے تھپتھپائیں اور پوری اسکرین پر جائیں۔"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"منتقل کریں"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"سسٹم نیویگیشن اپ ڈیٹ کیا گیا۔ تبدیلیاں کرنے کے لیے، ترتیبات پر جائیں۔"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"سسٹم نیویگیشن اپ ڈیٹ کرنے کے لیے ترتیبات پر جائیں"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"شامل کریں"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> کی طرف سے تجویز کردہ"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"کنٹرولز اپ ڈیٹ کیے گئے"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"آلہ مقفل کر دیا گیا"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN میں حروف یا علامات شامل ہیں"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g> کی تصدیق کریں"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"غلط PIN"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 3044a98ad814..0732c7f3e82b 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Skrinshotni aylantirish"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Skrinshotni yopish"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Skrinshotga razm solish"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Yuqori chegara"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Quyi chegara"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Ekrandan yozib olish"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran yozib olinmoqda"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekrandan yozib olish seansi uchun joriy bildirishnoma"</string> @@ -410,6 +412,7 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Quyosh chiqqunicha"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g> da yoqiladi"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"<xliff:g id="TIME">%s</xliff:g> gacha"</string> + <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"Yorqinlikni pasaytirish"</string> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC o‘chiq"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC yoniq"</string> @@ -443,6 +446,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ishlatish uchun qurilma qulfini oching"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Bu qurilma tashkilotingizga tegishli"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Bu qurilma <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> tashkilotiga tegishli"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Telefonni ochish uchun suring"</string> <string name="voice_hint" msgid="7476017460191291417">"Ovozli yordam: belgidan boshlab suring"</string> <string name="camera_hint" msgid="4519495795000658637">"Kamerani ochish uchun suring"</string> @@ -463,9 +468,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profilni ko‘rsatish"</string> <string name="user_add_user" msgid="4336657383006913022">"Foydalanuvchi"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yangi foydalanuvchi"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Mehmon seansi yakunlansinmi?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Mehmon seansini yakunlash"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Mehmon hisobi o‘chirib tashlansinmi?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Seansni yakunlash"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Olib tashlash"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Xush kelibsiz, mehmon!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Seansni davom ettirmoqchimisiz?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Boshidan boshlansin"</string> @@ -540,6 +546,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Tashkilotingiz ishchi profilingizga CA sertifikatini o‘rnatdi. U himoyalangan tarmoq trafigini nazorat qilishi va o‘zgartirishi mumkin."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Qurilmada CA sertifikati o‘rnatilgan. U himoyalangan tarmoq trafigini nazorat qilishi va o‘zgartirishi mumkin."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administrator qurilmangizdagi trafikni nazorat qiluvchi tarmoq jurnalini yoqdi."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administrator ish profilingizdagi trafikni nazorat qiluvchi tarmoq jurnalini yoqdi (shaxsiy profildan maʼlumotlar olinmaydi)."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"<xliff:g id="VPN_APP">%1$s</xliff:g> ilovasi ishga tushirilgan. U internetdagi harakatlaringiz, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> va <xliff:g id="VPN_APP_1">%2$s</xliff:g> ilovalari ishga tushirilgan. Ular tarmoqdagi, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Ishchi profilingizda tarmoqdagi, jumladan, e-pochta, ilova va veb-saytlardagi xatti-harakatlaringizni kuzatishi mumkin bo‘lgan <xliff:g id="VPN_APP">%1$s</xliff:g> ilovasi ishga tushirilgan."</string> @@ -620,6 +627,7 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ovozini o‘chirish uchun ustiga bosing. Maxsus imkoniyatlar ishlamasligi mumkin."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Tebranishni yoqish uchun ustiga bosing."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Ovozsiz qilish uchun ustiga bosing."</string> + <string name="volume_ringer_change" msgid="3574969197796055532">"Jiringlagich rejimini oʻzgartirish uchun bosing"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ovozsiz qilish"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ovozni yoqish"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tebranish"</string> @@ -733,7 +741,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Bu bildirishnomaning muhimlik darajasi tizim tomonidan avtomatik ravishda <b>Sokin darajaga tushirildi</b>."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Bu bildirishnomaning muhimlik darajasi tizim tomonidan avtomatik ravishda <b>yuqori darajaga chiqarildi</b>."</string> <string name="feedback_demoted" msgid="951884763467110604">"Bu bildirishnomaning muhimlik darajasi tizim tomonidan avtomatik ravishda <b>quyi darajaga tushirildi</b>."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Xatolar boʻlmadimi?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Ishlab chiquvchiga fikr-mulohazalaringizni bildiring. Xatolar boʻlmadimi?"</string> <string name="feedback_response" msgid="4671729244976641339">"Fikr-mulohazangiz uchun tashakkur!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun bildirishnoma sozlamalari ochildi"</string> @@ -880,6 +888,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Bu joyga olish: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bu joyga kiritish: <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Joylashuv: <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Katakcha kiritildi"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Katakcha olib tashlandi"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Tezkor sozlamalar muharriri"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirishnomasi: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Sozlamalarni ochish."</string> @@ -970,7 +980,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> hozir <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ishlatmoqda"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> yaqinda <xliff:g id="APP_OPP_NAME">%2$s</xliff:g> ishlatgan"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(korporativ)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Telefon chaqiruvi"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Telefon chaqiruvi"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(<xliff:g id="ATTRIBUTION">%s</xliff:g> orqali)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"kamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"joylashuv"</string> @@ -978,7 +988,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Sensorlar nofaol"</string> <string name="device_services" msgid="1549944177856658705">"Qurilma xizmatlari"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Nomsiz"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Bu ilovani qaytadan ishga tushirish va butun ekranga ochish uchun bosing."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Surish"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Tizim navigatsiyasi yangilandi. Buni Sozlamalar orqali oʻzgartirishingiz mumkin."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Tizim navigatsiyasini yangilash uchun Sozlamalarni oching"</string> @@ -1032,6 +1041,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Kiritish"</string> <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> taklif etgan"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Boshqaruv elementlari yangilandi"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Qurilma qulflandi"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Harflar yoki maxsus belgilardan iborat PIN kod"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Tekshirish: <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN kod xato"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 4913be4f9565..cc893037e1e5 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Cuộn để phóng to ảnh chụp màn hình"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Đóng ảnh chụp màn hình"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Xem trước ảnh chụp màn hình"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Ranh giới trên"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Ranh giới dưới"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Trình ghi màn hình"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Đang xử lý video ghi màn hình"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Thông báo đang diễn ra về phiên ghi màn hình"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Cho đến khi trời sáng"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Bật vào lúc <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Cho đến <xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC đã được tắt"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC đã được bật"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Mở khóa để sử dụng NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Thiết bị này thuộc về tổ chức của bạn"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Thiết bị này thuộc về <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Vuốt từ biểu tượng để mở điện thoại"</string> <string name="voice_hint" msgid="7476017460191291417">"Vuốt từ biểu tượng để mở trợ lý thoại"</string> <string name="camera_hint" msgid="4519495795000658637">"Vuốt từ biểu tượng để mở máy ảnh"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Hiển thị hồ sơ"</string> <string name="user_add_user" msgid="4336657383006913022">"Thêm người dùng"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Người dùng mới"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Kết thúc phiên khách?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Kết thúc phiên khách"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Xóa phiên khách?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Kết thúc phiên"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Xóa"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Chào mừng bạn trở lại!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Bạn có muốn tiếp tục phiên của mình không?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Bắt đầu lại"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Tổ chức của bạn đã cài đặt một tổ chức phát hành chứng chỉ trong hồ cơ công việc của bạn. Lưu lượng truy cập mạng bảo mật của bạn có thể được giám sát hoặc sửa đổi."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Một tổ chức phát hành chứng chỉ được cài đặt trên thiết bị này. Lưu lượng truy cập mạng bảo mật của bạn có thể được giám sát hoặc sửa đổi."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Quản trị viên của bạn đã bật tính năng ghi nhật ký mạng. Tính năng này giám sát lưu lượng truy cập trên thiết bị của bạn."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Quản trị viên của bạn đã bật tính năng ghi nhật ký mạng. Tính năng này giám sát lưu lượng truy cập trong hồ sơ công việc chứ không giám sát lưu lượng truy cập trong hồ sơ cá nhân của bạn."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Bạn đang kết nối với <xliff:g id="VPN_APP">%1$s</xliff:g>. Ứng dụng này có thể giám sát hoạt động mạng của bạn, bao gồm email, ứng dụng và trang web."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Bạn đang kết nối với <xliff:g id="VPN_APP_0">%1$s</xliff:g> và <xliff:g id="VPN_APP_1">%2$s</xliff:g>. Các ứng dụng này có thể giám sát hoạt động mạng của bạn, bao gồm email, ứng dụng và trang web."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Hồ sơ công việc của bạn được kết nối với <xliff:g id="VPN_APP">%1$s</xliff:g>, ứng dụng này có thể giám sát hoạt động mạng của bạn, bao gồm email, ứng dụng và trang web."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Nhấn để tắt tiếng. Bạn có thể tắt tiếng dịch vụ trợ năng."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Nhấn để đặt chế độ rung."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Nhấn để tắt tiếng."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"tắt tiếng"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"bật tiếng"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rung"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Hệ thống đã tự động <b>thay đổi thành Im lặng</b> theo tầm quan trọng của thông báo này."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Hệ thống đã tự động <b>tăng mức độ quan trọng</b> của thông báo này trong ngăn thông báo."</string> <string name="feedback_demoted" msgid="951884763467110604">"Hệ thống đã tự động <b>giảm mức độ quan trọng</b> của thông báo này trong ngăn thông báo."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Thông tin này có chính xác không?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Chia sẻ ý kiến phản hồi của bạn với nhà phát triển. Thông tin này có chính xác không?"</string> <string name="feedback_response" msgid="4671729244976641339">"Cảm ơn bạn đã phản hồi!"</string> <string name="feedback_ok" msgid="6481426753298857144">"OK"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Đã mở điều khiển thông báo đối với <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Di chuyển tới <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Thêm vào vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Đã thêm thẻ thông tin"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Đã xóa thẻ thông tin"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Trình chỉnh sửa cài đặt nhanh."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Thông báo của <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Mở phần cài đặt."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> đang sử dụng <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"Gần đây, <xliff:g id="APPLICATION_NAME">%1$s</xliff:g> đã sử dụng <xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(doanh nghiệp)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Gọi điện thoại"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Cuộc gọi điện thoại"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(thông qua <xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"máy ảnh"</string> <string name="privacy_type_location" msgid="7991481648444066703">"vị trí"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Tắt cảm biến"</string> <string name="device_services" msgid="1549944177856658705">"Dịch vụ cho thiết bị"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Không có tiêu đề"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Nhấn để khởi động lại ứng dụng này và xem ở chế độ toàn màn hình."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Di chuyển"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Đã cập nhật chế độ di chuyển trên hệ thống. Để thay đổi, hãy chuyển đến phần Cài đặt."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Chuyển đến phần Cài đặt để cập nhật chế độ di chuyển trên hệ thống"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Thêm"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Do <xliff:g id="APP">%s</xliff:g> đề xuất"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Đã cập nhật các tùy chọn điều khiển"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Đã khóa thiết bị"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Mã PIN chứa các ký tự hoặc ký hiệu"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Xác minh <xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Mã PIN sai"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 3479d009804c..92f9e67f5f08 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"滚动抓取长截图"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"关闭屏幕截图"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"屏幕截图预览"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"顶部边界"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"底部边界"</string> <string name="screenrecord_name" msgid="2596401223859996572">"屏幕录制器"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"正在处理屏幕录制视频"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"持续显示屏幕录制会话通知"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"在日出时关闭"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"在<xliff:g id="TIME">%s</xliff:g> 开启"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"直到<xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 已停用"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 已启用"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"需要解锁才能使用 NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"此设备归贵单位所有"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"此设备归<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>所有"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"滑动图标即可拨打电话"</string> <string name="voice_hint" msgid="7476017460191291417">"滑动图标即可打开语音助理"</string> <string name="camera_hint" msgid="4519495795000658637">"滑动图标即可打开相机"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"显示个人资料"</string> <string name="user_add_user" msgid="4336657383006913022">"添加用户"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新用户"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"要结束访客会话吗?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"结束访客会话"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"要移除访客吗?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"结束会话"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"访客,欢迎回来!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"要继续您的会话吗?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新开始"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"您所在的单位已为您的工作资料安装证书授权中心。您的安全网络流量可能会受到监控或修改。"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"此设备上已安装证书授权中心。您的安全网络流量可能会受到监控或修改。"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"您的管理员已开启网络日志功能(该功能会监控您设备上的流量)。"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"您的管理员已开启网络日志功能,该功能会监控您的工作资料的流量,而不会监控个人资料的流量。"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"您已连接到“<xliff:g id="VPN_APP">%1$s</xliff:g>”(该应用能够监控您的网络活动,其中包括收发电子邮件、使用应用和浏览网站)。"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"您已连接到“<xliff:g id="VPN_APP_0">%1$s</xliff:g>”和“<xliff:g id="VPN_APP_1">%2$s</xliff:g>”(这两个应用能够监控您的网络活动,其中包括收发电子邮件、使用应用和浏览网站)。"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"您的工作资料已连接到“<xliff:g id="VPN_APP">%1$s</xliff:g>”(该应用能够监控您的网络活动,其中包括收发电子邮件、使用应用和浏览网站)。"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。点按即可设为静音,但可能会同时将无障碍服务设为静音。"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s。点按即可设为振动。"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s。点按即可设为静音。"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"静音"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消静音"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"振动"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"系统已自动将此通知的重要性<b>降低为“静音”</b>。"</string> <string name="feedback_promoted" msgid="2125562787759780807">"系统已自动<b>调高</b>此通知在通知栏中的顺序。"</string> <string name="feedback_demoted" msgid="951884763467110604">"系统已自动<b>调低</b>此通知在通知栏中的顺序。"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"是否正确?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"向开发者提供反馈。此信息是否正确?"</string> <string name="feedback_response" msgid="4671729244976641339">"感谢您提供反馈!"</string> <string name="feedback_ok" msgid="6481426753298857144">"确定"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"<xliff:g id="APP_NAME">%1$s</xliff:g>的通知控件已打开"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"添加到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"已添加卡片"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"已移除卡片"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快捷设置编辑器。"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"打开设置。"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>正在使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>最近曾使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(企业版)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"电话"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"电话"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(通过<xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"相机"</string> <string name="privacy_type_location" msgid="7991481648444066703">"位置信息"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"已关闭传感器"</string> <string name="device_services" msgid="1549944177856658705">"设备服务"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"无标题"</string> - <string name="restart_button_description" msgid="6916116576177456480">"点按即可重启此应用并进入全屏模式。"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"移动"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"系统导航已更新。要进行更改,请转到“设置”。"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"转到“设置”即可更新系统导航"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"添加"</string> <string name="controls_dialog_message" msgid="342066938390663844">"来自<xliff:g id="APP">%s</xliff:g>的建议"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"控件已更新"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"设备已锁定"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN 码由字母或符号组成"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"验证<xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN 码错误"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 314b728071af..d2a232b6db22 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"整頁截圖"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"關閉螢幕截圖"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"上方邊界"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"下方邊界"</string> <string name="screenrecord_name" msgid="2596401223859996572">"螢幕畫面錄影工具"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"正在處理螢幕錄影內容"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"持續顯示錄影畫面工作階段通知"</string> @@ -410,6 +412,7 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"在日出時關閉"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"於<xliff:g id="TIME">%s</xliff:g>開啟"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"直至<xliff:g id="TIME">%s</xliff:g>"</string> + <string name="quick_settings_reduce_bright_colors_label" msgid="7537352080559075175">"調低亮度"</string> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 已停用"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 已啟用"</string> @@ -440,9 +443,11 @@ <string name="notification_tap_again" msgid="4477318164947497249">"再次輕按即可開啟"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"向上滑動即可開啟"</string> <string name="keyguard_retry" msgid="886802522584053523">"請向上滑動以再試一次"</string> - <string name="require_unlock_for_nfc" msgid="1305686454823018831">"解鎖以使用 NFC"</string> + <string name="require_unlock_for_nfc" msgid="1305686454823018831">"解鎖方可使用 NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"此裝置屬於您的機構"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"此裝置屬於「<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>」"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"從圖示滑動即可使用手機功能"</string> <string name="voice_hint" msgid="7476017460191291417">"從圖示滑動即可使用語音助手"</string> <string name="camera_hint" msgid="4519495795000658637">"從圖示滑動即可使用相機功能"</string> @@ -463,9 +468,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"顯示個人檔案"</string> <string name="user_add_user" msgid="4336657383006913022">"加入使用者"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"要結束訪客工作階段嗎?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"結束訪客工作階段"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"結束工作階段"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客您好,歡迎回來!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"您要繼續您的工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> @@ -540,6 +546,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"您的機構已在您的工作設定檔中安裝憑證授權單位。您的安全網絡流量可能會受監控或修改。"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"此裝置已安裝憑證授權單位。您的安全網絡流量可能會受監控或修改。"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"您的管理員已開啟網絡記錄功能,以監控您裝置上的流量。"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"您的管理員已開啟網絡記錄功能,可監控您工作設定檔 (而非個人設定檔) 的流量。"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"您已連接至「<xliff:g id="VPN_APP">%1$s</xliff:g>」,此應用程式可以監控您的網絡活動,包括電郵、應用程式及網站。"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"您已連接至「<xliff:g id="VPN_APP_0">%1$s</xliff:g>」和「<xliff:g id="VPN_APP_1">%2$s</xliff:g>」,這些應用程式可以監控您的網絡活動,包括電郵、應用程式及網站。"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"您的工作設定檔已連結至「<xliff:g id="VPN_APP">%1$s</xliff:g>」,此應用程式可以監控您的網絡活動,包括電郵、應用程式及網站。"</string> @@ -620,6 +627,7 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。輕按即可設為靜音。無障礙功能服務可能已經設為靜音。"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s。輕按即可設為震動。"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s。輕按即可設為靜音。"</string> + <string name="volume_ringer_change" msgid="3574969197796055532">"輕按即可變更響鈴模式"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string> @@ -733,7 +741,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"系統已自動將此通知的重要性<b>降低為靜音</b>。"</string> <string name="feedback_promoted" msgid="2125562787759780807">"系統已自動<b>提高</b>此通知在通知欄中的次序。"</string> <string name="feedback_demoted" msgid="951884763467110604">"系統已自動<b>調低</b>此通知在通知欄中的次序。"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"是否正確?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"與開發人員分享您的意見。是否正確?"</string> <string name="feedback_response" msgid="4671729244976641339">"多謝您提供意見!"</string> <string name="feedback_ok" msgid="6481426753298857144">"確定"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"開咗「<xliff:g id="APP_NAME">%1$s</xliff:g>」嘅通知控制項"</string> @@ -880,6 +888,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移去 <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"加去位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"加咗圖塊"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"移除咗圖塊"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯工具。"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"開啟設定。"</string> @@ -970,7 +980,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」正在使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」最近曾使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(企業版本)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"電話"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"電話"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(透過「<xliff:g id="ATTRIBUTION">%s</xliff:g>」)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"相機"</string> <string name="privacy_type_location" msgid="7991481648444066703">"位置"</string> @@ -978,7 +988,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"感應器已關閉"</string> <string name="device_services" msgid="1549944177856658705">"裝置服務"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"無標題"</string> - <string name="restart_button_description" msgid="6916116576177456480">"輕按即可重新開啟此應用程式並放大至全螢幕。"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"移動"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"系統導覽已更新。如需變更,請前往「設定」。"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"前往「設定」更新系統導覽"</string> @@ -1032,6 +1041,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"新增"</string> <string name="controls_dialog_message" msgid="342066938390663844">"由「<xliff:g id="APP">%s</xliff:g>」提供的建議"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"已更新控制項"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"裝置已上鎖"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN 含有字母或符號"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"驗證<xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN 錯誤"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 68b631fff5da..396484afb2ff 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -71,7 +71,7 @@ <string name="wifi_debugging_secondary_user_message" msgid="4492383073970079751">"目前登入這部裝置的使用者無法開啟無線偵錯功能。如要使用這項功能,請切換到主要使用者。"</string> <string name="usb_contaminant_title" msgid="894052515034594113">"USB 連接埠已停用"</string> <string name="usb_contaminant_message" msgid="7730476585174719805">"為了避免液體或灰塵導致你的裝置受損,系統已停用 USB 連接埠,因此目前無法偵測任何配件。\n\n系統會在可繼續使用 USB 連接埠時通知你。"</string> - <string name="usb_port_enabled" msgid="531823867664717018">"USB 通訊埠已啟用,可偵測充電器和配件"</string> + <string name="usb_port_enabled" msgid="531823867664717018">"USB 連接埠已啟用,可偵測充電器和配件"</string> <string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"啟用 USB 連接埠"</string> <string name="learn_more" msgid="4690632085667273811">"瞭解詳情"</string> <string name="compat_mode_on" msgid="4963711187149440884">"放大為全螢幕"</string> @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"以捲動畫面的方式拍攝長截圖"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"關閉螢幕截圖"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"頂端邊界"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"底部邊界"</string> <string name="screenrecord_name" msgid="2596401223859996572">"螢幕錄影器"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"處理螢幕錄影內容"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"持續顯示螢幕畫面錄製工作階段通知"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"於日出時關閉"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"開啟時間:<xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"關閉時間:<xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC 已停用"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC 已啟用"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"如要使用 NFC,請先解鎖"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"這部裝置的擁有者為貴機構"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"這部裝置的擁有者為「<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>」"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"滑動手機圖示即可啟用"</string> <string name="voice_hint" msgid="7476017460191291417">"滑動語音小幫手圖示即可啟用"</string> <string name="camera_hint" msgid="4519495795000658637">"滑動相機圖示即可啟用"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"顯示設定檔"</string> <string name="user_add_user" msgid="4336657383006913022">"新增使用者"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"要結束訪客工作階段嗎?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"結束訪客工作階段"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會遭到刪除。"</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"結束工作階段"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客你好,歡迎回來!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"你要繼續這個工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"貴機構已為你的工作資料夾安裝憑證授權單位憑證。你的安全網路流量可能會受到監控或修改。"</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"這個裝置已安裝憑證授權單位憑證。你的安全網路流量可能會受到監控或修改。"</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"你的管理員已啟用網路記錄功能,可監控你裝置的流量。"</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"你的管理員已啟用網路記錄功能,可監控你的工作資料夾流量,但不會監控個人資料夾的流量。"</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"由於你已連結至「<xliff:g id="VPN_APP">%1$s</xliff:g>」,因此你的網路活動 (包括收發電子郵件、使用應用程式及瀏覽網站) 可能會受到這個應用程式監控。"</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"由於你已連結至「<xliff:g id="VPN_APP_0">%1$s</xliff:g>」和「<xliff:g id="VPN_APP_1">%2$s</xliff:g>」,因此你的網路活動 (包括收發電子郵件、使用應用程式及瀏覽網站) 可能會受到這兩個應用程式監控。"</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"由於你的工作資料夾已連結至「<xliff:g id="VPN_APP">%1$s</xliff:g>」,因此你的網路活動 (包括收發電子郵件、使用應用程式及瀏覽網站) 可能會受到這個應用程式監控。"</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。輕觸即可設為靜音,但系統可能會將無障礙服務一併設為靜音。"</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s。輕觸即可設為震動。"</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s。輕觸即可設為靜音。"</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"系統已自動將這則通知的重要性<b>降低為靜音</b>。"</string> <string name="feedback_promoted" msgid="2125562787759780807">"系統已自動<b>調高</b>這則通知在通知欄中的順序。"</string> <string name="feedback_demoted" msgid="951884763467110604">"系統已自動<b>調降</b>這則通知在通知欄中的順序。"</string> - <string name="feedback_prompt" msgid="2278631214125128281">"是否正確?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"與開發人員分享你的意見。是否正確?"</string> <string name="feedback_response" msgid="4671729244976641339">"感謝你提供意見!"</string> <string name="feedback_ok" msgid="6481426753298857144">"確定"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」的通知控制項已開啟"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"新增到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"已新增資訊方塊"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"已移除資訊方塊"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯器。"</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"開啟設定。"</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」正在使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"「<xliff:g id="APPLICATION_NAME">%1$s</xliff:g>」最近曾使用<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(企業版)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"電話"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"電話"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(透過「<xliff:g id="ATTRIBUTION">%s</xliff:g>」)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"相機"</string> <string name="privacy_type_location" msgid="7991481648444066703">"位置"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"已關閉感應器"</string> <string name="device_services" msgid="1549944177856658705">"裝置服務"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"無標題"</string> - <string name="restart_button_description" msgid="6916116576177456480">"輕觸即可重新啟動這個應用程式並進入全螢幕模式。"</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"移動"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"系統操作機制已更新。如要進行變更,請前往「設定」。"</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"請前往「設定」更新系統操作機制"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"新增"</string> <string name="controls_dialog_message" msgid="342066938390663844">"來自「<xliff:g id="APP">%s</xliff:g>」的建議"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"已更新控制項"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"裝置已鎖定"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"PIN 碼含有字母或符號"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"驗證「<xliff:g id="DEVICE">%s</xliff:g>」"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN 碼錯誤"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 78c4f17edf2d..d4e05d0d29aa 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -93,6 +93,8 @@ <string name="screenshot_scroll_description" msgid="7855773867093272175">"Isithombe seskrini sokuskrola"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Cashisa isithombe-skrini"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ukubuka kuqala isithombe-skrini"</string> + <string name="screenshot_top_boundary" msgid="1500569103321300856">"Umngcele ophezulu"</string> + <string name="screenshot_bottom_boundary" msgid="5657242629526407311">"Umngcele ophansi"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Irekhoda yesikrini"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Icubungula okokuqopha iskrini"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Isaziso esiqhubekayo seseshini yokurekhoda isikrini"</string> @@ -410,6 +412,8 @@ <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Kuze kube sekuphumeni kwelanga"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"Kuvulwe ngo-<xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_dark_mode_secondary_label_until" msgid="2289774641256492437">"Kuze kube ngu-<xliff:g id="TIME">%s</xliff:g>"</string> + <!-- no translation found for quick_settings_reduce_bright_colors_label (7537352080559075175) --> + <skip /> <string name="quick_settings_nfc_label" msgid="1054317416221168085">"I-NFC"</string> <string name="quick_settings_nfc_off" msgid="3465000058515424663">"I-NFC ikhutshaziwe"</string> <string name="quick_settings_nfc_on" msgid="1004976611203202230">"I-NFC inikwe amandla"</string> @@ -443,6 +447,8 @@ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Vula ukuze usebenzise i-NFC"</string> <string name="do_disclosure_generic" msgid="4896482821974707167">"Le divayisi eyenhlangano yakho"</string> <string name="do_disclosure_with_name" msgid="2091641464065004091">"Le divayisi ngeye-<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> + <!-- no translation found for do_financed_disclosure_with_name (6723004643314467864) --> + <skip /> <string name="phone_hint" msgid="6682125338461375925">"Swayiphela ifoni kusukela kusithonjana"</string> <string name="voice_hint" msgid="7476017460191291417">"Swayiphela isilekeleli sezwi kusukela kusithonjana"</string> <string name="camera_hint" msgid="4519495795000658637">"Swayiphela ikhamela kusukela kusithonjana"</string> @@ -463,9 +469,10 @@ <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Bonisa iphrofayela"</string> <string name="user_add_user" msgid="4336657383006913022">"Engeza umsebenzisi"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Umsebenzisi omusha"</string> - <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Misa isikhathi sesihambeli?"</string> + <string name="guest_exit_button" msgid="3059840571760915762">"Misa isikhathi sesihambeli"</string> + <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Susa isivakashi?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Zonke izinhlelo zokusebenza nedatha kulesi sikhathi zizosuswa."</string> - <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Phothula iseshini"</string> + <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Susa"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Siyakwamukela futhi, sivakashi!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ingabe ufuna ukuqhubeka ngesikhathi sakho?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Qala phansi"</string> @@ -540,6 +547,7 @@ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Inhlangano yakho ifake ukugunyaza kwesitifiketi kuphrofayela yakho yomsebenzi. Ithrafikhi yenethiwekhi yakho evikelekile ingaqashwa noma ilungiswe."</string> <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Ukugunyaza kwesitifiketi kufakwe kule divayisi. Ithrafikhi yenethiwekhi yakho evikelekile ingaqashelwa noma ilungiswe."</string> <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Umlawuli wakho uvule ukungena kwedivayisi yakho, okuqapha ithrafikhi kudivayisi yakho."</string> + <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Umlawuli wakho uvule ukungena kwenethiwekhi, okuhlola ithrafikhi kudivayisi yakho yephrofayela yomsebenzi kodwa hhayi kuphrofayela yakho yomuntu siqu."</string> <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"Uxhumeke ku-<xliff:g id="VPN_APP">%1$s</xliff:g>, engaqapha umsebenzi wenethiwekhi yakho, ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi."</string> <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"Uxhumeke ku-<xliff:g id="VPN_APP_0">%1$s</xliff:g> naku-<xliff:g id="VPN_APP_1">%2$s</xliff:g>, okungaqaphela umsebenzi wakho wenethiwekhi, ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi."</string> <string name="monitoring_description_managed_profile_named_vpn" msgid="368812367182387320">"Iphrofayela yakho yomsebenzi ixhumeke ku-<xliff:g id="VPN_APP">%1$s</xliff:g>, engaqapha umsebenzi wenethiwekhi yakho, ofaka ama-imeyili, izinhlelo zokusebenza, namawebhusayithi."</string> @@ -620,6 +628,8 @@ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Thepha ukuze uthulise. Amasevisi okufinyelela angathuliswa."</string> <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Thepha ukuze usethele ekudlidlizeni."</string> <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Thepha ukuze uthulise."</string> + <!-- no translation found for volume_ringer_change (3574969197796055532) --> + <skip /> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"thulisa"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"susa ukuthula"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dlidliza"</string> @@ -733,7 +743,7 @@ <string name="feedback_silenced" msgid="9116540317466126457">"Lesi saziso sehliswe isikhundla ngokuzenzakalelayo <b>saba Okuthulile</b> isistimu."</string> <string name="feedback_promoted" msgid="2125562787759780807">"Lesi saziso silinganiselwe phezulu <b>ngokuzenzakalelayo</b> kumthunzi wakho."</string> <string name="feedback_demoted" msgid="951884763467110604">"Lesi saziso silinganiselwe phansi <b>ngokuzenzakalelayo</b> kumthunzi wakho."</string> - <string name="feedback_prompt" msgid="2278631214125128281">"Ingabe kade kulungile lokhu?"</string> + <string name="feedback_prompt" msgid="3656728972307896379">"Tshela unjiniyela impendulo yakho. Ingabe kade kulungile lokhu?"</string> <string name="feedback_response" msgid="4671729244976641339">"Siyabonga ngempendulo!"</string> <string name="feedback_ok" msgid="6481426753298857144">"KULUNGILE"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Izilawuli zesaziso ze-<xliff:g id="APP_NAME">%1$s</xliff:g> zivuliwe"</string> @@ -880,6 +890,8 @@ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hambisa ku-<xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engeza kusikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string> <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Isikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string> + <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Ithayela lingeziwe"</string> + <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Ithayela likhishiwe"</string> <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Isihleli sezilungiselelo ezisheshayo."</string> <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> isaziso: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Vula izilungiselelo."</string> @@ -970,7 +982,7 @@ <string name="ongoing_privacy_dialog_using_op" msgid="4125175620929701569">"I-<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> isebenzisa i-<xliff:g id="APP_OPP_NAME">%2$s</xliff:g>"</string> <string name="ongoing_privacy_dialog_recent_op" msgid="4255923947334262404">"I-<xliff:g id="APPLICATION_NAME">%1$s</xliff:g> isebenzise i-<xliff:g id="APP_OPP_NAME">%2$s</xliff:g> kamuva nje"</string> <string name="ongoing_privacy_dialog_enterprise" msgid="4082735415905550729">"(ibhizinisi)"</string> - <string name="ongoing_privacy_dialog_phonecall" msgid="3526223335298089311">"Ikholi yefoni"</string> + <string name="ongoing_privacy_dialog_phonecall" msgid="4487370562589839298">"Ikholi yefoni"</string> <string name="ongoing_privacy_dialog_attribution_text" msgid="9186683306719924646">"(kuya ku-<xliff:g id="ATTRIBUTION">%s</xliff:g>)"</string> <string name="privacy_type_camera" msgid="7974051382167078332">"ikhamera"</string> <string name="privacy_type_location" msgid="7991481648444066703">"indawo"</string> @@ -978,7 +990,6 @@ <string name="sensor_privacy_mode" msgid="4462866919026513692">"Izinzwa zivaliwe"</string> <string name="device_services" msgid="1549944177856658705">"Amasevisi edivayisi"</string> <string name="music_controls_no_title" msgid="4166497066552290938">"Asikho isihloko"</string> - <string name="restart_button_description" msgid="6916116576177456480">"Thepha ukuze uqale kabusha lolu hlelo lokusebenza uphinde uye kusikrini esigcwele."</string> <string name="bubble_accessibility_action_move" msgid="3185080443743819178">"Hambisa"</string> <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"Ukuzulazula kwesistimu kubuyekeziwe. Ukuze wenze ushintsho, hamba kokuthi Izilungiselelo."</string> <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"Hamba kuzilungiselelo ukuze ubuyekeze ukuzulazula kwesistimu"</string> @@ -1032,6 +1043,7 @@ <string name="controls_dialog_ok" msgid="2770230012857881822">"Engeza"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Kuphakanyiswe ngu-<xliff:g id="APP">%s</xliff:g>"</string> <string name="controls_dialog_confirmation" msgid="586517302736263447">"Izilawuli zibuyekeziwe"</string> + <string name="controls_tile_locked" msgid="731547768182831938">"Idivayisi ikhiyiwe"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Iphinikhodi iqukethe amaletha namasimbui"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"Qinisekisa i-<xliff:g id="DEVICE">%s</xliff:g>"</string> <string name="controls_pin_wrong" msgid="6162694056042164211">"Iphinikhodi engalungile"</string> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index a1191aeacdde..be49e1f8c71e 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -143,6 +143,8 @@ <attr name="handleColor" format="color" /> <attr name="scrimColor" format="color" /> + <attr name="isVertical" format="boolean" /> + <!-- Used display CarrierText in Keyguard or QS Footer --> <declare-styleable name="CarrierText"> <attr name="allCaps" format="boolean" /> @@ -168,7 +170,6 @@ <declare-styleable name="AlphaTintDrawableWrapper"> <attr name="android:tint" /> - <attr name="android:drawable" /> <attr name="android:alpha" /> </declare-styleable> @@ -189,9 +190,5 @@ <attr name="borderThickness" format="dimension" /> <attr name="borderColor" format="color" /> </declare-styleable> - - <declare-styleable name="RoundedCornerProgressDrawable"> - <attr name="android:drawable" /> - </declare-styleable> </resources> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index acd671cb6297..3bc1c8053db3 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -265,7 +265,6 @@ <color name="control_enabled_cool_foreground">@color/GM2_blue_300</color> <color name="control_thumbnail_tint">#33000000</color> <color name="control_thumbnail_shadow_color">@*android:color/black</color> - <color name="controls_lockscreen_scrim">#AA000000</color> <!-- Docked misalignment message --> <color name="misalignment_text_color">#F28B82</color> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 13c01102d032..0893c1488005 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -91,6 +91,9 @@ <!-- The number of columns in the QuickSettings --> <integer name="quick_settings_num_columns">3</integer> + <!-- The number of columns in the Quick Settings customizer --> + <integer name="quick_settings_edit_num_columns">@integer/quick_settings_num_columns</integer> + <!-- The number of rows in the QuickSettings --> <integer name="quick_settings_max_rows">3</integer> @@ -421,6 +424,9 @@ vibrator is capable of subtle vibrations --> <bool name="config_vibrateOnIconAnimation">false</bool> + <!-- Adjust the theme on fully custom and decorated custom view notifications --> + <bool name="config_adjustThemeOnNotificationCustomViews">false</bool> + <!-- If true, enable the advance anti-falsing classifier on the lockscreen. On some devices it does not work well, particularly with noisy touchscreens. Note that disabling it may increase the rate of unintentional unlocks. --> @@ -574,4 +580,7 @@ <!-- Whether to use the split 2-column notification shade --> <bool name="config_use_split_notification_shade">false</bool> + + <!-- Determines whether the shell features all run on another thread. --> + <bool name="config_enableShellMainThread">false</bool> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index b07df9caa95b..ea0ea5e9472a 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -456,10 +456,12 @@ <dimen name="volume_dialog_panel_transparent_padding">20dp</dimen> - <dimen name="volume_dialog_stream_padding">8dp</dimen> + <dimen name="volume_dialog_stream_padding">12dp</dimen> <dimen name="volume_dialog_panel_width">64dp</dimen> + <dimen name="volume_dialog_panel_width_half">32dp</dimen> + <dimen name="volume_dialog_slider_height">116dp</dimen> <dimen name="volume_dialog_ringer_size">64dp</dimen> @@ -486,6 +488,13 @@ <dimen name="volume_tool_tip_arrow_corner_radius">2dp</dimen> + <!-- Size of each item in the ringer selector drawer. --> + <dimen name="volume_ringer_drawer_item_size">64dp</dimen> + <dimen name="volume_ringer_drawer_item_size_half">32dp</dimen> + + <!-- Size of the icon inside each item in the ringer selector drawer. --> + <dimen name="volume_ringer_drawer_icon_size">24dp</dimen> + <!-- Gravity for the notification panel --> <integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top --> @@ -651,7 +660,7 @@ </dimen> <!-- The height of a notification header --> - <dimen name="notification_header_height">53dp</dimen> + <dimen name="notification_header_height">@*android:dimen/notification_header_height</dimen> <!-- The height of the gap between adjacent notification sections. --> <dimen name="notification_section_divider_height">@dimen/notification_side_paddings</dimen> @@ -966,7 +975,7 @@ <dimen name="volume_row_padding_start">4dp</dimen> <dimen name="volume_row_header_padding_start">16dp</dimen> <dimen name="volume_row_height">64dp</dimen> - <dimen name="volume_row_slider_height">48dp</dimen> + <dimen name="volume_row_slider_height">192dp</dimen> <dimen name="volume_row_slider_padding_start">12dp</dimen> <dimen name="volume_expander_margin_end">2dp</dimen> @@ -1359,11 +1368,4 @@ <dimen name="rounded_slider_icon_size">24dp</dimen> <!-- rounded_slider_icon_size / 2 --> <dimen name="rounded_slider_icon_inset">12dp</dimen> - - <dimen name="toast_width">296dp</dimen> - <item name="toast_icon_alpha" format="float" type="dimen">1</item> - <dimen name="toast_text_size">14sp</dimen> - <dimen name="toast_y_offset">48dp</dimen> - <dimen name="toast_icon_size">48dp</dimen> - <dimen name="toast_bg_radius">28dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index d4bb128120e9..e5518928c98c 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -42,4 +42,8 @@ <bool name="flag_lockscreen_animations">false</bool> <bool name="flag_toast_style">false</bool> + + <bool name="flag_navigation_bar_overlay">false</bool> + + <bool name="flag_pm_lite">false</bool> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 3b42600076ac..5f8df5a13ad1 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -959,7 +959,7 @@ <!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until some user-selected time. [CHAR LIMIT=20] --> <string name="quick_settings_dark_mode_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string> <!-- QuickSettings: Label for the toggle that controls whether Reduce Brightness is enabled. [CHAR LIMIT=NONE] --> - <string name="quick_settings_reduce_bright_colors_label">Reduce Brightness</string> + <string name="quick_settings_reduce_bright_colors_label">Reduce brightness</string> <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] --> <string name="quick_settings_nfc_label">NFC</string> @@ -1054,6 +1054,9 @@ <!-- Text on keyguard screen and in Quick Settings footer indicating that user's device belongs to their organization. [CHAR LIMIT=40] --> <string name="do_disclosure_with_name">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string> + <!-- Text on keyguard screen and in Quick Settings footer indicating that the user's device is provided by the Creditor. [CHAR LIMIT=60] --> + <string name="do_financed_disclosure_with_name">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string> + <!-- Shows when people have clicked on the phone icon [CHAR LIMIT=60] --> <string name="phone_hint">Swipe from icon for phone</string> @@ -1558,6 +1561,8 @@ <string name="volume_stream_content_description_vibrate_a11y">%1$s. Tap to set to vibrate.</string> <string name="volume_stream_content_description_mute_a11y">%1$s. Tap to mute.</string> + <string name="volume_ringer_change">Tap to change ringer mode</string> + <!-- Hint for accessibility. For example: double tap to mute [CHAR_LIMIT=NONE] --> <string name="volume_ringer_hint_mute">mute</string> <!-- Hint for accessibility. For example: double tap to unmute [CHAR_LIMIT=NONE] --> @@ -2717,6 +2722,9 @@ <!-- Controls dialog confirmation [CHAR LIMIT=30] --> <string name="controls_dialog_confirmation">Controls updated</string> + <!-- Controls tile secondary label when device is locked and user does not want access to controls from lockscreen [CHAR LIMIT=20] --> + <string name="controls_tile_locked">Device locked</string> + <!-- Controls PIN entry dialog, switch to alphanumeric keyboard [CHAR LIMIT=100] --> <string name="controls_pin_use_alphanumeric">PIN contains letters or symbols</string> <!-- Controls PIN entry dialog, title [CHAR LIMIT=30] --> @@ -2837,6 +2845,8 @@ <string name="empty_user_name" translatable="false">Your friend</string> <!-- Empty status shown before user has selected a friend [CHAR LIMIT=30] --> <string name="empty_status" translatable="false">Their status</string> + <!-- Default text for missed call notifications [CHAR LIMIT=30] --> + <string name="missed_call" translatable="false">Missed call</string> <!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 14b376a8bf6c..2d202fb45bbc 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -663,16 +663,16 @@ </style> <style name="Theme.SystemUI.Dialog.Control.LockScreen" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar"> - <item name="android:windowAnimationStyle">@style/Animation.Fade</item> + <item name="android:windowAnimationStyle">@style/Animation.ControlDialog</item> <item name="android:windowFullscreen">true</item> <item name="android:windowIsFloating">false</item> - <item name="android:windowBackground">@color/controls_lockscreen_scrim</item> + <item name="android:windowBackground">@null</item> <item name="android:backgroundDimEnabled">true</item> </style> - <style name="Animation.Fade"> - <item name="android:windowEnterAnimation">@android:anim/fade_in</item> - <item name="android:windowExitAnimation">@android:anim/fade_out</item> + <style name="Animation.ControlDialog"> + <item name="android:windowEnterAnimation">@*android:anim/dialog_enter</item> + <item name="android:windowExitAnimation">@*android:anim/dialog_exit</item> </style> <style name="Control" /> diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index 606fd2c1848e..09e9675a3277 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -12,6 +12,15 @@ // 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_packages_SystemUI_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], +} + genrule { name: "statslog-SystemUI-java-gen", tools: ["stats-log-api-gen"], diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java index f6b239e31e99..ebb6e30d4b3b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java @@ -254,6 +254,7 @@ public class Task { public Task(Task other) { this(other.key, other.colorPrimary, other.colorBackground, other.isDockable, other.isLocked, other.taskDescription, other.topActivity); + lastSnapshotData.set(other.lastSnapshotData); } /** @@ -281,6 +282,25 @@ public class Task { : key.baseIntent.getComponent(); } + public void setLastSnapshotData(ActivityManager.RecentTaskInfo rawTask) { + lastSnapshotData.set(rawTask.lastSnapshotData); + } + + /** + * Returns the visible width to height ratio. Returns 0f if snapshot data is not available. + */ + public float getVisibleThumbnailRatio() { + if (lastSnapshotData.taskSize == null || lastSnapshotData.contentInsets == null) { + return 0f; + } + + float availableWidth = lastSnapshotData.taskSize.x - (lastSnapshotData.contentInsets.left + + lastSnapshotData.contentInsets.right); + float availableHeight = lastSnapshotData.taskSize.y - (lastSnapshotData.contentInsets.top + + lastSnapshotData.contentInsets.bottom); + return availableWidth / availableHeight; + } + @Override public boolean equals(Object o) { // Check that the id matches diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java index ffde84128549..259cca8c01e2 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputChannelCompat.java @@ -16,13 +16,12 @@ package com.android.systemui.shared.system; -import android.os.Bundle; +import android.graphics.Matrix; import android.os.Looper; import android.view.BatchedInputEventReceiver; import android.view.Choreographer; import android.view.InputChannel; import android.view.InputEvent; -import android.view.InputEventSender; import android.view.MotionEvent; /** @@ -53,6 +52,12 @@ public class InputChannelCompat { return target.addBatch(src); } + /** @see MotionEvent#createRotateMatrix */ + public static Matrix createRotationMatrix( + /*@Surface.Rotation*/ int rotation, int displayW, int displayH) { + return MotionEvent.createRotateMatrix(rotation, displayW, displayH); + } + /** * @see BatchedInputEventReceiver */ diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index 6c77af7bbddd..c5d54391959a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java @@ -100,4 +100,10 @@ public abstract class TaskStackChangeListener { /** @see ITaskStackListener#onActivityRotation(int)*/ public void onActivityRotation(int displayId) { } + + /** + * Called when the lock task mode changes. See ActivityManager#LOCK_TASK_MODE_* and + * LockTaskController. + */ + public void onLockTaskModeChanged(int mode) { } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index 8f08f5a51143..b3a29a3fec78 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -93,6 +93,7 @@ public class TaskStackChangeListeners { private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 20; private static final int ON_TASK_DESCRIPTION_CHANGED = 21; private static final int ON_ACTIVITY_ROTATION = 22; + private static final int ON_LOCK_TASK_MODE_CHANGED = 23; /** * List of {@link TaskStackChangeListener} registered from {@link #addListener}. @@ -189,7 +190,7 @@ public class TaskStackChangeListeners { } @Override - public void onActivityDismissingDockedStack() { + public void onActivityDismissingDockedTask() { mHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK); } @@ -273,6 +274,11 @@ public class TaskStackChangeListeners { } @Override + public void onLockTaskModeChanged(int mode) { + mHandler.obtainMessage(ON_LOCK_TASK_MODE_CHANGED, mode, 0 /* unused */).sendToTarget(); + } + + @Override public boolean handleMessage(Message msg) { synchronized (mTaskStackListeners) { switch (msg.what) { @@ -421,6 +427,12 @@ public class TaskStackChangeListeners { } break; } + case ON_LOCK_TASK_MODE_CHANGED: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onLockTaskModeChanged(msg.arg1); + } + break; + } } } if (msg.obj instanceof SomeArgs) { diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java index 0a117c17a354..1569fff63453 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java @@ -21,9 +21,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Color; -import android.graphics.Paint; import android.icu.text.NumberFormat; -import android.util.MathUtils; import com.android.settingslib.Utils; import com.android.systemui.R; @@ -94,11 +92,6 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie mStatusBarStateController.removeCallback(mStatusBarStateListener); } - float getClockTextTopPadding() { - Paint.FontMetrics fm = mView.getPaint().getFontMetrics(); - return MathUtils.abs(fm.ascent - fm.top); - } - /** * Updates the time for the view. */ diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierText.java b/packages/SystemUI/src/com/android/keyguard/CarrierText.java index e4f6e131258e..f6b03c1fa013 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierText.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierText.java @@ -24,14 +24,41 @@ import android.util.AttributeSet; import android.view.View; import android.widget.TextView; +import com.android.systemui.Dependency; import com.android.systemui.R; import java.util.Locale; public class CarrierText extends TextView { - private final boolean mShowMissingSim; + private static final boolean DEBUG = KeyguardConstants.DEBUG; + private static final String TAG = "CarrierText"; - private final boolean mShowAirplaneMode; + private static CharSequence mSeparator; + + private boolean mShowMissingSim; + + private boolean mShowAirplaneMode; + private boolean mShouldMarquee; + + private CarrierTextController mCarrierTextController; + + private CarrierTextController.CarrierTextCallback mCarrierTextCallback = + new CarrierTextController.CarrierTextCallback() { + @Override + public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { + setText(info.carrierText); + } + + @Override + public void startedGoingToSleep() { + setSelected(false); + } + + @Override + public void finishedWakingUp() { + setSelected(true); + } + }; public CarrierText(Context context) { this(context, null); @@ -51,6 +78,30 @@ public class CarrierText extends TextView { } setTransformationMethod(new CarrierTextTransformationMethod(mContext, useAllCaps)); } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mSeparator = getResources().getString( + com.android.internal.R.string.kg_text_message_separator); + mCarrierTextController = new CarrierTextController(mContext, mSeparator, mShowAirplaneMode, + mShowMissingSim); + mShouldMarquee = Dependency.get(KeyguardUpdateMonitor.class).isDeviceInteractive(); + setSelected(mShouldMarquee); // Allow marquee to work. + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mCarrierTextController.setListening(mCarrierTextCallback); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mCarrierTextController.setListening(null); + } + @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); @@ -62,15 +113,7 @@ public class CarrierText extends TextView { } } - public boolean getShowAirplaneMode() { - return mShowAirplaneMode; - } - - public boolean getShowMissingSim() { - return mShowMissingSim; - } - - private static class CarrierTextTransformationMethod extends SingleLineTransformationMethod { + private class CarrierTextTransformationMethod extends SingleLineTransformationMethod { private final Locale mLocale; private final boolean mAllCaps; diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java index 46a6d8b82911..b1e14346c3fa 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * 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. @@ -16,61 +16,680 @@ package com.android.keyguard; -import com.android.systemui.util.ViewController; +import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; +import static android.telephony.PhoneStateListener.LISTEN_NONE; + +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import com.android.settingslib.WirelessUtils; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.keyguard.WakefulnessLifecycle; + +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; import javax.inject.Inject; /** - * Controller for {@link CarrierText}. + * Controller that generates text including the carrier names and/or the status of all the SIM + * interfaces in the device. Through a callback, the updates can be retrieved either as a list or + * separated by a given separator {@link CharSequence}. */ -public class CarrierTextController extends ViewController<CarrierText> { - private final CarrierTextManager mCarrierTextManager; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; +public class CarrierTextController { + private static final boolean DEBUG = KeyguardConstants.DEBUG; + private static final String TAG = "CarrierTextController"; - private final CarrierTextManager.CarrierTextCallback mCarrierTextCallback = - new CarrierTextManager.CarrierTextCallback() { + private final boolean mIsEmergencyCallCapable; + private final Handler mMainHandler; + private final Handler mBgHandler; + private boolean mTelephonyCapable; + private boolean mShowMissingSim; + private boolean mShowAirplaneMode; + private final AtomicBoolean mNetworkSupported = new AtomicBoolean(); + @VisibleForTesting + protected KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private WifiManager mWifiManager; + private boolean[] mSimErrorState; + private final int mSimSlotsNumber; + @Nullable // Check for nullability before dispatching + private CarrierTextCallback mCarrierTextCallback; + private Context mContext; + private CharSequence mSeparator; + private WakefulnessLifecycle mWakefulnessLifecycle; + private final WakefulnessLifecycle.Observer mWakefulnessObserver = + new WakefulnessLifecycle.Observer() { @Override - public void updateCarrierInfo(CarrierTextManager.CarrierTextCallbackInfo info) { - mView.setText(info.carrierText); + public void onFinishedWakingUp() { + final CarrierTextCallback callback = mCarrierTextCallback; + if (callback != null) callback.finishedWakingUp(); } @Override - public void startedGoingToSleep() { - mView.setSelected(false); + public void onStartedGoingToSleep() { + final CarrierTextCallback callback = mCarrierTextCallback; + if (callback != null) callback.startedGoingToSleep(); } + }; - @Override - public void finishedWakingUp() { - mView.setSelected(true); + @VisibleForTesting + protected final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onRefreshCarrierInfo() { + if (DEBUG) { + Log.d(TAG, "onRefreshCarrierInfo(), mTelephonyCapable: " + + Boolean.toString(mTelephonyCapable)); + } + updateCarrierText(); + } + + @Override + public void onTelephonyCapable(boolean capable) { + if (DEBUG) { + Log.d(TAG, "onTelephonyCapable() mTelephonyCapable: " + + Boolean.toString(capable)); + } + mTelephonyCapable = capable; + updateCarrierText(); + } + + public void onSimStateChanged(int subId, int slotId, int simState) { + if (slotId < 0 || slotId >= mSimSlotsNumber) { + Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId + + " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable)); + return; + } + + if (DEBUG) Log.d(TAG, "onSimStateChanged: " + getStatusForIccState(simState)); + if (getStatusForIccState(simState) == CarrierTextController.StatusMode.SimIoError) { + mSimErrorState[slotId] = true; + updateCarrierText(); + } else if (mSimErrorState[slotId]) { + mSimErrorState[slotId] = false; + updateCarrierText(); + } + } + }; + + private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + @Override + public void onActiveDataSubscriptionIdChanged(int subId) { + mActiveMobileDataSubscription = subId; + if (mNetworkSupported.get() && mCarrierTextCallback != null) { + updateCarrierText(); + } + } + }; + + /** + * The status of this lock screen. Primarily used for widgets on LockScreen. + */ + private enum StatusMode { + Normal, // Normal case (sim card present, it's not locked) + NetworkLocked, // SIM card is 'network locked'. + SimMissing, // SIM card is missing. + SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access + SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times + SimLocked, // SIM card is currently locked + SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure + SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM. + SimIoError, // SIM card is faulty + SimUnknown // SIM card is unknown + } + + /** + * Controller that provides updates on text with carriers names or SIM status. + * Used by {@link CarrierText}. + * + * @param separator Separator between different parts of the text + */ + public CarrierTextController(Context context, CharSequence separator, boolean showAirplaneMode, + boolean showMissingSim) { + mContext = context; + mIsEmergencyCallCapable = getTelephonyManager().isVoiceCapable(); + + mShowAirplaneMode = showAirplaneMode; + mShowMissingSim = showMissingSim; + + mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + mSeparator = separator; + mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); + mSimSlotsNumber = getTelephonyManager().getSupportedModemCount(); + mSimErrorState = new boolean[mSimSlotsNumber]; + mMainHandler = Dependency.get(Dependency.MAIN_HANDLER); + mBgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER)); + mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); + mBgHandler.post(() -> { + boolean supported = ConnectivityManager.from(mContext).isNetworkSupported( + ConnectivityManager.TYPE_MOBILE); + if (supported && mNetworkSupported.compareAndSet(false, supported)) { + // This will set/remove the listeners appropriately. Note that it will never double + // add the listeners. + handleSetListening(mCarrierTextCallback); + } + }); + } + + private TelephonyManager getTelephonyManager() { + return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + } + + /** + * Checks if there are faulty cards. Adds the text depending on the slot of the card + * + * @param text: current carrier text based on the sim state + * @param carrierNames names order by subscription order + * @param subOrderBySlot array containing the sub index for each slot ID + * @param noSims: whether a valid sim card is inserted + * @return text + */ + private CharSequence updateCarrierTextWithSimIoError(CharSequence text, + CharSequence[] carrierNames, int[] subOrderBySlot, boolean noSims) { + final CharSequence carrier = ""; + CharSequence carrierTextForSimIOError = getCarrierTextForSimState( + TelephonyManager.SIM_STATE_CARD_IO_ERROR, carrier); + // mSimErrorState has the state of each sim indexed by slotID. + for (int index = 0; index < getTelephonyManager().getActiveModemCount(); index++) { + if (!mSimErrorState[index]) { + continue; + } + // In the case when no sim cards are detected but a faulty card is inserted + // overwrite the text and only show "Invalid card" + if (noSims) { + return concatenate(carrierTextForSimIOError, + getContext().getText( + com.android.internal.R.string.emergency_calls_only), + mSeparator); + } else if (subOrderBySlot[index] != -1) { + int subIndex = subOrderBySlot[index]; + // prepend "Invalid card" when faulty card is inserted in slot 0 or 1 + carrierNames[subIndex] = concatenate(carrierTextForSimIOError, + carrierNames[subIndex], + mSeparator); + } else { + // concatenate "Invalid card" when faulty card is inserted in other slot + text = concatenate(text, carrierTextForSimIOError, mSeparator); + } + + } + return text; + } + + /** + * This may be called internally after retrieving the correct value of {@code mNetworkSupported} + * (assumed false to start). In that case, the following happens: + * <ul> + * <li> If there was a registered callback, and the network is supported, it will register + * listeners. + * <li> If there was not a registered callback, it will try to remove unregistered listeners + * which is a no-op + * </ul> + * + * This call will always be processed in a background thread. + */ + private void handleSetListening(CarrierTextCallback callback) { + TelephonyManager telephonyManager = getTelephonyManager(); + if (callback != null) { + mCarrierTextCallback = callback; + if (mNetworkSupported.get()) { + // Keyguard update monitor expects callbacks from main thread + mMainHandler.post(() -> mKeyguardUpdateMonitor.registerCallback(mCallback)); + mWakefulnessLifecycle.addObserver(mWakefulnessObserver); + telephonyManager.listen(mPhoneStateListener, + LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); + } else { + // Don't listen and clear out the text when the device isn't a phone. + mMainHandler.post(() -> callback.updateCarrierInfo( + new CarrierTextCallbackInfo("", null, false, null) + )); + } + } else { + mCarrierTextCallback = null; + mMainHandler.post(() -> mKeyguardUpdateMonitor.removeCallback(mCallback)); + mWakefulnessLifecycle.removeObserver(mWakefulnessObserver); + telephonyManager.listen(mPhoneStateListener, LISTEN_NONE); + } + } + + /** + * Sets the listening status of this controller. If the callback is null, it is set to + * not listening. + * + * @param callback Callback to provide text updates + */ + public void setListening(CarrierTextCallback callback) { + mBgHandler.post(() -> handleSetListening(callback)); + } + + protected List<SubscriptionInfo> getSubscriptionInfo() { + return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false); + } + + protected void updateCarrierText() { + boolean allSimsMissing = true; + boolean anySimReadyAndInService = false; + CharSequence displayText = null; + List<SubscriptionInfo> subs = getSubscriptionInfo(); + + final int numSubs = subs.size(); + final int[] subsIds = new int[numSubs]; + // This array will contain in position i, the index of subscription in slot ID i. + // -1 if no subscription in that slot + final int[] subOrderBySlot = new int[mSimSlotsNumber]; + for (int i = 0; i < mSimSlotsNumber; i++) { + subOrderBySlot[i] = -1; + } + final CharSequence[] carrierNames = new CharSequence[numSubs]; + if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs); + + for (int i = 0; i < numSubs; i++) { + int subId = subs.get(i).getSubscriptionId(); + carrierNames[i] = ""; + subsIds[i] = subId; + subOrderBySlot[subs.get(i).getSimSlotIndex()] = i; + int simState = mKeyguardUpdateMonitor.getSimState(subId); + CharSequence carrierName = subs.get(i).getCarrierName(); + CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName); + if (DEBUG) { + Log.d(TAG, "Handling (subId=" + subId + "): " + simState + " " + carrierName); + } + if (carrierTextForSimState != null) { + allSimsMissing = false; + carrierNames[i] = carrierTextForSimState; + } + if (simState == TelephonyManager.SIM_STATE_READY) { + ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId); + if (ss != null && ss.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE) { + // hack for WFC (IWLAN) not turning off immediately once + // Wi-Fi is disassociated or disabled + if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN + || (mWifiManager.isWifiEnabled() + && mWifiManager.getConnectionInfo() != null + && mWifiManager.getConnectionInfo().getBSSID() != null)) { + if (DEBUG) { + Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + ss); + } + anySimReadyAndInService = true; + } } - }; + } + } + // Only create "No SIM card" if no cards with CarrierName && no wifi when some sim is READY + // This condition will also be true always when numSubs == 0 + if (allSimsMissing && !anySimReadyAndInService) { + if (numSubs != 0) { + // Shows "No SIM card | Emergency calls only" on devices that are voice-capable. + // This depends on mPlmn containing the text "Emergency calls only" when the radio + // has some connectivity. Otherwise, it should be null or empty and just show + // "No SIM card" + // Grab the first subscripton, because they all should contain the emergency text, + // described above. + displayText = makeCarrierStringOnEmergencyCapable( + getMissingSimMessage(), subs.get(0).getCarrierName()); + } else { + // We don't have a SubscriptionInfo to get the emergency calls only from. + // Grab it from the old sticky broadcast if possible instead. We can use it + // here because no subscriptions are active, so we don't have + // to worry about MSIM clashing. + CharSequence text = + getContext().getText(com.android.internal.R.string.emergency_calls_only); + Intent i = getContext().registerReceiver(null, + new IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)); + if (i != null) { + String spn = ""; + String plmn = ""; + if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)) { + spn = i.getStringExtra(TelephonyManager.EXTRA_SPN); + } + if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)) { + plmn = i.getStringExtra(TelephonyManager.EXTRA_PLMN); + } + if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn); + if (Objects.equals(plmn, spn)) { + text = plmn; + } else { + text = concatenate(plmn, spn, mSeparator); + } + } + displayText = makeCarrierStringOnEmergencyCapable(getMissingSimMessage(), text); + } + } + + if (TextUtils.isEmpty(displayText)) displayText = joinNotEmpty(mSeparator, carrierNames); + + displayText = updateCarrierTextWithSimIoError(displayText, carrierNames, subOrderBySlot, + allSimsMissing); + + boolean airplaneMode = false; + // APM (airplane mode) != no carrier state. There are carrier services + // (e.g. WFC = Wi-Fi calling) which may operate in APM. + if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) { + displayText = getAirplaneModeMessage(); + airplaneMode = true; + } + + final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo( + displayText, + carrierNames, + !allSimsMissing, + subsIds, + airplaneMode); + postToCallback(info); + } - @Inject - public CarrierTextController(CarrierText view, - CarrierTextManager.Builder carrierTextManagerBuilder, - KeyguardUpdateMonitor keyguardUpdateMonitor) { - super(view); + @VisibleForTesting + protected void postToCallback(CarrierTextCallbackInfo info) { + final CarrierTextCallback callback = mCarrierTextCallback; + if (callback != null) { + mMainHandler.post(() -> callback.updateCarrierInfo(info)); + } + } - mCarrierTextManager = carrierTextManagerBuilder - .setShowAirplaneMode(mView.getShowAirplaneMode()) - .setShowMissingSim(mView.getShowMissingSim()) - .build(); - mKeyguardUpdateMonitor = keyguardUpdateMonitor; + private Context getContext() { + return mContext; } - @Override - protected void onInit() { - super.onInit(); - mView.setSelected(mKeyguardUpdateMonitor.isDeviceInteractive()); + private String getMissingSimMessage() { + return mShowMissingSim && mTelephonyCapable + ? getContext().getString(R.string.keyguard_missing_sim_message_short) : ""; } - @Override - protected void onViewAttached() { - mCarrierTextManager.setListening(mCarrierTextCallback); + private String getAirplaneModeMessage() { + return mShowAirplaneMode + ? getContext().getString(R.string.airplane_mode) : ""; } - @Override - protected void onViewDetached() { - mCarrierTextManager.setListening(null); + /** + * Top-level function for creating carrier text. Makes text based on simState, PLMN + * and SPN as well as device capabilities, such as being emergency call capable. + * + * @return Carrier text if not in missing state, null otherwise. + */ + private CharSequence getCarrierTextForSimState(int simState, CharSequence text) { + CharSequence carrierText = null; + CarrierTextController.StatusMode status = getStatusForIccState(simState); + switch (status) { + case Normal: + carrierText = text; + break; + + case SimNotReady: + // Null is reserved for denoting missing, in this case we have nothing to display. + carrierText = ""; // nothing to display yet. + break; + + case NetworkLocked: + carrierText = makeCarrierStringOnEmergencyCapable( + mContext.getText(R.string.keyguard_network_locked_message), text); + break; + + case SimMissing: + carrierText = null; + break; + + case SimPermDisabled: + carrierText = makeCarrierStringOnEmergencyCapable( + getContext().getText( + R.string.keyguard_permanent_disabled_sim_message_short), + text); + break; + + case SimMissingLocked: + carrierText = null; + break; + + case SimLocked: + carrierText = makeCarrierStringOnLocked( + getContext().getText(R.string.keyguard_sim_locked_message), + text); + break; + + case SimPukLocked: + carrierText = makeCarrierStringOnLocked( + getContext().getText(R.string.keyguard_sim_puk_locked_message), + text); + break; + case SimIoError: + carrierText = makeCarrierStringOnEmergencyCapable( + getContext().getText(R.string.keyguard_sim_error_message_short), + text); + break; + case SimUnknown: + carrierText = null; + break; + } + + return carrierText; + } + + /* + * Add emergencyCallMessage to carrier string only if phone supports emergency calls. + */ + private CharSequence makeCarrierStringOnEmergencyCapable( + CharSequence simMessage, CharSequence emergencyCallMessage) { + if (mIsEmergencyCallCapable) { + return concatenate(simMessage, emergencyCallMessage, mSeparator); + } + return simMessage; + } + + /* + * Add "SIM card is locked" in parenthesis after carrier name, so it is easily associated in + * DSDS + */ + private CharSequence makeCarrierStringOnLocked(CharSequence simMessage, + CharSequence carrierName) { + final boolean simMessageValid = !TextUtils.isEmpty(simMessage); + final boolean carrierNameValid = !TextUtils.isEmpty(carrierName); + if (simMessageValid && carrierNameValid) { + return mContext.getString(R.string.keyguard_carrier_name_with_sim_locked_template, + carrierName, simMessage); + } else if (simMessageValid) { + return simMessage; + } else if (carrierNameValid) { + return carrierName; + } else { + return ""; + } + } + + /** + * Determine the current status of the lock screen given the SIM state and other stuff. + */ + private CarrierTextController.StatusMode getStatusForIccState(int simState) { + final boolean missingAndNotProvisioned = + !mKeyguardUpdateMonitor.isDeviceProvisioned() + && (simState == TelephonyManager.SIM_STATE_ABSENT + || simState == TelephonyManager.SIM_STATE_PERM_DISABLED); + + // Assume we're NETWORK_LOCKED if not provisioned + simState = missingAndNotProvisioned ? TelephonyManager.SIM_STATE_NETWORK_LOCKED : simState; + switch (simState) { + case TelephonyManager.SIM_STATE_ABSENT: + return CarrierTextController.StatusMode.SimMissing; + case TelephonyManager.SIM_STATE_NETWORK_LOCKED: + return CarrierTextController.StatusMode.SimMissingLocked; + case TelephonyManager.SIM_STATE_NOT_READY: + return CarrierTextController.StatusMode.SimNotReady; + case TelephonyManager.SIM_STATE_PIN_REQUIRED: + return CarrierTextController.StatusMode.SimLocked; + case TelephonyManager.SIM_STATE_PUK_REQUIRED: + return CarrierTextController.StatusMode.SimPukLocked; + case TelephonyManager.SIM_STATE_READY: + return CarrierTextController.StatusMode.Normal; + case TelephonyManager.SIM_STATE_PERM_DISABLED: + return CarrierTextController.StatusMode.SimPermDisabled; + case TelephonyManager.SIM_STATE_UNKNOWN: + return CarrierTextController.StatusMode.SimUnknown; + case TelephonyManager.SIM_STATE_CARD_IO_ERROR: + return CarrierTextController.StatusMode.SimIoError; + } + return CarrierTextController.StatusMode.SimUnknown; + } + + private static CharSequence concatenate(CharSequence plmn, CharSequence spn, + CharSequence separator) { + final boolean plmnValid = !TextUtils.isEmpty(plmn); + final boolean spnValid = !TextUtils.isEmpty(spn); + if (plmnValid && spnValid) { + return new StringBuilder().append(plmn).append(separator).append(spn).toString(); + } else if (plmnValid) { + return plmn; + } else if (spnValid) { + return spn; + } else { + return ""; + } + } + + /** + * Joins the strings in a sequence using a separator. Empty strings are discarded with no extra + * separator added so there are no extra separators that are not needed. + */ + private static CharSequence joinNotEmpty(CharSequence separator, CharSequence[] sequences) { + int length = sequences.length; + if (length == 0) return ""; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + if (!TextUtils.isEmpty(sequences[i])) { + if (!TextUtils.isEmpty(sb)) { + sb.append(separator); + } + sb.append(sequences[i]); + } + } + return sb.toString(); + } + + private static List<CharSequence> append(List<CharSequence> list, CharSequence string) { + if (!TextUtils.isEmpty(string)) { + list.add(string); + } + return list; + } + + private CharSequence getCarrierHelpTextForSimState(int simState, + String plmn, String spn) { + int carrierHelpTextId = 0; + CarrierTextController.StatusMode status = getStatusForIccState(simState); + switch (status) { + case NetworkLocked: + carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled; + break; + + case SimMissing: + carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long; + break; + + case SimPermDisabled: + carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions; + break; + + case SimMissingLocked: + carrierHelpTextId = R.string.keyguard_missing_sim_instructions; + break; + + case Normal: + case SimLocked: + case SimPukLocked: + break; + } + + return mContext.getText(carrierHelpTextId); + } + + public static class Builder { + private final Context mContext; + private final String mSeparator; + private boolean mShowAirplaneMode; + private boolean mShowMissingSim; + + @Inject + public Builder(Context context, @Main Resources resources) { + mContext = context; + mSeparator = resources.getString( + com.android.internal.R.string.kg_text_message_separator); + } + + + public Builder setShowAirplaneMode(boolean showAirplaneMode) { + mShowAirplaneMode = showAirplaneMode; + return this; + } + + public Builder setShowMissingSim(boolean showMissingSim) { + mShowMissingSim = showMissingSim; + return this; + } + + public CarrierTextController build() { + return new CarrierTextController( + mContext, mSeparator, mShowAirplaneMode, mShowMissingSim); + } + } + /** + * Data structure for passing information to CarrierTextController subscribers + */ + public static final class CarrierTextCallbackInfo { + public final CharSequence carrierText; + public final CharSequence[] listOfCarriers; + public final boolean anySimReady; + public final int[] subscriptionIds; + public boolean airplaneMode; + + @VisibleForTesting + public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, + boolean anySimReady, int[] subscriptionIds) { + this(carrierText, listOfCarriers, anySimReady, subscriptionIds, false); + } + + @VisibleForTesting + public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, + boolean anySimReady, int[] subscriptionIds, boolean airplaneMode) { + this.carrierText = carrierText; + this.listOfCarriers = listOfCarriers; + this.anySimReady = anySimReady; + this.subscriptionIds = subscriptionIds; + this.airplaneMode = airplaneMode; + } + } + + /** + * Callback to communicate to Views + */ + public interface CarrierTextCallback { + /** + * Provides updated carrier information. + */ + default void updateCarrierInfo(CarrierTextCallbackInfo info) {}; + + /** + * Notifies the View that the device is going to sleep + */ + default void startedGoingToSleep() {}; + + /** + * Notifies the View that the device finished waking up + */ + default void finishedWakingUp() {}; } } diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java deleted file mode 100644 index 87b01e8671f0..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java +++ /dev/null @@ -1,720 +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.keyguard; - -import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; -import static android.telephony.PhoneStateListener.LISTEN_NONE; - -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.wifi.WifiManager; -import android.os.Handler; -import android.telephony.PhoneStateListener; -import android.telephony.ServiceState; -import android.telephony.SubscriptionInfo; -import android.telephony.TelephonyManager; -import android.text.TextUtils; -import android.util.Log; - -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - -import com.android.settingslib.WirelessUtils; -import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.Background; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.keyguard.WakefulnessLifecycle; - -import java.util.List; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.inject.Inject; - -/** - * Controller that generates text including the carrier names and/or the status of all the SIM - * interfaces in the device. Through a callback, the updates can be retrieved either as a list or - * separated by a given separator {@link CharSequence}. - */ -public class CarrierTextManager { - private static final boolean DEBUG = KeyguardConstants.DEBUG; - private static final String TAG = "CarrierTextController"; - - private final boolean mIsEmergencyCallCapable; - private final Handler mMainHandler; - private final Handler mBgHandler; - private boolean mTelephonyCapable; - private final boolean mShowMissingSim; - private final boolean mShowAirplaneMode; - private final AtomicBoolean mNetworkSupported = new AtomicBoolean(); - @VisibleForTesting - protected KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final WifiManager mWifiManager; - private final boolean[] mSimErrorState; - private final int mSimSlotsNumber; - @Nullable // Check for nullability before dispatching - private CarrierTextCallback mCarrierTextCallback; - private final Context mContext; - private final TelephonyManager mTelephonyManager; - private final CharSequence mSeparator; - private final WakefulnessLifecycle mWakefulnessLifecycle; - private final WakefulnessLifecycle.Observer mWakefulnessObserver = - new WakefulnessLifecycle.Observer() { - @Override - public void onFinishedWakingUp() { - final CarrierTextCallback callback = mCarrierTextCallback; - if (callback != null) callback.finishedWakingUp(); - } - - @Override - public void onStartedGoingToSleep() { - final CarrierTextCallback callback = mCarrierTextCallback; - if (callback != null) callback.startedGoingToSleep(); - } - }; - - @VisibleForTesting - protected final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onRefreshCarrierInfo() { - if (DEBUG) { - Log.d(TAG, "onRefreshCarrierInfo(), mTelephonyCapable: " - + Boolean.toString(mTelephonyCapable)); - } - updateCarrierText(); - } - - @Override - public void onTelephonyCapable(boolean capable) { - if (DEBUG) { - Log.d(TAG, "onTelephonyCapable() mTelephonyCapable: " - + Boolean.toString(capable)); - } - mTelephonyCapable = capable; - updateCarrierText(); - } - - public void onSimStateChanged(int subId, int slotId, int simState) { - if (slotId < 0 || slotId >= mSimSlotsNumber) { - Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId - + " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable)); - return; - } - - if (DEBUG) Log.d(TAG, "onSimStateChanged: " + getStatusForIccState(simState)); - if (getStatusForIccState(simState) == CarrierTextManager.StatusMode.SimIoError) { - mSimErrorState[slotId] = true; - updateCarrierText(); - } else if (mSimErrorState[slotId]) { - mSimErrorState[slotId] = false; - updateCarrierText(); - } - } - }; - - private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { - @Override - public void onActiveDataSubscriptionIdChanged(int subId) { - if (mNetworkSupported.get() && mCarrierTextCallback != null) { - updateCarrierText(); - } - } - }; - - /** - * The status of this lock screen. Primarily used for widgets on LockScreen. - */ - private enum StatusMode { - Normal, // Normal case (sim card present, it's not locked) - NetworkLocked, // SIM card is 'network locked'. - SimMissing, // SIM card is missing. - SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access - SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times - SimLocked, // SIM card is currently locked - SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure - SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM. - SimIoError, // SIM card is faulty - SimUnknown // SIM card is unknown - } - - /** - * Controller that provides updates on text with carriers names or SIM status. - * Used by {@link CarrierText}. - * - * @param separator Separator between different parts of the text - */ - private CarrierTextManager(Context context, CharSequence separator, boolean showAirplaneMode, - boolean showMissingSim, @Nullable WifiManager wifiManager, - ConnectivityManager connectivityManager, TelephonyManager telephonyManager, - WakefulnessLifecycle wakefulnessLifecycle, @Main Handler mainHandler, - @Background Handler bgHandler, KeyguardUpdateMonitor keyguardUpdateMonitor) { - mContext = context; - mIsEmergencyCallCapable = telephonyManager.isVoiceCapable(); - - mShowAirplaneMode = showAirplaneMode; - mShowMissingSim = showMissingSim; - - mWifiManager = wifiManager; - mTelephonyManager = telephonyManager; - mSeparator = separator; - mWakefulnessLifecycle = wakefulnessLifecycle; - mSimSlotsNumber = getTelephonyManager().getSupportedModemCount(); - mSimErrorState = new boolean[mSimSlotsNumber]; - mMainHandler = mainHandler; - mBgHandler = bgHandler; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mBgHandler.post(() -> { - boolean supported = connectivityManager.isNetworkSupported( - ConnectivityManager.TYPE_MOBILE); - if (supported && mNetworkSupported.compareAndSet(false, supported)) { - // This will set/remove the listeners appropriately. Note that it will never double - // add the listeners. - handleSetListening(mCarrierTextCallback); - } - }); - } - - private TelephonyManager getTelephonyManager() { - return mTelephonyManager; - } - - /** - * Checks if there are faulty cards. Adds the text depending on the slot of the card - * - * @param text: current carrier text based on the sim state - * @param carrierNames names order by subscription order - * @param subOrderBySlot array containing the sub index for each slot ID - * @param noSims: whether a valid sim card is inserted - * @return text - */ - private CharSequence updateCarrierTextWithSimIoError(CharSequence text, - CharSequence[] carrierNames, int[] subOrderBySlot, boolean noSims) { - final CharSequence carrier = ""; - CharSequence carrierTextForSimIOError = getCarrierTextForSimState( - TelephonyManager.SIM_STATE_CARD_IO_ERROR, carrier); - // mSimErrorState has the state of each sim indexed by slotID. - for (int index = 0; index < getTelephonyManager().getActiveModemCount(); index++) { - if (!mSimErrorState[index]) { - continue; - } - // In the case when no sim cards are detected but a faulty card is inserted - // overwrite the text and only show "Invalid card" - if (noSims) { - return concatenate(carrierTextForSimIOError, - getContext().getText( - com.android.internal.R.string.emergency_calls_only), - mSeparator); - } else if (subOrderBySlot[index] != -1) { - int subIndex = subOrderBySlot[index]; - // prepend "Invalid card" when faulty card is inserted in slot 0 or 1 - carrierNames[subIndex] = concatenate(carrierTextForSimIOError, - carrierNames[subIndex], - mSeparator); - } else { - // concatenate "Invalid card" when faulty card is inserted in other slot - text = concatenate(text, carrierTextForSimIOError, mSeparator); - } - - } - return text; - } - - /** - * This may be called internally after retrieving the correct value of {@code mNetworkSupported} - * (assumed false to start). In that case, the following happens: - * <ul> - * <li> If there was a registered callback, and the network is supported, it will register - * listeners. - * <li> If there was not a registered callback, it will try to remove unregistered listeners - * which is a no-op - * </ul> - * - * This call will always be processed in a background thread. - */ - private void handleSetListening(CarrierTextCallback callback) { - TelephonyManager telephonyManager = getTelephonyManager(); - if (callback != null) { - mCarrierTextCallback = callback; - if (mNetworkSupported.get()) { - // Keyguard update monitor expects callbacks from main thread - mMainHandler.post(() -> mKeyguardUpdateMonitor.registerCallback(mCallback)); - mWakefulnessLifecycle.addObserver(mWakefulnessObserver); - telephonyManager.listen(mPhoneStateListener, - LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE); - } else { - // Don't listen and clear out the text when the device isn't a phone. - mMainHandler.post(() -> callback.updateCarrierInfo( - new CarrierTextCallbackInfo("", null, false, null) - )); - } - } else { - mCarrierTextCallback = null; - mMainHandler.post(() -> mKeyguardUpdateMonitor.removeCallback(mCallback)); - mWakefulnessLifecycle.removeObserver(mWakefulnessObserver); - telephonyManager.listen(mPhoneStateListener, LISTEN_NONE); - } - } - - /** - * Sets the listening status of this controller. If the callback is null, it is set to - * not listening. - * - * @param callback Callback to provide text updates - */ - public void setListening(CarrierTextCallback callback) { - mBgHandler.post(() -> handleSetListening(callback)); - } - - protected List<SubscriptionInfo> getSubscriptionInfo() { - return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false); - } - - protected void updateCarrierText() { - boolean allSimsMissing = true; - boolean anySimReadyAndInService = false; - CharSequence displayText = null; - List<SubscriptionInfo> subs = getSubscriptionInfo(); - - final int numSubs = subs.size(); - final int[] subsIds = new int[numSubs]; - // This array will contain in position i, the index of subscription in slot ID i. - // -1 if no subscription in that slot - final int[] subOrderBySlot = new int[mSimSlotsNumber]; - for (int i = 0; i < mSimSlotsNumber; i++) { - subOrderBySlot[i] = -1; - } - final CharSequence[] carrierNames = new CharSequence[numSubs]; - if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs); - - for (int i = 0; i < numSubs; i++) { - int subId = subs.get(i).getSubscriptionId(); - carrierNames[i] = ""; - subsIds[i] = subId; - subOrderBySlot[subs.get(i).getSimSlotIndex()] = i; - int simState = mKeyguardUpdateMonitor.getSimState(subId); - CharSequence carrierName = subs.get(i).getCarrierName(); - CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName); - if (DEBUG) { - Log.d(TAG, "Handling (subId=" + subId + "): " + simState + " " + carrierName); - } - if (carrierTextForSimState != null) { - allSimsMissing = false; - carrierNames[i] = carrierTextForSimState; - } - if (simState == TelephonyManager.SIM_STATE_READY) { - ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId); - if (ss != null && ss.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE) { - // hack for WFC (IWLAN) not turning off immediately once - // Wi-Fi is disassociated or disabled - if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN - || (mWifiManager != null && mWifiManager.isWifiEnabled() - && mWifiManager.getConnectionInfo() != null - && mWifiManager.getConnectionInfo().getBSSID() != null)) { - if (DEBUG) { - Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + ss); - } - anySimReadyAndInService = true; - } - } - } - } - // Only create "No SIM card" if no cards with CarrierName && no wifi when some sim is READY - // This condition will also be true always when numSubs == 0 - if (allSimsMissing && !anySimReadyAndInService) { - if (numSubs != 0) { - // Shows "No SIM card | Emergency calls only" on devices that are voice-capable. - // This depends on mPlmn containing the text "Emergency calls only" when the radio - // has some connectivity. Otherwise, it should be null or empty and just show - // "No SIM card" - // Grab the first subscripton, because they all should contain the emergency text, - // described above. - displayText = makeCarrierStringOnEmergencyCapable( - getMissingSimMessage(), subs.get(0).getCarrierName()); - } else { - // We don't have a SubscriptionInfo to get the emergency calls only from. - // Grab it from the old sticky broadcast if possible instead. We can use it - // here because no subscriptions are active, so we don't have - // to worry about MSIM clashing. - CharSequence text = - getContext().getText(com.android.internal.R.string.emergency_calls_only); - Intent i = getContext().registerReceiver(null, - new IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)); - if (i != null) { - String spn = ""; - String plmn = ""; - if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)) { - spn = i.getStringExtra(TelephonyManager.EXTRA_SPN); - } - if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)) { - plmn = i.getStringExtra(TelephonyManager.EXTRA_PLMN); - } - if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn); - if (Objects.equals(plmn, spn)) { - text = plmn; - } else { - text = concatenate(plmn, spn, mSeparator); - } - } - displayText = makeCarrierStringOnEmergencyCapable(getMissingSimMessage(), text); - } - } - - if (TextUtils.isEmpty(displayText)) displayText = joinNotEmpty(mSeparator, carrierNames); - - displayText = updateCarrierTextWithSimIoError(displayText, carrierNames, subOrderBySlot, - allSimsMissing); - - boolean airplaneMode = false; - // APM (airplane mode) != no carrier state. There are carrier services - // (e.g. WFC = Wi-Fi calling) which may operate in APM. - if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) { - displayText = getAirplaneModeMessage(); - airplaneMode = true; - } - - final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo( - displayText, - carrierNames, - !allSimsMissing, - subsIds, - airplaneMode); - postToCallback(info); - } - - @VisibleForTesting - protected void postToCallback(CarrierTextCallbackInfo info) { - final CarrierTextCallback callback = mCarrierTextCallback; - if (callback != null) { - mMainHandler.post(() -> callback.updateCarrierInfo(info)); - } - } - - private Context getContext() { - return mContext; - } - - private String getMissingSimMessage() { - return mShowMissingSim && mTelephonyCapable - ? getContext().getString(R.string.keyguard_missing_sim_message_short) : ""; - } - - private String getAirplaneModeMessage() { - return mShowAirplaneMode - ? getContext().getString(R.string.airplane_mode) : ""; - } - - /** - * Top-level function for creating carrier text. Makes text based on simState, PLMN - * and SPN as well as device capabilities, such as being emergency call capable. - * - * @return Carrier text if not in missing state, null otherwise. - */ - private CharSequence getCarrierTextForSimState(int simState, CharSequence text) { - CharSequence carrierText = null; - CarrierTextManager.StatusMode status = getStatusForIccState(simState); - switch (status) { - case Normal: - carrierText = text; - break; - - case SimNotReady: - // Null is reserved for denoting missing, in this case we have nothing to display. - carrierText = ""; // nothing to display yet. - break; - - case NetworkLocked: - carrierText = makeCarrierStringOnEmergencyCapable( - mContext.getText(R.string.keyguard_network_locked_message), text); - break; - - case SimMissing: - carrierText = null; - break; - - case SimPermDisabled: - carrierText = makeCarrierStringOnEmergencyCapable( - getContext().getText( - R.string.keyguard_permanent_disabled_sim_message_short), - text); - break; - - case SimMissingLocked: - carrierText = null; - break; - - case SimLocked: - carrierText = makeCarrierStringOnLocked( - getContext().getText(R.string.keyguard_sim_locked_message), - text); - break; - - case SimPukLocked: - carrierText = makeCarrierStringOnLocked( - getContext().getText(R.string.keyguard_sim_puk_locked_message), - text); - break; - case SimIoError: - carrierText = makeCarrierStringOnEmergencyCapable( - getContext().getText(R.string.keyguard_sim_error_message_short), - text); - break; - case SimUnknown: - carrierText = null; - break; - } - - return carrierText; - } - - /* - * Add emergencyCallMessage to carrier string only if phone supports emergency calls. - */ - private CharSequence makeCarrierStringOnEmergencyCapable( - CharSequence simMessage, CharSequence emergencyCallMessage) { - if (mIsEmergencyCallCapable) { - return concatenate(simMessage, emergencyCallMessage, mSeparator); - } - return simMessage; - } - - /* - * Add "SIM card is locked" in parenthesis after carrier name, so it is easily associated in - * DSDS - */ - private CharSequence makeCarrierStringOnLocked(CharSequence simMessage, - CharSequence carrierName) { - final boolean simMessageValid = !TextUtils.isEmpty(simMessage); - final boolean carrierNameValid = !TextUtils.isEmpty(carrierName); - if (simMessageValid && carrierNameValid) { - return mContext.getString(R.string.keyguard_carrier_name_with_sim_locked_template, - carrierName, simMessage); - } else if (simMessageValid) { - return simMessage; - } else if (carrierNameValid) { - return carrierName; - } else { - return ""; - } - } - - /** - * Determine the current status of the lock screen given the SIM state and other stuff. - */ - private CarrierTextManager.StatusMode getStatusForIccState(int simState) { - final boolean missingAndNotProvisioned = - !mKeyguardUpdateMonitor.isDeviceProvisioned() - && (simState == TelephonyManager.SIM_STATE_ABSENT - || simState == TelephonyManager.SIM_STATE_PERM_DISABLED); - - // Assume we're NETWORK_LOCKED if not provisioned - simState = missingAndNotProvisioned ? TelephonyManager.SIM_STATE_NETWORK_LOCKED : simState; - switch (simState) { - case TelephonyManager.SIM_STATE_ABSENT: - return CarrierTextManager.StatusMode.SimMissing; - case TelephonyManager.SIM_STATE_NETWORK_LOCKED: - return CarrierTextManager.StatusMode.SimMissingLocked; - case TelephonyManager.SIM_STATE_NOT_READY: - return CarrierTextManager.StatusMode.SimNotReady; - case TelephonyManager.SIM_STATE_PIN_REQUIRED: - return CarrierTextManager.StatusMode.SimLocked; - case TelephonyManager.SIM_STATE_PUK_REQUIRED: - return CarrierTextManager.StatusMode.SimPukLocked; - case TelephonyManager.SIM_STATE_READY: - return CarrierTextManager.StatusMode.Normal; - case TelephonyManager.SIM_STATE_PERM_DISABLED: - return CarrierTextManager.StatusMode.SimPermDisabled; - case TelephonyManager.SIM_STATE_UNKNOWN: - return CarrierTextManager.StatusMode.SimUnknown; - case TelephonyManager.SIM_STATE_CARD_IO_ERROR: - return CarrierTextManager.StatusMode.SimIoError; - } - return CarrierTextManager.StatusMode.SimUnknown; - } - - private static CharSequence concatenate(CharSequence plmn, CharSequence spn, - CharSequence separator) { - final boolean plmnValid = !TextUtils.isEmpty(plmn); - final boolean spnValid = !TextUtils.isEmpty(spn); - if (plmnValid && spnValid) { - return new StringBuilder().append(plmn).append(separator).append(spn).toString(); - } else if (plmnValid) { - return plmn; - } else if (spnValid) { - return spn; - } else { - return ""; - } - } - - /** - * Joins the strings in a sequence using a separator. Empty strings are discarded with no extra - * separator added so there are no extra separators that are not needed. - */ - private static CharSequence joinNotEmpty(CharSequence separator, CharSequence[] sequences) { - int length = sequences.length; - if (length == 0) return ""; - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < length; i++) { - if (!TextUtils.isEmpty(sequences[i])) { - if (!TextUtils.isEmpty(sb)) { - sb.append(separator); - } - sb.append(sequences[i]); - } - } - return sb.toString(); - } - - private static List<CharSequence> append(List<CharSequence> list, CharSequence string) { - if (!TextUtils.isEmpty(string)) { - list.add(string); - } - return list; - } - - private CharSequence getCarrierHelpTextForSimState(int simState, - String plmn, String spn) { - int carrierHelpTextId = 0; - CarrierTextManager.StatusMode status = getStatusForIccState(simState); - switch (status) { - case NetworkLocked: - carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled; - break; - - case SimMissing: - carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long; - break; - - case SimPermDisabled: - carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions; - break; - - case SimMissingLocked: - carrierHelpTextId = R.string.keyguard_missing_sim_instructions; - break; - - case Normal: - case SimLocked: - case SimPukLocked: - break; - } - - return mContext.getText(carrierHelpTextId); - } - - /** Injectable Buildeer for {@#link CarrierTextManager}. */ - public static class Builder { - private final Context mContext; - private final String mSeparator; - private final WifiManager mWifiManager; - private final ConnectivityManager mConnectivityManager; - private final TelephonyManager mTelephonyManager; - private final WakefulnessLifecycle mWakefulnessLifecycle; - private final Handler mMainHandler; - private final Handler mBgHandler; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private boolean mShowAirplaneMode; - private boolean mShowMissingSim; - - @Inject - public Builder(Context context, @Main Resources resources, - @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager, - TelephonyManager telephonyManager, WakefulnessLifecycle wakefulnessLifecycle, - @Main Handler mainHandler, @Background Handler bgHandler, - KeyguardUpdateMonitor keyguardUpdateMonitor) { - mContext = context; - mSeparator = resources.getString( - com.android.internal.R.string.kg_text_message_separator); - mWifiManager = wifiManager; - mConnectivityManager = connectivityManager; - mTelephonyManager = telephonyManager; - mWakefulnessLifecycle = wakefulnessLifecycle; - mMainHandler = mainHandler; - mBgHandler = bgHandler; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - } - - /** */ - public Builder setShowAirplaneMode(boolean showAirplaneMode) { - mShowAirplaneMode = showAirplaneMode; - return this; - } - - /** */ - public Builder setShowMissingSim(boolean showMissingSim) { - mShowMissingSim = showMissingSim; - return this; - } - - /** Create a CarrierTextManager. */ - public CarrierTextManager build() { - return new CarrierTextManager( - mContext, mSeparator, mShowAirplaneMode, mShowMissingSim, mWifiManager, - mConnectivityManager, mTelephonyManager, mWakefulnessLifecycle, mMainHandler, - mBgHandler, mKeyguardUpdateMonitor); - } - } - /** - * Data structure for passing information to CarrierTextController subscribers - */ - public static final class CarrierTextCallbackInfo { - public final CharSequence carrierText; - public final CharSequence[] listOfCarriers; - public final boolean anySimReady; - public final int[] subscriptionIds; - public boolean airplaneMode; - - @VisibleForTesting - public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, - boolean anySimReady, int[] subscriptionIds) { - this(carrierText, listOfCarriers, anySimReady, subscriptionIds, false); - } - - @VisibleForTesting - public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, - boolean anySimReady, int[] subscriptionIds, boolean airplaneMode) { - this.carrierText = carrierText; - this.listOfCarriers = listOfCarriers; - this.anySimReady = anySimReady; - this.subscriptionIds = subscriptionIds; - this.airplaneMode = airplaneMode; - } - } - - /** - * Callback to communicate to Views - */ - public interface CarrierTextCallback { - /** - * Provides updated carrier information. - */ - default void updateCarrierInfo(CarrierTextCallbackInfo info) {}; - - /** - * Notifies the View that the device is going to sleep - */ - default void startedGoingToSleep() {}; - - /** - * Notifies the View that the device finished waking up - */ - default void finishedWakingUp() {}; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java index f01b67b7b5c2..945c9c499401 100644 --- a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java +++ b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java @@ -20,6 +20,7 @@ import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import android.hardware.biometrics.BiometricSourceType; import android.view.View; +import android.view.ViewGroup; import androidx.annotation.NonNull; @@ -73,13 +74,15 @@ public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> i @Override protected void onViewAttached() { - mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); mIsBouncerShowing = mKeyguardViewController.isBouncerShowing(); - - mStatusBarStateController.addCallback(mStatusBarStateListener); mIsKeyguardShowing = mStatusBarStateController.getState() == StatusBarState.KEYGUARD; mIsDozing = mStatusBarStateController.isDozing(); + mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning(); mAuthenticated = false; + updateButtonVisibility(); + + mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); + mStatusBarStateController.addCallback(mStatusBarStateListener); } @Override @@ -88,6 +91,15 @@ public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> i mStatusBarStateController.removeCallback(mStatusBarStateListener); } + /** + * Call when this controller is no longer needed. This will remove the view from its parent. + */ + public void destroy() { + if (mView != null && mView.getParent() != null) { + ((ViewGroup) mView.getParent()).removeView(mView); + } + } + private void updateButtonVisibility() { mShowButton = !mAuthenticated && !mIsDozing && mIsKeyguardShowing && !mIsBouncerShowing && !mRunningFPS; @@ -143,6 +155,14 @@ public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> i public void onBiometricRunningStateChanged(boolean running, BiometricSourceType biometricSourceType) { mRunningFPS = running && biometricSourceType == FINGERPRINT; + mAuthenticated &= !mRunningFPS; + updateButtonVisibility(); + } + + @Override + public void onBiometricAuthenticated(int userId, + BiometricSourceType biometricSourceType, boolean isStrongBiometric) { + mAuthenticated = true; updateButtonVisibility(); } diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java index c4b02f62f291..707ee298a55a 100644 --- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java +++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java @@ -16,16 +16,34 @@ package com.android.keyguard; +import static com.android.systemui.DejankUtils.whitelistIpcs; + +import android.app.ActivityOptions; +import android.app.ActivityTaskManager; import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.telecom.TelecomManager; +import android.telephony.TelephonyManager; import android.util.AttributeSet; +import android.util.Log; +import android.util.Slog; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.widget.Button; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.widget.LockPatternUtils; import com.android.settingslib.Utils; +import com.android.systemui.Dependency; +import com.android.systemui.util.EmergencyDialerConstants; /** * This class implements a smart emergency button that updates itself based @@ -35,14 +53,34 @@ import com.android.settingslib.Utils; */ public class EmergencyButton extends Button { + private static final String LOG_TAG = "EmergencyButton"; private final EmergencyAffordanceManager mEmergencyAffordanceManager; private int mDownX; private int mDownY; + KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { + + @Override + public void onSimStateChanged(int subId, int slotId, int simState) { + updateEmergencyCallButton(); + } + + @Override + public void onPhoneStateChanged(int phoneState) { + updateEmergencyCallButton(); + } + }; private boolean mLongPressWasDragged; + public interface EmergencyButtonCallback { + public void onEmergencyButtonClickedWhenInCall(); + } + private LockPatternUtils mLockPatternUtils; + private PowerManager mPowerManager; + private EmergencyButtonCallback mEmergencyButtonCallback; + private final boolean mIsVoiceCapable; private final boolean mEnableEmergencyCallWhileSimLocked; public EmergencyButton(Context context) { @@ -51,15 +89,34 @@ public class EmergencyButton extends Button { public EmergencyButton(Context context, AttributeSet attrs) { super(context, attrs); + mIsVoiceCapable = getTelephonyManager().isVoiceCapable(); mEnableEmergencyCallWhileSimLocked = mContext.getResources().getBoolean( com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked); mEmergencyAffordanceManager = new EmergencyAffordanceManager(context); } + private TelephonyManager getTelephonyManager() { + return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mInfoCallback); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mInfoCallback); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); mLockPatternUtils = new LockPatternUtils(mContext); + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + setOnClickListener(v -> takeEmergencyCallAction()); if (mEmergencyAffordanceManager.needsEmergencyAffordance()) { setOnLongClickListener(v -> { if (!mLongPressWasDragged @@ -70,6 +127,7 @@ public class EmergencyButton extends Button { return false; }); } + whitelistIpcs(this::updateEmergencyCallButton); } @Override @@ -107,13 +165,65 @@ public class EmergencyButton extends Button { return super.performLongClick(); } - void updateEmergencyCallButton(boolean isInCall, boolean isVoiceCapable, boolean simLocked) { + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + updateEmergencyCallButton(); + } + + /** + * Shows the emergency dialer or returns the user to the existing call. + */ + public void takeEmergencyCallAction() { + MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_CALL); + if (mPowerManager != null) { + mPowerManager.userActivity(SystemClock.uptimeMillis(), true); + } + try { + ActivityTaskManager.getService().stopSystemLockTaskMode(); + } catch (RemoteException e) { + Slog.w(LOG_TAG, "Failed to stop app pinning"); + } + if (isInCall()) { + resumeCall(); + if (mEmergencyButtonCallback != null) { + mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall(); + } + } else { + KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class); + if (updateMonitor != null) { + updateMonitor.reportEmergencyCallAction(true /* bypassHandler */); + } else { + Log.w(LOG_TAG, "KeyguardUpdateMonitor was null, launching intent anyway."); + } + TelecomManager telecomManager = getTelecommManager(); + if (telecomManager == null) { + Log.wtf(LOG_TAG, "TelecomManager was null, cannot launch emergency dialer"); + return; + } + Intent emergencyDialIntent = + telecomManager.createLaunchEmergencyDialerIntent(null /* number*/) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | Intent.FLAG_ACTIVITY_CLEAR_TOP) + .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE, + EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON); + + getContext().startActivityAsUser(emergencyDialIntent, + ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(), + new UserHandle(KeyguardUpdateMonitor.getCurrentUser())); + } + } + + private void updateEmergencyCallButton() { boolean visible = false; - if (isVoiceCapable) { + if (mIsVoiceCapable) { // Emergency calling requires voice capability. - if (isInCall) { + if (isInCall()) { visible = true; // always show "return to call" if phone is off-hook } else { + final boolean simLocked = Dependency.get(KeyguardUpdateMonitor.class) + .isSimPinVoiceSecure(); if (simLocked) { // Some countries can't handle emergency calls while SIM is locked. visible = mEnableEmergencyCallWhileSimLocked; @@ -127,7 +237,7 @@ public class EmergencyButton extends Button { setVisibility(View.VISIBLE); int textId; - if (isInCall) { + if (isInCall()) { textId = com.android.internal.R.string.lockscreen_return_to_call; } else { textId = com.android.internal.R.string.lockscreen_emergency_call; @@ -137,4 +247,26 @@ public class EmergencyButton extends Button { setVisibility(View.GONE); } } + + public void setCallback(EmergencyButtonCallback callback) { + mEmergencyButtonCallback = callback; + } + + /** + * Resumes a call in progress. + */ + private void resumeCall() { + getTelecommManager().showInCallScreen(false); + } + + /** + * @return {@code true} if there is a call currently in progress. + */ + private boolean isInCall() { + return getTelecommManager().isInCall(); + } + + private TelecomManager getTelecommManager() { + return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); + } } diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java deleted file mode 100644 index 4275189cfe26..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java +++ /dev/null @@ -1,196 +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.keyguard; - -import static com.android.systemui.DejankUtils.whitelistIpcs; - -import android.app.ActivityOptions; -import android.app.ActivityTaskManager; -import android.content.Intent; -import android.content.res.Configuration; -import android.os.PowerManager; -import android.os.SystemClock; -import android.os.UserHandle; -import android.telecom.TelecomManager; -import android.telephony.TelephonyManager; -import android.util.Log; - -import androidx.annotation.Nullable; - -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.keyguard.dagger.KeyguardBouncerScope; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; -import com.android.systemui.util.EmergencyDialerConstants; -import com.android.systemui.util.ViewController; - -import javax.inject.Inject; - -/** View Controller for {@link com.android.keyguard.EmergencyButton}. */ -@KeyguardBouncerScope -public class EmergencyButtonController extends ViewController<EmergencyButton> { - static final String LOG_TAG = "EmergencyButton"; - private final ConfigurationController mConfigurationController; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final TelephonyManager mTelephonyManager; - private final PowerManager mPowerManager; - private final ActivityTaskManager mActivityTaskManager; - private final TelecomManager mTelecomManager; - private final MetricsLogger mMetricsLogger; - - private EmergencyButtonCallback mEmergencyButtonCallback; - - private final KeyguardUpdateMonitorCallback mInfoCallback = - new KeyguardUpdateMonitorCallback() { - @Override - public void onSimStateChanged(int subId, int slotId, int simState) { - updateEmergencyCallButton(); - } - - @Override - public void onPhoneStateChanged(int phoneState) { - updateEmergencyCallButton(); - } - }; - - private final ConfigurationListener mConfigurationListener = new ConfigurationListener() { - @Override - public void onConfigChanged(Configuration newConfig) { - updateEmergencyCallButton(); - } - }; - - private EmergencyButtonController(@Nullable EmergencyButton view, - ConfigurationController configurationController, - KeyguardUpdateMonitor keyguardUpdateMonitor, TelephonyManager telephonyManager, - PowerManager powerManager, ActivityTaskManager activityTaskManager, - @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger) { - super(view); - mConfigurationController = configurationController; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mTelephonyManager = telephonyManager; - mPowerManager = powerManager; - mActivityTaskManager = activityTaskManager; - mTelecomManager = telecomManager; - mMetricsLogger = metricsLogger; - } - - @Override - protected void onInit() { - whitelistIpcs(this::updateEmergencyCallButton); - } - - @Override - protected void onViewAttached() { - mKeyguardUpdateMonitor.registerCallback(mInfoCallback); - mConfigurationController.addCallback(mConfigurationListener); - mView.setOnClickListener(v -> takeEmergencyCallAction()); - } - - @Override - protected void onViewDetached() { - mKeyguardUpdateMonitor.removeCallback(mInfoCallback); - mConfigurationController.removeCallback(mConfigurationListener); - } - - private void updateEmergencyCallButton() { - if (mView != null) { - mView.updateEmergencyCallButton( - mTelecomManager != null && mTelecomManager.isInCall(), - mTelephonyManager.isVoiceCapable(), - mKeyguardUpdateMonitor.isSimPinVoiceSecure()); - } - } - - public void setEmergencyButtonCallback(EmergencyButtonCallback callback) { - mEmergencyButtonCallback = callback; - } - /** - * Shows the emergency dialer or returns the user to the existing call. - */ - public void takeEmergencyCallAction() { - mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_CALL); - if (mPowerManager != null) { - mPowerManager.userActivity(SystemClock.uptimeMillis(), true); - } - mActivityTaskManager.stopSystemLockTaskMode(); - if (mTelecomManager != null && mTelecomManager.isInCall()) { - mTelecomManager.showInCallScreen(false); - if (mEmergencyButtonCallback != null) { - mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall(); - } - } else { - mKeyguardUpdateMonitor.reportEmergencyCallAction(true /* bypassHandler */); - if (mTelecomManager == null) { - Log.wtf(LOG_TAG, "TelecomManager was null, cannot launch emergency dialer"); - return; - } - Intent emergencyDialIntent = - mTelecomManager.createLaunchEmergencyDialerIntent(null /* number*/) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS - | Intent.FLAG_ACTIVITY_CLEAR_TOP) - .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE, - EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON); - - getContext().startActivityAsUser(emergencyDialIntent, - ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(), - new UserHandle(KeyguardUpdateMonitor.getCurrentUser())); - } - } - - /** */ - public interface EmergencyButtonCallback { - /** */ - void onEmergencyButtonClickedWhenInCall(); - } - - /** Injectable Factory for creating {@link EmergencyButtonController}. */ - public static class Factory { - private final ConfigurationController mConfigurationController; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final TelephonyManager mTelephonyManager; - private final PowerManager mPowerManager; - private final ActivityTaskManager mActivityTaskManager; - @Nullable - private final TelecomManager mTelecomManager; - private final MetricsLogger mMetricsLogger; - - @Inject - public Factory(ConfigurationController configurationController, - KeyguardUpdateMonitor keyguardUpdateMonitor, TelephonyManager telephonyManager, - PowerManager powerManager, ActivityTaskManager activityTaskManager, - @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger) { - - mConfigurationController = configurationController; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mTelephonyManager = telephonyManager; - mPowerManager = powerManager; - mActivityTaskManager = activityTaskManager; - mTelecomManager = telecomManager; - mMetricsLogger = metricsLogger; - } - - /** Construct an {@link com.android.keyguard.EmergencyButtonController}. */ - public EmergencyButtonController create(EmergencyButton view) { - return new EmergencyButtonController(view, mConfigurationController, - mKeyguardUpdateMonitor, mTelephonyManager, mPowerManager, mActivityTaskManager, - mTelecomManager, mMetricsLogger); - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java index 7a05a17c8010..5760565aaab1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java @@ -31,7 +31,7 @@ import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockscreenCredential; -import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback; +import com.android.keyguard.EmergencyButton.EmergencyButtonCallback; import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.R; @@ -41,7 +41,6 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final LockPatternUtils mLockPatternUtils; private final LatencyTracker mLatencyTracker; - private final EmergencyButtonController mEmergencyButtonController; private CountDownTimer mCountdownTimer; protected KeyguardMessageAreaController mMessageAreaController; private boolean mDismissing; @@ -71,12 +70,11 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey LockPatternUtils lockPatternUtils, KeyguardSecurityCallback keyguardSecurityCallback, KeyguardMessageAreaController.Factory messageAreaControllerFactory, - LatencyTracker latencyTracker, EmergencyButtonController emergencyButtonController) { - super(view, securityMode, keyguardSecurityCallback, emergencyButtonController); + LatencyTracker latencyTracker) { + super(view, securityMode, keyguardSecurityCallback); mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; mLatencyTracker = latencyTracker; - mEmergencyButtonController = emergencyButtonController; KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView); mMessageAreaController = messageAreaControllerFactory.create(kma); } @@ -85,7 +83,6 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey @Override public void onInit() { - super.onInit(); mMessageAreaController.init(); } @@ -94,7 +91,10 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey super.onViewAttached(); mView.setKeyDownListener(mKeyDownListener); mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled()); - mEmergencyButtonController.setEmergencyButtonCallback(mEmergencyButtonCallback); + EmergencyButton button = mView.findViewById(R.id.emergency_call_button); + if (button != null) { + button.setCallback(mEmergencyButtonCallback); + } } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 6eb54c25a440..93ed0eaa3fba 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -179,20 +179,15 @@ public class KeyguardClockSwitch extends RelativeLayout { setPaddingRelative(startEndPadding, 0, startEndPadding, 0); mSmallClockFrame.setVisibility(GONE); mNewLockscreenClockFrame.setVisibility(VISIBLE); - - statusAreaLP.removeRule(RelativeLayout.BELOW); + statusAreaLP.addRule(RelativeLayout.BELOW, R.id.new_lockscreen_clock_view); statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START); - statusAreaLP.addRule(RelativeLayout.START_OF, R.id.new_lockscreen_clock_view); - statusAreaLP.width = 0; } else { setPaddingRelative(0, 0, 0, 0); mSmallClockFrame.setVisibility(VISIBLE); mNewLockscreenClockFrame.setVisibility(GONE); statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START); - statusAreaLP.removeRule(RelativeLayout.START_OF); statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view); - statusAreaLP.width = ViewGroup.LayoutParams.MATCH_PARENT; } requestLayout(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index e375877ed6cf..0675200f81e2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -20,6 +20,7 @@ import android.app.WallpaperManager; import android.content.ContentResolver; import android.content.res.Resources; import android.provider.Settings; +import android.text.TextUtils; import android.text.format.DateFormat; import android.view.View; import android.view.ViewGroup; @@ -212,10 +213,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS * keep the clock centered. */ void updatePosition(int x, float scale, AnimationProperties props, boolean animate) { - x = Math.abs(x); + x = getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? -x : x; if (mNewLockScreenClockFrame != null) { PropertyAnimator.setProperty(mNewLockScreenClockFrame, AnimatableProperty.TRANSLATION_X, - -x, props, animate); + x, props, animate); PropertyAnimator.setProperty(mNewLockScreenLargeClockFrame, AnimatableProperty.SCALE_X, scale, props, animate); PropertyAnimator.setProperty(mNewLockScreenLargeClockFrame, AnimatableProperty.SCALE_Y, @@ -277,15 +278,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS refreshFormat(mTimeFormat); } - float getClockTextTopPadding() { - if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1 - && mNewLockScreenClockViewController != null) { - return mNewLockScreenClockViewController.getClockTextTopPadding(); - } - - return mView.getClockTextTopPadding(); - } - private void updateAodIcons() { NotificationIconContainer nic = (NotificationIconContainer) mView.findViewById( @@ -337,4 +329,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS sCacheKey = key; } } + + private int getCurrentLayoutDirection() { + return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()); + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 76a7473e25e8..276036c400e1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -36,6 +36,7 @@ import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.dagger.KeyguardStatusViewComponent; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.navigationbar.NavigationBarController; @@ -45,15 +46,12 @@ import java.util.concurrent.Executor; import javax.inject.Inject; -import dagger.Lazy; - public class KeyguardDisplayManager { protected static final String TAG = "KeyguardDisplayManager"; private static boolean DEBUG = KeyguardConstants.DEBUG; private MediaRouter mMediaRouter = null; private final DisplayManager mDisplayService; - private final Lazy<NavigationBarController> mNavigationBarControllerLazy; private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; private final Context mContext; @@ -87,11 +85,9 @@ public class KeyguardDisplayManager { @Inject public KeyguardDisplayManager(Context context, - Lazy<NavigationBarController> navigationBarControllerLazy, KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, @UiBackground Executor uiBgExecutor) { mContext = context; - mNavigationBarControllerLazy = navigationBarControllerLazy; mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; uiBgExecutor.execute(() -> mMediaRouter = mContext.getSystemService(MediaRouter.class)); mDisplayService = mContext.getSystemService(DisplayManager.class); @@ -244,7 +240,7 @@ public class KeyguardDisplayManager { // Leave this task to {@link StatusBarKeyguardViewManager} if (displayId == DEFAULT_DISPLAY) return; - NavigationBarView navBarView = mNavigationBarControllerLazy.get() + NavigationBarView navBarView = Dependency.get(NavigationBarController.class) .getNavigationBarView(displayId); // We may not have nav bar on a display. if (navBarView == null) return; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java index a0c5958284ec..957882dc9c6b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java @@ -42,7 +42,6 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> private final SecurityMode mSecurityMode; private final KeyguardSecurityCallback mKeyguardSecurityCallback; private final EmergencyButton mEmergencyButton; - private final EmergencyButtonController mEmergencyButtonController; private boolean mPaused; @@ -70,18 +69,11 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> }; protected KeyguardInputViewController(T view, SecurityMode securityMode, - KeyguardSecurityCallback keyguardSecurityCallback, - EmergencyButtonController emergencyButtonController) { + KeyguardSecurityCallback keyguardSecurityCallback) { super(view); mSecurityMode = securityMode; mKeyguardSecurityCallback = keyguardSecurityCallback; mEmergencyButton = view == null ? null : view.findViewById(R.id.emergency_call_button); - mEmergencyButtonController = emergencyButtonController; - } - - @Override - protected void onInit() { - mEmergencyButtonController.init(); } @Override @@ -163,9 +155,8 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> private final InputMethodManager mInputMethodManager; private final DelayableExecutor mMainExecutor; private final Resources mResources; - private final LiftToActivateListener mLiftToActivateListener; - private final TelephonyManager mTelephonyManager; - private final EmergencyButtonController.Factory mEmergencyButtonControllerFactory; + private LiftToActivateListener mLiftToActivateListener; + private TelephonyManager mTelephonyManager; private final FalsingCollector mFalsingCollector; private final boolean mIsNewLayoutEnabled; @@ -177,7 +168,6 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor, @Main Resources resources, LiftToActivateListener liftToActivateListener, TelephonyManager telephonyManager, - EmergencyButtonController.Factory emergencyButtonControllerFactory, FalsingCollector falsingCollector, FeatureFlags featureFlags) { mKeyguardUpdateMonitor = keyguardUpdateMonitor; @@ -189,7 +179,6 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> mResources = resources; mLiftToActivateListener = liftToActivateListener; mTelephonyManager = telephonyManager; - mEmergencyButtonControllerFactory = emergencyButtonControllerFactory; mFalsingCollector = falsingCollector; mIsNewLayoutEnabled = featureFlags.isKeyguardLayoutEnabled(); } @@ -197,40 +186,31 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> /** Create a new {@link KeyguardInputViewController}. */ public KeyguardInputViewController create(KeyguardInputView keyguardInputView, SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback) { - EmergencyButtonController emergencyButtonController = - mEmergencyButtonControllerFactory.create( - keyguardInputView.findViewById(R.id.emergency_call_button)); - if (keyguardInputView instanceof KeyguardPatternView) { return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView, mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, - keyguardSecurityCallback, mLatencyTracker, - emergencyButtonController, - mMessageAreaControllerFactory); + keyguardSecurityCallback, mLatencyTracker, mMessageAreaControllerFactory); } else if (keyguardInputView instanceof KeyguardPasswordView) { return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView, mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, - mInputMethodManager, emergencyButtonController, mMainExecutor, mResources); + mInputMethodManager, mMainExecutor, mResources); } else if (keyguardInputView instanceof KeyguardPINView) { return new KeyguardPinViewController((KeyguardPINView) keyguardInputView, mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, - mLiftToActivateListener, emergencyButtonController, mFalsingCollector, - mIsNewLayoutEnabled); + mLiftToActivateListener, mFalsingCollector, mIsNewLayoutEnabled); } else if (keyguardInputView instanceof KeyguardSimPinView) { return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView, mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, mLiftToActivateListener, mTelephonyManager, - emergencyButtonController, mFalsingCollector, mIsNewLayoutEnabled); } else if (keyguardInputView instanceof KeyguardSimPukView) { return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView, mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, mLiftToActivateListener, mTelephonyManager, - emergencyButtonController, mFalsingCollector, mIsNewLayoutEnabled); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 92b65b242b0e..533bec14f60a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -136,14 +136,23 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView { @Override public void startAppearAnimation() { - // Reset state, and let IME animation reveal the view as it slides in + // Reset state, and let IME animation reveal the view as it slides in, if one exists. + // It is possible for an IME to have no view, so provide a default animation since no + // calls to animateForIme would occur setAlpha(0f); + animate() + .alpha(1f) + .setDuration(500) + .setStartDelay(300) + .start(); + setTranslationY(0f); } @Override public void animateForIme(float interpolatedFraction) { - setAlpha(interpolatedFraction); + animate().cancel(); + setAlpha(Math.max(interpolatedFraction, getAlpha())); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java index 2e4554592580..0f1c3c8a20b7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java @@ -111,11 +111,10 @@ public class KeyguardPasswordViewController KeyguardMessageAreaController.Factory messageAreaControllerFactory, LatencyTracker latencyTracker, InputMethodManager inputMethodManager, - EmergencyButtonController emergencyButtonController, @Main DelayableExecutor mainExecutor, @Main Resources resources) { super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, - messageAreaControllerFactory, latencyTracker, emergencyButtonController); + messageAreaControllerFactory, latencyTracker); mKeyguardSecurityCallback = keyguardSecurityCallback; mInputMethodManager = inputMethodManager; mMainExecutor = mainExecutor; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index 55e348cc06b1..2aaf748e2415 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -31,7 +31,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; import com.android.internal.widget.LockPatternView.Cell; import com.android.internal.widget.LockscreenCredential; -import com.android.keyguard.EmergencyButtonController.EmergencyButtonCallback; +import com.android.keyguard.EmergencyButton.EmergencyButtonCallback; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.settingslib.Utils; import com.android.systemui.R; @@ -50,7 +50,6 @@ public class KeyguardPatternViewController private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final LockPatternUtils mLockPatternUtils; private final LatencyTracker mLatencyTracker; - private final EmergencyButtonController mEmergencyButtonController; private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory; private KeyguardMessageAreaController mMessageAreaController; @@ -180,13 +179,11 @@ public class KeyguardPatternViewController LockPatternUtils lockPatternUtils, KeyguardSecurityCallback keyguardSecurityCallback, LatencyTracker latencyTracker, - EmergencyButtonController emergencyButtonController, KeyguardMessageAreaController.Factory messageAreaControllerFactory) { - super(view, securityMode, keyguardSecurityCallback, emergencyButtonController); + super(view, securityMode, keyguardSecurityCallback); mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; mLatencyTracker = latencyTracker; - mEmergencyButtonController = emergencyButtonController; mMessageAreaControllerFactory = messageAreaControllerFactory; KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView); mMessageAreaController = mMessageAreaControllerFactory.create(kma); @@ -208,7 +205,11 @@ public class KeyguardPatternViewController KeyguardUpdateMonitor.getCurrentUser())); // vibrate mode will be the same for the life of this screen mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled()); - mEmergencyButtonController.setEmergencyButtonCallback(mEmergencyButtonCallback); + + EmergencyButton button = mView.findViewById(R.id.emergency_call_button); + if (button != null) { + button.setCallback(mEmergencyButtonCallback); + } View cancelBtn = mView.findViewById(R.id.cancel_button); if (cancelBtn != null) { @@ -223,7 +224,10 @@ public class KeyguardPatternViewController protected void onViewDetached() { super.onViewDetached(); mLockPatternView.setOnPatternListener(null); - mEmergencyButtonController.setEmergencyButtonCallback(null); + EmergencyButton button = mView.findViewById(R.id.emergency_call_button); + if (button != null) { + button.setCallback(null); + } View cancelBtn = mView.findViewById(R.id.cancel_button); if (cancelBtn != null) { cancelBtn.setOnClickListener(null); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java index 1b5aa453ac97..f2479488db0f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java @@ -71,10 +71,9 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB KeyguardMessageAreaController.Factory messageAreaControllerFactory, LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener, - EmergencyButtonController emergencyButtonController, FalsingCollector falsingCollector) { super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, - messageAreaControllerFactory, latencyTracker, emergencyButtonController); + messageAreaControllerFactory, latencyTracker); mLiftToActivateListener = liftToActivateListener; mFalsingCollector = falsingCollector; mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId()); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java index a456d42f5be5..49099fa18323 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java @@ -34,11 +34,10 @@ public class KeyguardPinViewController KeyguardSecurityCallback keyguardSecurityCallback, KeyguardMessageAreaController.Factory messageAreaControllerFactory, LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener, - EmergencyButtonController emergencyButtonController, FalsingCollector falsingCollector, boolean isNewLayoutEnabled) { super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, messageAreaControllerFactory, latencyTracker, liftToActivateListener, - emergencyButtonController, falsingCollector); + falsingCollector); mKeyguardUpdateMonitor = keyguardUpdateMonitor; view.setIsNewLayoutEnabled(isNewLayoutEnabled); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java index bacd29f661ae..c77c86711abf 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java @@ -23,6 +23,7 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.Dependency; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -48,27 +49,24 @@ public class KeyguardSecurityModel { private final boolean mIsPukScreenAvailable; private final LockPatternUtils mLockPatternUtils; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Inject - KeyguardSecurityModel(@Main Resources resources, LockPatternUtils lockPatternUtils, - KeyguardUpdateMonitor keyguardUpdateMonitor) { + KeyguardSecurityModel(@Main Resources resources, LockPatternUtils lockPatternUtils) { mIsPukScreenAvailable = resources.getBoolean( com.android.internal.R.bool.config_enable_puk_unlock_screen); mLockPatternUtils = lockPatternUtils; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; } public SecurityMode getSecurityMode(int userId) { + KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class); + if (mIsPukScreenAvailable && SubscriptionManager.isValidSubscriptionId( - mKeyguardUpdateMonitor.getNextSubIdForState( - TelephonyManager.SIM_STATE_PUK_REQUIRED))) { + monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PUK_REQUIRED))) { return SecurityMode.SimPuk; } if (SubscriptionManager.isValidSubscriptionId( - mKeyguardUpdateMonitor.getNextSubIdForState( - TelephonyManager.SIM_STATE_PIN_REQUIRED))) { + monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED))) { return SecurityMode.SimPin; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java index 33d47fe13f38..f1b504e9f941 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java @@ -44,18 +44,15 @@ public class KeyguardSecurityViewFlipperController private final List<KeyguardInputViewController<KeyguardInputView>> mChildren = new ArrayList<>(); private final LayoutInflater mLayoutInflater; - private final EmergencyButtonController.Factory mEmergencyButtonControllerFactory; private final Factory mKeyguardSecurityViewControllerFactory; @Inject protected KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view, LayoutInflater layoutInflater, - KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory, - EmergencyButtonController.Factory emergencyButtonControllerFactory) { + KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory) { super(view); mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory; mLayoutInflater = layoutInflater; - mEmergencyButtonControllerFactory = emergencyButtonControllerFactory; } @Override @@ -114,8 +111,7 @@ public class KeyguardSecurityViewFlipperController if (childController == null) { childController = new NullKeyguardInputViewController( - securityMode, keyguardSecurityCallback, - mEmergencyButtonControllerFactory.create(null)); + securityMode, keyguardSecurityCallback); } return childController; @@ -144,9 +140,8 @@ public class KeyguardSecurityViewFlipperController private static class NullKeyguardInputViewController extends KeyguardInputViewController<KeyguardInputView> { protected NullKeyguardInputViewController(SecurityMode securityMode, - KeyguardSecurityCallback keyguardSecurityCallback, - EmergencyButtonController emergencyButtonController) { - super(null, securityMode, keyguardSecurityCallback, emergencyButtonController); + KeyguardSecurityCallback keyguardSecurityCallback) { + super(null, securityMode, keyguardSecurityCallback); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java index 4d2ebbb4a594..cdbbfe643812 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java @@ -78,11 +78,11 @@ public class KeyguardSimPinViewController KeyguardSecurityCallback keyguardSecurityCallback, KeyguardMessageAreaController.Factory messageAreaControllerFactory, LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener, - TelephonyManager telephonyManager, EmergencyButtonController emergencyButtonController, + TelephonyManager telephonyManager, FalsingCollector falsingCollector, boolean isNewLayoutEnabled) { super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, messageAreaControllerFactory, latencyTracker, liftToActivateListener, - emergencyButtonController, falsingCollector); + falsingCollector); mKeyguardUpdateMonitor = keyguardUpdateMonitor; mTelephonyManager = telephonyManager; mSimImageView = mView.findViewById(R.id.keyguard_sim); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java index 0d9bb6f73b49..8fff34278216 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java @@ -37,6 +37,7 @@ import android.widget.ImageView; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.classifier.FalsingCollector; @@ -84,11 +85,11 @@ public class KeyguardSimPukViewController KeyguardSecurityCallback keyguardSecurityCallback, KeyguardMessageAreaController.Factory messageAreaControllerFactory, LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener, - TelephonyManager telephonyManager, EmergencyButtonController emergencyButtonController, + TelephonyManager telephonyManager, FalsingCollector falsingCollector, boolean isNewLayoutEnabled) { super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, messageAreaControllerFactory, latencyTracker, liftToActivateListener, - emergencyButtonController, falsingCollector); + falsingCollector); mKeyguardUpdateMonitor = keyguardUpdateMonitor; mTelephonyManager = telephonyManager; mSimImageView = mView.findViewById(R.id.keyguard_sim); @@ -197,7 +198,8 @@ public class KeyguardSimPukViewController if (count < 2) { msg = rez.getString(R.string.kg_puk_enter_puk_hint); } else { - SubscriptionInfo info = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(mSubId); + SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class) + .getSubscriptionInfoForSubId(mSubId); CharSequence displayName = info != null ? info.getDisplayName() : ""; msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName); if (info != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index 1fbf71de47ca..2373d75cd4ea 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -49,8 +49,10 @@ import androidx.slice.widget.SliceContent; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.settingslib.Utils; +import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.wakelock.KeepAwakeAnimationListener; import java.io.FileDescriptor; @@ -83,10 +85,6 @@ public class KeyguardSliceView extends LinearLayout { */ private Runnable mContentChangeListener; private boolean mHasHeader; - private final int mRowWithHeaderPadding; - private final int mRowPadding; - private float mRowTextSize; - private float mRowWithHeaderTextSize; private View.OnClickListener mOnClickListener; private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; @@ -95,9 +93,6 @@ public class KeyguardSliceView extends LinearLayout { super(context, attrs); Resources resources = context.getResources(); - mRowPadding = resources.getDimensionPixelSize(R.dimen.subtitle_clock_padding); - mRowWithHeaderPadding = resources.getDimensionPixelSize(R.dimen.header_subtitle_padding); - mLayoutTransition = new LayoutTransition(); mLayoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2); mLayoutTransition.setDuration(LayoutTransition.APPEARING, DEFAULT_ANIM_DURATION); @@ -118,10 +113,6 @@ public class KeyguardSliceView extends LinearLayout { mTextColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor); mIconSize = (int) mContext.getResources().getDimension(R.dimen.widget_icon_size); mIconSizeWithHeader = (int) mContext.getResources().getDimension(R.dimen.header_icon_size); - mRowTextSize = mContext.getResources().getDimensionPixelSize( - R.dimen.widget_label_font_size); - mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize( - R.dimen.header_row_font_size); mTitle.setBreakStrategy(LineBreaker.BREAK_STRATEGY_BALANCED); } @@ -202,7 +193,6 @@ public class KeyguardSliceView extends LinearLayout { LinearLayout.LayoutParams layoutParams = (LayoutParams) mRow.getLayoutParams(); layoutParams.gravity = mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL ? Gravity.START : Gravity.CENTER; - layoutParams.topMargin = mHasHeader ? mRowWithHeaderPadding : mRowPadding; mRow.setLayoutParams(layoutParams); for (int i = startIndex; i < subItemsCount; i++) { @@ -228,8 +218,6 @@ public class KeyguardSliceView extends LinearLayout { final SliceItem titleItem = rc.getTitleItem(); button.setText(titleItem == null ? null : titleItem.getText()); button.setContentDescription(rc.getContentDescription()); - button.setTextSize(TypedValue.COMPLEX_UNIT_PX, - mHasHeader ? mRowWithHeaderTextSize : mRowTextSize); Drawable iconDrawable = null; SliceItem icon = SliceQuery.find(item.getSlice(), @@ -311,26 +299,6 @@ public class KeyguardSliceView extends LinearLayout { void onDensityOrFontScaleChanged() { mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.widget_icon_size); mIconSizeWithHeader = (int) mContext.getResources().getDimension(R.dimen.header_icon_size); - mRowTextSize = mContext.getResources().getDimensionPixelSize( - R.dimen.widget_label_font_size); - mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize( - R.dimen.header_row_font_size); - - for (int i = 0; i < mRow.getChildCount(); i++) { - View child = mRow.getChildAt(i); - if (child instanceof KeyguardSliceTextView) { - ((KeyguardSliceTextView) child).onDensityOrFontScaleChanged(); - } - } - } - - void onOverlayChanged() { - for (int i = 0; i < mRow.getChildCount(); i++) { - View child = mRow.getChildAt(i); - if (child instanceof KeyguardSliceTextView) { - ((KeyguardSliceTextView) child).onOverlayChanged(); - } - } } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -493,7 +461,8 @@ public class KeyguardSliceView extends LinearLayout { * Representation of an item that appears under the clock on main keyguard message. */ @VisibleForTesting - static class KeyguardSliceTextView extends TextView { + static class KeyguardSliceTextView extends TextView implements + ConfigurationController.ConfigurationListener { private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; @StyleRes @@ -505,10 +474,24 @@ public class KeyguardSliceView extends LinearLayout { setEllipsize(TruncateAt.END); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Dependency.get(ConfigurationController.class).addCallback(this); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + Dependency.get(ConfigurationController.class).removeCallback(this); + } + + @Override public void onDensityOrFontScaleChanged() { updatePadding(); } + @Override public void onOverlayChanged() { setTextAppearance(sStyleId); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java index 8038ce4c7b69..1b0a7faeddab 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java @@ -83,10 +83,6 @@ public class KeyguardSliceViewController extends ViewController<KeyguardSliceVie public void onDensityOrFontScaleChanged() { mView.onDensityOrFontScaleChanged(); } - @Override - public void onOverlayChanged() { - mView.onOverlayChanged(); - } }; Observer<Slice> mObserver = new Observer<Slice>() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 5db4f9e61140..fea152abe36a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -34,6 +34,7 @@ import android.widget.TextView; import androidx.core.graphics.ColorUtils; import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.Dependency; import com.android.systemui.R; import java.io.FileDescriptor; @@ -55,6 +56,7 @@ public class KeyguardStatusView extends GridLayout { private final IActivityManager mIActivityManager; private TextView mLogoutView; + private boolean mCanShowLogout = true; // by default, try to show the logout button here private KeyguardClockSwitch mClockView; private TextView mOwnerInfo; private boolean mCanShowOwnerInfo = true; // by default, try to show the owner information here @@ -128,6 +130,11 @@ public class KeyguardStatusView extends GridLayout { } } + void setCanShowLogout(boolean canShowLogout) { + mCanShowLogout = canShowLogout; + updateLogoutView(); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -152,7 +159,10 @@ public class KeyguardStatusView extends GridLayout { mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged); onSliceContentChanged(); + boolean shouldMarquee = Dependency.get(KeyguardUpdateMonitor.class).isDeviceInteractive(); + setEnableMarquee(shouldMarquee); updateOwnerInfo(); + updateLogoutView(); updateDark(); } @@ -199,11 +209,11 @@ public class KeyguardStatusView extends GridLayout { return mOwnerInfo.getVisibility() == VISIBLE ? mOwnerInfo.getHeight() : 0; } - void updateLogoutView(boolean shouldShowLogout) { + void updateLogoutView() { if (mLogoutView == null) { return; } - mLogoutView.setVisibility(shouldShowLogout ? VISIBLE : GONE); + mLogoutView.setVisibility(mCanShowLogout && shouldShowLogout() ? VISIBLE : GONE); // Logout button will stay in language of user 0 if we don't set that manually. mLogoutView.setText(mContext.getResources().getString( com.android.internal.R.string.global_action_logout)); @@ -303,6 +313,11 @@ public class KeyguardStatusView extends GridLayout { } } + private boolean shouldShowLogout() { + return Dependency.get(KeyguardUpdateMonitor.class).isLogoutEnabled() + && KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM; + } + private void onLogoutClicked(View view) { int currentUserId = KeyguardUpdateMonitor.getCurrentUser(); try { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index bfe7f8c7ebd8..934e768f9ba8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -16,7 +16,6 @@ package com.android.keyguard; -import android.os.UserHandle; import android.util.Slog; import android.view.View; @@ -79,8 +78,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV @Override public void onInit() { mKeyguardClockSwitchController.init(); - mView.setEnableMarquee(mKeyguardUpdateMonitor.isDeviceInteractive()); - mView.updateLogoutView(shouldShowLogout()); } @Override @@ -248,11 +245,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } } - private boolean shouldShowLogout() { - return mKeyguardUpdateMonitor.isLogoutEnabled() - && KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM; - } - private final ConfigurationController.ConfigurationListener mConfigurationListener = new ConfigurationController.ConfigurationListener() { @Override @@ -274,17 +266,11 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV mKeyguardClockSwitchController.updateLockScreenMode(mode); mKeyguardSliceViewController.updateLockScreenMode(mode); if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { - // align the top of keyguard_status_area with the top of the clock text instead - // of the top of the view - mKeyguardSliceViewController.updateTopMargin( - mKeyguardClockSwitchController.getClockTextTopPadding()); mView.setCanShowOwnerInfo(false); - mView.updateLogoutView(false); + mView.setCanShowLogout(false); } else { - // reset margin - mKeyguardSliceViewController.updateTopMargin(0); mView.setCanShowOwnerInfo(true); - mView.updateLogoutView(false); + mView.setCanShowLogout(false); } updateAodIcons(); } @@ -310,7 +296,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing); refreshTime(); mView.updateOwnerInfo(); - mView.updateLogoutView(shouldShowLogout()); + mView.updateLogoutView(); } } @@ -328,12 +314,12 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV public void onUserSwitchComplete(int userId) { mKeyguardClockSwitchController.refreshFormat(); mView.updateOwnerInfo(); - mView.updateLogoutView(shouldShowLogout()); + mView.updateLogoutView(); } @Override public void onLogoutEnabledChanged() { - mView.updateLogoutView(shouldShowLogout()); + mView.updateLogoutView(); } }; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index d9a1eb6c0b28..a4054bea1167 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -305,6 +305,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private boolean mLogoutEnabled; // cached value to avoid IPCs private boolean mIsUdfpsEnrolled; + private boolean mKeyguardQsUserSwitchEnabled; // If the user long pressed the lock icon, disabling face auth for the current session. private boolean mLockIconPressed; private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -1916,7 +1917,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return isFaceAuthEnabledForUser(KeyguardUpdateMonitor.getCurrentUser()) && !isUdfpsEnrolled(); } - return true; + return !isKeyguardQsUserSwitchEnabled(); } /** @@ -1926,6 +1927,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return mIsUdfpsEnrolled; } + /** + * @return true if the keyguard qs user switcher shortcut is enabled + */ + public boolean isKeyguardQsUserSwitchEnabled() { + return mKeyguardQsUserSwitchEnabled; + } + + public void setKeyguardQsUserSwitchEnabled(boolean enabled) { + mKeyguardQsUserSwitchEnabled = enabled; + } + private final UserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() { @Override public void onUserSwitching(int newUserId, IRemoteCallback reply) { diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java index 8cb1bc4878a5..5db3349b646a 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java @@ -69,7 +69,9 @@ public class NumPadButton extends AlphaOptimizedImageButton { @Override public boolean onTouchEvent(MotionEvent event) { - if (mAnimator != null) mAnimator.start(); + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + if (mAnimator != null) mAnimator.start(); + } return super.onTouchEvent(event); } diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index a4a781dc6ff5..e6a9d4fdd547 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -160,10 +160,9 @@ public class NumPadKey extends ViewGroup { public boolean onTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { doHapticKeyClick(); + if (mAnimator != null) mAnimator.start(); } - if (mAnimator != null) mAnimator.start(); - return super.onTouchEvent(event); } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java index b6413cb61deb..5ef35be8df51 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java @@ -28,12 +28,11 @@ import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.Dependency; import java.io.FileNotFoundException; import java.util.List; - -import javax.inject.Inject; -import javax.inject.Provider; +import java.util.function.Supplier; /** * Exposes custom clock face options and provides realistic preview images. @@ -66,12 +65,15 @@ public final class ClockOptionsProvider extends ContentProvider { private static final String CONTENT_SCHEME = "content"; private static final String AUTHORITY = "com.android.keyguard.clock"; - @Inject - public Provider<List<ClockInfo>> mClockInfosProvider; + private final Supplier<List<ClockInfo>> mClocksSupplier; + + public ClockOptionsProvider() { + this(() -> Dependency.get(ClockManager.class).getClockInfos()); + } @VisibleForTesting - ClockOptionsProvider(Provider<List<ClockInfo>> clockInfosProvider) { - mClockInfosProvider = clockInfosProvider; + ClockOptionsProvider(Supplier<List<ClockInfo>> clocksSupplier) { + mClocksSupplier = clocksSupplier; } @Override @@ -97,7 +99,7 @@ public final class ClockOptionsProvider extends ContentProvider { } MatrixCursor cursor = new MatrixCursor(new String[] { COLUMN_NAME, COLUMN_TITLE, COLUMN_ID, COLUMN_THUMBNAIL, COLUMN_PREVIEW}); - List<ClockInfo> clocks = mClockInfosProvider.get(); + List<ClockInfo> clocks = mClocksSupplier.get(); for (int i = 0; i < clocks.size(); i++) { ClockInfo clock = clocks.get(i); cursor.newRow() @@ -137,7 +139,7 @@ public final class ClockOptionsProvider extends ContentProvider { throw new FileNotFoundException("Invalid preview url, missing id"); } ClockInfo clock = null; - List<ClockInfo> clocks = mClockInfosProvider.get(); + List<ClockInfo> clocks = mClocksSupplier.get(); for (int i = 0; i < clocks.size(); i++) { if (id.equals(clocks.get(i).getId())) { clock = clocks.get(i); diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java deleted file mode 100644 index 49a617eeb6c0..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java +++ /dev/null @@ -1,42 +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.keyguard.dagger; - -import com.android.keyguard.KeyguardStatusViewController; -import com.android.systemui.statusbar.phone.KeyguardStatusBarView; -import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController; - -import dagger.BindsInstance; -import dagger.Subcomponent; - -/** - * Subcomponent for helping work with KeyguardStatusView and its children. - * - * TODO: unify this with {@link KeyguardStatusViewComponent} - */ -@Subcomponent(modules = {KeyguardStatusBarViewModule.class}) -@KeyguardStatusBarViewScope -public interface KeyguardStatusBarViewComponent { - /** Simple factory for {@link KeyguardStatusBarViewComponent}. */ - @Subcomponent.Factory - interface Factory { - KeyguardStatusBarViewComponent build(@BindsInstance KeyguardStatusBarView view); - } - - /** Builds a {@link KeyguardStatusViewController}. */ - KeyguardStatusBarViewController getKeyguardStatusBarViewController(); -} diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java deleted file mode 100644 index a6725234e4af..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java +++ /dev/null @@ -1,34 +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.keyguard.dagger; - -import com.android.keyguard.CarrierText; -import com.android.systemui.R; -import com.android.systemui.statusbar.phone.KeyguardStatusBarView; - -import dagger.Module; -import dagger.Provides; - -/** Dagger module for {@link KeyguardStatusBarViewComponent}. */ -@Module -public abstract class KeyguardStatusBarViewModule { - @Provides - @KeyguardStatusBarViewScope - static CarrierText getCarrierText(KeyguardStatusBarView view) { - return view.findViewById(R.id.keyguard_carrier_text); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java index d342377da49b..1b6476ce74df 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java @@ -25,8 +25,6 @@ import dagger.Subcomponent; /** * Subcomponent for helping work with KeyguardStatusView and its children. - * - * TODO: unify this with {@link KeyguardStatusBarViewComponent} */ @Subcomponent(modules = {KeyguardStatusViewModule.class}) @KeyguardStatusViewScope diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 71ec33e16e0e..a9b4c492e10c 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -16,7 +16,10 @@ package com.android.systemui; +import android.app.WallpaperColors; +import android.graphics.Bitmap; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; @@ -26,13 +29,16 @@ import android.util.Log; import android.util.Size; import android.view.SurfaceHolder; +import androidx.annotation.NonNull; + import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.glwallpaper.EglHelper; -import com.android.systemui.glwallpaper.GLWallpaperRenderer; import com.android.systemui.glwallpaper.ImageWallpaperRenderer; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; import javax.inject.Inject; @@ -45,8 +51,13 @@ public class ImageWallpaper extends WallpaperService { // We delayed destroy render context that subsequent render requests have chance to cancel it. // This is to avoid destroying then recreating render context in a very short time. private static final int DELAY_FINISH_RENDERING = 1000; + private static final @android.annotation.NonNull RectF LOCAL_COLOR_BOUNDS = + new RectF(0, 0, 1, 1); private static final boolean DEBUG = false; + private ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>(); private HandlerThread mWorker; + // scaled down version + private Bitmap mMiniBitmap; @Inject public ImageWallpaper() { @@ -70,6 +81,7 @@ public class ImageWallpaper extends WallpaperService { super.onDestroy(); mWorker.quitSafely(); mWorker = null; + mMiniBitmap = null; } class GLEngine extends Engine { @@ -80,7 +92,7 @@ public class ImageWallpaper extends WallpaperService { @VisibleForTesting static final int MIN_SURFACE_HEIGHT = 64; - private GLWallpaperRenderer mRenderer; + private ImageWallpaperRenderer mRenderer; private EglHelper mEglHelper; private final Runnable mFinishRenderingTask = this::finishRendering; private boolean mNeedRedraw; @@ -101,6 +113,10 @@ public class ImageWallpaper extends WallpaperService { setFixedSizeAllowed(true); setOffsetNotificationsEnabled(false); updateSurfaceSize(); + mMiniBitmap = null; + if (mWorker != null && mWorker.getThreadHandler() != null) { + mWorker.getThreadHandler().post(this::updateMiniBitmap); + } } EglHelper getEglHelperInstance() { @@ -111,6 +127,20 @@ public class ImageWallpaper extends WallpaperService { return new ImageWallpaperRenderer(getDisplayContext()); } + private void updateMiniBitmap() { + mRenderer.useBitmap(b -> { + int size = Math.min(b.getWidth(), b.getHeight()); + float scale = 1.0f; + if (size > MIN_SURFACE_WIDTH) { + scale = (float) MIN_SURFACE_WIDTH / (float) size; + } + mMiniBitmap = Bitmap.createScaledBitmap(b, Math.round(scale * b.getWidth()), + Math.round(scale * b.getHeight()), false); + computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap); + mLocalColorsToAdd.clear(); + }); + } + private void updateSurfaceSize() { SurfaceHolder holder = getSurfaceHolder(); Size frameSize = mRenderer.reportSurfaceSize(); @@ -126,6 +156,7 @@ public class ImageWallpaper extends WallpaperService { @Override public void onDestroy() { + mMiniBitmap = null; mWorker.getThreadHandler().post(() -> { mRenderer.finish(); mRenderer = null; @@ -135,6 +166,59 @@ public class ImageWallpaper extends WallpaperService { } @Override + public boolean supportsLocalColorExtraction() { + return true; + } + + @Override + public void addLocalColorsAreas(@NonNull List<RectF> regions) { + mWorker.getThreadHandler().post(() -> { + Bitmap bitmap = mMiniBitmap; + if (bitmap == null) { + mLocalColorsToAdd.addAll(regions); + } else { + computeAndNotifyLocalColors(regions, bitmap); + } + }); + } + + private void computeAndNotifyLocalColors(@NonNull List<RectF> regions, Bitmap b) { + List<WallpaperColors> colors = getLocalWallpaperColors(regions, b); + try { + notifyLocalColorsChanged(regions, colors); + } catch (RuntimeException e) { + Log.e(TAG, e.getMessage(), e); + } + } + + @Override + public void removeLocalColorsAreas(@NonNull List<RectF> regions) { + // No-OP + } + + private List<WallpaperColors> getLocalWallpaperColors(@NonNull List<RectF> areas, + Bitmap b) { + List<WallpaperColors> colors = new ArrayList<>(areas.size()); + for (int i = 0; i < areas.size(); i++) { + RectF area = areas.get(i); + if (area == null || !LOCAL_COLOR_BOUNDS.contains(area)) { + colors.add(null); + continue; + } + Rect subImage = new Rect( + Math.round(area.left * b.getWidth()), + Math.round(area.top * b.getHeight()), + Math.round(area.right * b.getWidth()), + Math.round(area.bottom * b.getHeight())); + Bitmap colorImg = Bitmap.createBitmap(b, + subImage.left, subImage.top, subImage.width(), subImage.height()); + WallpaperColors color = WallpaperColors.fromBitmap(colorImg); + colors.add(color); + } + return colors; + } + + @Override public void onSurfaceCreated(SurfaceHolder holder) { if (mWorker == null) return; mWorker.getThreadHandler().post(() -> { diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index d8ca63960b30..2040347de1b5 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -16,8 +16,8 @@ package com.android.systemui.appops; -import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA; -import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE; +import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; +import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED; import android.Manifest; @@ -40,6 +40,7 @@ import android.util.SparseArray; import androidx.annotation.WorkerThread; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; @@ -78,6 +79,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon private static final boolean DEBUG = false; private final BroadcastDispatcher mDispatcher; + private final Context mContext; private final AppOpsManager mAppOps; private final AudioManager mAudioManager; private final LocationManager mLocationManager; @@ -137,10 +139,11 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon mAudioManager = audioManager; mSensorPrivacyController = sensorPrivacyController; mMicMuted = audioManager.isMicrophoneMute() - || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE); - mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA); + || mSensorPrivacyController.isSensorBlocked(MICROPHONE); + mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA); mLocationManager = context.getSystemService(LocationManager.class); mPackageManager = context.getPackageManager(); + mContext = context; dumpManager.registerDumpable(TAG, this); } @@ -159,8 +162,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon mSensorPrivacyController.addCallback(this); mMicMuted = mAudioManager.isMicrophoneMute() - || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE); - mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA); + || mSensorPrivacyController.isSensorBlocked(MICROPHONE); + mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA); mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged( mAudioManager.getActiveRecordingConfigurations())); @@ -357,6 +360,15 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon return mLocationProviderPackages.contains(packageName); } + private boolean isSpeechRecognizerUsage(int opCode, String packageName) { + if (AppOpsManager.OP_RECORD_AUDIO != opCode) { + return false; + } + + return packageName.equals( + mContext.getString(R.string.config_systemSpeechRecognizer)); + } + // TODO ntmyren: remove after teamfood is finished private boolean shouldShowAppPredictor(String pkgName) { if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled", @@ -388,7 +400,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon } // TODO ntmyren: Replace this with more robust check if this moves beyond teamfood if ((appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName)) - || shouldShowAppPredictor(packageName)) { + || shouldShowAppPredictor(packageName) + || isSpeechRecognizerUsage(appOpCode, packageName)) { return true; } @@ -473,7 +486,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon @Override public void onOpNoted(int code, int uid, String packageName, - @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) { + String attributionTag, @AppOpsManager.OpFlags int flags, + @AppOpsManager.Mode int result) { if (DEBUG) { Log.w(TAG, "Noted op: " + code + " with result " + AppOpsManager.MODE_NAMES[result] + " for package " + packageName); @@ -582,16 +596,16 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon @Override public void onReceive(Context context, Intent intent) { mMicMuted = mAudioManager.isMicrophoneMute() - || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE); + || mSensorPrivacyController.isSensorBlocked(MICROPHONE); updateSensorDisabledStatus(); } @Override public void onSensorBlockedChanged(int sensor, boolean blocked) { mBGHandler.post(() -> { - if (sensor == INDIVIDUAL_SENSOR_CAMERA) { + if (sensor == CAMERA) { mCameraDisabled = blocked; - } else if (sensor == INDIVIDUAL_SENSOR_MICROPHONE) { + } else if (sensor == MICROPHONE) { mMicMuted = mAudioManager.isMicrophoneMute() || blocked; } updateSensorDisabledStatus(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java index 3ea8140427cb..3bf75d105b9f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java @@ -19,8 +19,10 @@ package com.android.systemui.biometrics; import android.content.Context; import android.graphics.RectF; import android.graphics.drawable.Drawable; +import android.view.View; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.systemui.R; @@ -33,6 +35,7 @@ public abstract class UdfpsAnimation extends Drawable { @NonNull protected final Context mContext; @NonNull protected final Drawable mFingerprintDrawable; + @Nullable private View mView; public UdfpsAnimation(@NonNull Context context) { mContext = context; @@ -53,6 +56,10 @@ public abstract class UdfpsAnimation extends Drawable { mFingerprintDrawable.setAlpha(alpha); } + public void setAnimationView(UdfpsAnimationView view) { + mView = view; + } + /** * @return The amount of padding that's needed on each side of the sensor, in pixels. */ @@ -66,4 +73,10 @@ public abstract class UdfpsAnimation extends Drawable { public int getPaddingY() { return 0; } + + protected void postInvalidateView() { + if (mView != null) { + mView.postInvalidate(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java index 501de9df575b..8664e44c9ad2 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java @@ -23,7 +23,6 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.util.MathUtils; -import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -42,7 +41,6 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv private static final String TAG = "UdfpsAnimationKeyguard"; - @NonNull private final View mParent; @NonNull private final Context mContext; private final int mMaxBurnInOffsetX; private final int mMaxBurnInOffsetY; @@ -52,10 +50,9 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv private float mBurnInOffsetX; private float mBurnInOffsetY; - UdfpsAnimationKeyguard(@NonNull View parent, @NonNull Context context, + UdfpsAnimationKeyguard(@NonNull Context context, @NonNull StatusBarStateController statusBarStateController) { super(context); - mParent = parent; mContext = context; mMaxBurnInOffsetX = context.getResources() @@ -73,10 +70,10 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv mInterpolatedDarkAmount); mBurnInOffsetY = MathUtils.lerp(0f, getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */) - - 0.5f * mMaxBurnInOffsetY, + - mMaxBurnInOffsetY, mInterpolatedDarkAmount); updateColor(); - mParent.postInvalidate(); + postInvalidateView(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java index 41ea4d66f575..44122cba8716 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java @@ -74,7 +74,14 @@ public class UdfpsAnimationView extends View implements DozeReceiver, } void setAnimation(@Nullable UdfpsAnimation animation) { + if (mUdfpsAnimation != null) { + mUdfpsAnimation.setAnimationView(null); + } + mUdfpsAnimation = animation; + if (mUdfpsAnimation != null) { + mUdfpsAnimation.setAnimationView(this); + } } void onSensorRectUpdated(@NonNull RectF sensorRect) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index e7b08e72877d..71fba3302abc 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -44,7 +44,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -69,6 +68,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private final Context mContext; private final FingerprintManager mFingerprintManager; + @NonNull private final LayoutInflater mInflater; private final WindowManager mWindowManager; private final DelayableExecutor mFgExecutor; private final StatusBarStateController mStatusBarStateController; @@ -76,10 +76,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // sensors, this, in addition to a lot of the code here, will be updated. @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps; private final WindowManager.LayoutParams mCoreLayoutParams; - private final UdfpsView mView; - // Indicates whether the overlay is currently showing. Even if it has been requested, it might - // not be showing. - private boolean mIsOverlayShowing; + + @Nullable private UdfpsView mView; // Indicates whether the overlay has been requested. private boolean mIsOverlayRequested; // Reason the overlay has been requested. See IUdfpsOverlayController for definitions. @@ -112,20 +110,48 @@ public class UdfpsController implements DozeReceiver, HbmCallback { @Override public void onEnrollmentProgress(int sensorId, int remaining) { + if (mView == null) { + return; + } mView.onEnrollmentProgress(remaining); } @Override public void onEnrollmentHelp(int sensorId) { + if (mView == null) { + return; + } mView.onEnrollmentHelp(); } @Override public void setDebugMessage(int sensorId, String message) { + if (mView == null) { + return; + } mView.setDebugMessage(message); } } + @VisibleForTesting + final StatusBar.ExpansionChangedListener mStatusBarExpansionListener = + (expansion, expanded) -> { + if (mView != null) { + mView.onExpansionChanged(expansion, expanded); + } + }; + + @VisibleForTesting + final StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + if (mView != null) { + mView.onStateChanged(newState); + } + } + }; + @SuppressLint("ClickableViewAccessibility") private final UdfpsView.OnTouchListener mOnTouchListener = (v, event) -> { UdfpsView view = (UdfpsView) v; @@ -158,13 +184,14 @@ public class UdfpsController implements DozeReceiver, HbmCallback { @Inject public UdfpsController(@NonNull Context context, @Main Resources resources, - LayoutInflater inflater, + @NonNull LayoutInflater inflater, @Nullable FingerprintManager fingerprintManager, WindowManager windowManager, @NonNull StatusBarStateController statusBarStateController, @Main DelayableExecutor fgExecutor, @Nullable StatusBar statusBar) { mContext = context; + mInflater = inflater; // The fingerprint manager is queried for UDFPS before this class is constructed, so the // fingerprint manager should never be null. mFingerprintManager = checkNotNull(fingerprintManager); @@ -190,15 +217,10 @@ public class UdfpsController implements DozeReceiver, HbmCallback { WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; - mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false); - mView.setSensorProperties(mSensorProps); - mView.setHbmCallback(this); - - statusBar.addExpansionChangedListener(mView); - statusBarStateController.addCallback(mView); + statusBar.addExpansionChangedListener(mStatusBarExpansionListener); + mStatusBarStateController.addCallback(mStatusBarStateListener); mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController()); - mIsOverlayShowing = false; } @Nullable @@ -221,7 +243,13 @@ public class UdfpsController implements DozeReceiver, HbmCallback { * @return where the UDFPS exists on the screen in pixels. */ public RectF getSensorLocation() { - return mView.getSensorRect(); + // This is currently used to calculate the amount of space available for notifications + // on lockscreen. Keyguard is only shown in portrait mode for now, so this will need to + // be updated if that ever changes. + return new RectF(mSensorProps.sensorLocationX - mSensorProps.sensorRadius, + mSensorProps.sensorLocationY - mSensorProps.sensorRadius, + mSensorProps.sensorLocationX + mSensorProps.sensorRadius, + mSensorProps.sensorLocationY + mSensorProps.sensorRadius); } private void showOverlay(int reason) { @@ -299,14 +327,18 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private void showUdfpsOverlay(int reason) { mFgExecutor.execute(() -> { - if (!mIsOverlayShowing) { + if (mView == null) { try { Log.v(TAG, "showUdfpsOverlay | adding window"); + + mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false); + mView.setSensorProperties(mSensorProps); + mView.setHbmCallback(this); + final UdfpsAnimation animation = getUdfpsAnimationForReason(reason); mView.setExtras(animation, mEnrollHelper); mWindowManager.addView(mView, computeLayoutParams(animation)); mView.setOnTouchListener(mOnTouchListener); - mIsOverlayShowing = true; } catch (RuntimeException e) { Log.e(TAG, "showUdfpsOverlay | failed to add window", e); } @@ -324,7 +356,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: return new UdfpsAnimationEnroll(mContext); case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: - return new UdfpsAnimationKeyguard(mView, mContext, mStatusBarStateController); + return new UdfpsAnimationKeyguard(mContext, mStatusBarStateController); case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: return new UdfpsAnimationFpmOther(mContext); default: @@ -335,14 +367,12 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private void hideUdfpsOverlay() { mFgExecutor.execute(() -> { - if (mIsOverlayShowing) { + if (mView != null) { Log.v(TAG, "hideUdfpsOverlay | removing window"); - mView.setExtras(null, null); - mView.setOnTouchListener(null); // Reset the controller back to its starting state. onFingerUp(); mWindowManager.removeView(mView); - mIsOverlayShowing = false; + mView = null; } else { Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden"); } @@ -389,12 +419,20 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // This method can be called from the UI thread. private void onFingerDown(int x, int y, float minor, float major) { + if (mView == null) { + Log.w(TAG, "Null view in onFingerDown"); + return; + } mView.startIllumination(() -> mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major)); } // This method can be called from the UI thread. private void onFingerUp() { + if (mView == null) { + Log.w(TAG, "Null view in onFingerUp"); + return; + } mFingerprintManager.onPointerUp(mSensorProps.sensorId); mView.stopIllumination(); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index 00cb28b8b8fb..399794391700 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -113,6 +113,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin void setExtras(@Nullable UdfpsAnimation animation, @Nullable UdfpsEnrollHelper enrollHelper) { mAnimationView.setAnimation(animation); + mEnrollHelper = enrollHelper; if (enrollHelper != null) { @@ -135,17 +136,13 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin } @Override - public void onExpandedChanged(boolean isExpanded) { - mNotificationShadeExpanded = isExpanded; - } - - @Override public void onStateChanged(int newState) { mStatusBarState = newState; } @Override public void onExpansionChanged(float expansion, boolean expanded) { + mNotificationShadeExpanded = expanded; mAnimationView.onExpansionChanged(expansion, expanded); } @@ -191,10 +188,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin } } - RectF getSensorRect() { - return new RectF(mSensorRect); - } - void setDebugMessage(String message) { mDebugMessage = message; postInvalidate(); diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt index 36937d622b5b..6b7a1ac8cb7e 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt @@ -91,7 +91,7 @@ class ControlsComponent @Inject constructor( } /** - * @return true if controls are feature-enabled and have available services to serve controls + * @return true if controls are feature-enabled and the user has the setting enabled */ fun isEnabled() = featureEnabled && lazyControlsController.get().available diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt index cad166d7cd9e..1ea1d97cace5 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt @@ -41,7 +41,7 @@ import com.android.systemui.controls.ui.ControlsUiController object ControlsAnimations { - private const val ALPHA_EXIT_DURATION = 167L + private const val ALPHA_EXIT_DURATION = 183L private const val ALPHA_ENTER_DELAY = ALPHA_EXIT_DURATION private const val ALPHA_ENTER_DURATION = 350L - ALPHA_ENTER_DELAY diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt index 40c238680a45..fc89783018bc 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt @@ -32,6 +32,8 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.controls.CustomIconCache import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.StructureInfo +import com.android.systemui.controls.ui.ControlsDialog +import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.globalactions.GlobalActionsComponent import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.util.LifecycleActivity @@ -42,9 +44,10 @@ import javax.inject.Inject */ class ControlsEditingActivity @Inject constructor( private val controller: ControlsControllerImpl, - broadcastDispatcher: BroadcastDispatcher, + private val broadcastDispatcher: BroadcastDispatcher, private val globalActionsComponent: GlobalActionsComponent, - private val customIconCache: CustomIconCache + private val customIconCache: CustomIconCache, + private val uiController: ControlsUiController ) : LifecycleActivity() { companion object { @@ -59,6 +62,7 @@ class ControlsEditingActivity @Inject constructor( private lateinit var model: FavoritesModel private lateinit var subtitle: TextView private lateinit var saveButton: View + private var backToGlobalActions = true private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) { private val startingUser = controller.currentUserId @@ -82,6 +86,11 @@ class ControlsEditingActivity @Inject constructor( structure = it } ?: run(this::finish) + backToGlobalActions = intent.getBooleanExtra( + ControlsUiController.BACK_TO_GLOBAL_ACTIONS, + true + ) + bindViews() bindButtons() @@ -100,7 +109,11 @@ class ControlsEditingActivity @Inject constructor( } override fun onBackPressed() { - globalActionsComponent.handleShowGlobalActionsMenu() + if (backToGlobalActions) { + globalActionsComponent.handleShowGlobalActionsMenu() + } else { + ControlsDialog(applicationContext, broadcastDispatcher).show(uiController) + } animateExitAndFinish() } diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt index b2821579c389..2d647a907b17 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt @@ -40,6 +40,8 @@ import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.TooltipManager import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.StructureInfo +import com.android.systemui.controls.ui.ControlsDialog +import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.globalactions.GlobalActionsComponent import com.android.systemui.settings.CurrentUserTracker @@ -53,8 +55,9 @@ class ControlsFavoritingActivity @Inject constructor( @Main private val executor: Executor, private val controller: ControlsControllerImpl, private val listingController: ControlsListingController, - broadcastDispatcher: BroadcastDispatcher, - private val globalActionsComponent: GlobalActionsComponent + private val broadcastDispatcher: BroadcastDispatcher, + private val globalActionsComponent: GlobalActionsComponent, + private val uiController: ControlsUiController ) : LifecycleActivity() { companion object { @@ -89,6 +92,7 @@ class ControlsFavoritingActivity @Inject constructor( private lateinit var comparator: Comparator<StructureContainer> private var cancelLoadRunnable: Runnable? = null private var isPagerLoaded = false + private var backToGlobalActions = true private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) { private val startingUser = controller.currentUserId @@ -114,7 +118,7 @@ class ControlsFavoritingActivity @Inject constructor( override fun onBackPressed() { if (!fromProviderSelector) { - globalActionsComponent.handleShowGlobalActionsMenu() + openControlsOrigin() } animateExitAndFinish() } @@ -129,6 +133,11 @@ class ControlsFavoritingActivity @Inject constructor( component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME) fromProviderSelector = intent.getBooleanExtra(EXTRA_FROM_PROVIDER_SELECTOR, false) + backToGlobalActions = intent.getBooleanExtra( + ControlsUiController.BACK_TO_GLOBAL_ACTIONS, + true + ) + bindViews() } @@ -304,6 +313,10 @@ class ControlsFavoritingActivity @Inject constructor( setOnClickListener { val i = Intent().apply { component = ComponentName(context, ControlsProviderSelectorActivity::class.java) + putExtra( + ControlsUiController.BACK_TO_GLOBAL_ACTIONS, + backToGlobalActions + ) } if (doneButton.isEnabled) { // The user has made changes @@ -330,11 +343,19 @@ class ControlsFavoritingActivity @Inject constructor( ) } animateExitAndFinish() - globalActionsComponent.handleShowGlobalActionsMenu() + openControlsOrigin() } } } + private fun openControlsOrigin() { + if (backToGlobalActions) { + globalActionsComponent.handleShowGlobalActionsMenu() + } else { + ControlsDialog(applicationContext, broadcastDispatcher).show(uiController) + } + } + override fun onPause() { super.onPause() mTooltipManager?.hide(false) diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt index 08147746a4c8..d5e41d031eac 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt @@ -32,6 +32,8 @@ import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver import com.android.systemui.R import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.controls.controller.ControlsController +import com.android.systemui.controls.ui.ControlsDialog +import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.globalactions.GlobalActionsComponent @@ -49,13 +51,15 @@ class ControlsProviderSelectorActivity @Inject constructor( private val listingController: ControlsListingController, private val controlsController: ControlsController, private val globalActionsComponent: GlobalActionsComponent, - broadcastDispatcher: BroadcastDispatcher + private val broadcastDispatcher: BroadcastDispatcher, + private val uiController: ControlsUiController ) : LifecycleActivity() { companion object { private const val TAG = "ControlsProviderSelectorActivity" } + private var backToGlobalActions = true private lateinit var recyclerView: RecyclerView private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) { private val startingUser = listingController.currentUserId @@ -101,10 +105,19 @@ class ControlsProviderSelectorActivity @Inject constructor( } } requireViewById<View>(R.id.done).visibility = View.GONE + + backToGlobalActions = intent.getBooleanExtra( + ControlsUiController.BACK_TO_GLOBAL_ACTIONS, + true + ) } override fun onBackPressed() { - globalActionsComponent.handleShowGlobalActionsMenu() + if (backToGlobalActions) { + globalActionsComponent.handleShowGlobalActionsMenu() + } else { + ControlsDialog(applicationContext, broadcastDispatcher).show(uiController) + } animateExitAndFinish() } @@ -152,8 +165,13 @@ class ControlsProviderSelectorActivity @Inject constructor( listingController.getAppLabel(it)) putExtra(Intent.EXTRA_COMPONENT_NAME, it) putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true) + putExtra( + ControlsUiController.BACK_TO_GLOBAL_ACTIONS, + backToGlobalActions + ) } startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle()) + animateExitAndFinish() } } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt index db68d17461fa..537334aeb2f7 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt @@ -67,7 +67,7 @@ class ControlsDialog @Inject constructor( val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls) vg.alpha = 0f - controller.show(vg, { /* do nothing */ }, false /* startedFromGlobalActions */) + controller.show(vg, { dismiss() }, false /* startedFromGlobalActions */) vg.animate() .alpha(1f) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt index 944887741721..20bdf609357e 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt @@ -27,6 +27,7 @@ interface ControlsUiController { companion object { public const val TAG = "ControlsUiController" public const val EXTRA_ANIMATE = "extra_animate" + public const val BACK_TO_GLOBAL_ACTIONS = "back_to_global_actions" } fun show(parent: ViewGroup, onDismiss: Runnable, startedFromGlobalActions: Boolean) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 762362cde095..c94d85aa58c4 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -266,6 +266,10 @@ class ControlsUiControllerImpl @Inject constructor ( private fun startActivity(context: Context, intent: Intent) { // Force animations when transitioning from a dialog to an activity intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true) + intent.putExtra( + ControlsUiController.BACK_TO_GLOBAL_ACTIONS, + controlActionCoordinator.startedFromGlobalActions + ) onDismiss.run() activityStarter.dismissKeyguardThenExecute({ diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index 2b362b94d1f5..8d2639d4cdd0 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -22,6 +22,7 @@ import com.android.systemui.ForegroundServicesDialog; import com.android.systemui.keyguard.WorkLockActivity; import com.android.systemui.people.PeopleSpaceActivity; import com.android.systemui.screenrecord.ScreenRecordDialog; +import com.android.systemui.screenshot.LongScreenshotActivity; import com.android.systemui.settings.brightness.BrightnessDialog; import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity; import com.android.systemui.tuner.TunerActivity; @@ -99,4 +100,10 @@ public abstract class DefaultActivityBinder { @IntoMap @ClassKey(PeopleSpaceActivity.class) public abstract Activity bindPeopleSpaceActivity(PeopleSpaceActivity activity); + + /** Inject into LongScreenshotActivity. */ + @Binds + @IntoMap + @ClassKey(LongScreenshotActivity.class) + public abstract Activity bindLongScreenshotActivity(LongScreenshotActivity activity); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 91c2dcfd9202..ffb8446f3e21 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -16,7 +16,6 @@ package com.android.systemui.dagger; -import com.android.keyguard.clock.ClockOptionsProvider; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.Dependency; import com.android.systemui.InitController; @@ -147,9 +146,4 @@ public interface SysUIComponent { * Member injection into the supplied argument. */ void inject(KeyguardSliceProvider keyguardSliceProvider); - - /** - * Member injection into the supplied argument. - */ - void inject(ClockOptionsProvider clockOptionsProvider); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index ec3188a6144f..5d226d562c29 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -29,6 +29,7 @@ import com.android.systemui.keyguard.dagger.KeyguardModule; import com.android.systemui.media.systemsounds.HomeSoundEffectController; import com.android.systemui.people.widget.PeopleSpaceWidgetEnabler; import com.android.systemui.power.PowerUI; +import com.android.systemui.privacy.television.TvOngoingPrivacyChip; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsModule; import com.android.systemui.shortcut.ShortcutKeyDispatcher; @@ -155,6 +156,12 @@ public abstract class SystemUIBinder { @ClassKey(TvNotificationPanel.class) public abstract SystemUI bindsTvNotificationPanel(TvNotificationPanel sysui); + /** Inject into TvOngoingPrivacyChip. */ + @Binds + @IntoMap + @ClassKey(TvOngoingPrivacyChip.class) + public abstract SystemUI bindsTvOngoingPrivacyChip(TvOngoingPrivacyChip sysui); + /** Inject into VolumeUI. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index b67db03a743c..b0067cd15c1b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -22,7 +22,6 @@ import android.content.Context; import androidx.annotation.Nullable; import com.android.internal.statusbar.IStatusBarService; -import com.android.keyguard.clock.ClockModule; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; @@ -91,7 +90,6 @@ import dagger.Provides; @Module(includes = { AppOpsModule.class, AssistModule.class, - ClockModule.class, ControlsModule.class, DemoModeModule.class, FalsingModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 5c8c9f22d585..4418696bfc9b 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -23,6 +23,7 @@ import android.app.AlarmManager; import android.content.Context; import android.os.Handler; import android.os.SystemClock; +import android.provider.Settings; import android.text.format.Formatter; import android.util.Log; @@ -31,7 +32,9 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.dagger.DozeScope; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.tuner.TunerService; import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.wakelock.WakeLock; @@ -39,12 +42,15 @@ import java.util.Calendar; import javax.inject.Inject; +import dagger.Lazy; + /** * The policy controlling doze. */ @DozeScope -public class DozeUi implements DozeMachine.Part { - +public class DozeUi implements DozeMachine.Part, TunerService.Tunable { + // if enabled, calls dozeTimeTick() whenever the time changes: + private static final boolean BURN_IN_TESTING_ENABLED = false; private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min private final Context mContext; private final DozeHost mHost; @@ -55,16 +61,28 @@ public class DozeUi implements DozeMachine.Part { private final boolean mCanAnimateTransition; private final DozeParameters mDozeParameters; private final DozeLog mDozeLog; + private final Lazy<StatusBarStateController> mStatusBarStateController; private boolean mKeyguardShowing; private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback = new KeyguardUpdateMonitorCallback() { - @Override public void onKeyguardVisibilityChanged(boolean showing) { mKeyguardShowing = showing; updateAnimateScreenOff(); } + + @Override + public void onTimeChanged() { + if (BURN_IN_TESTING_ENABLED && mStatusBarStateController != null + && mStatusBarStateController.get().isDozing()) { + // update whenever the time changes for manual burn in testing + mHost.dozeTimeTick(); + + // Keep wakelock until a frame has been pushed. + mHandler.post(mWakeLock.wrap(() -> {})); + } + } }; private long mLastTimeTickElapsed = 0; @@ -73,7 +91,8 @@ public class DozeUi implements DozeMachine.Part { public DozeUi(Context context, AlarmManager alarmManager, WakeLock wakeLock, DozeHost host, @Main Handler handler, DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor, - DozeLog dozeLog) { + DozeLog dozeLog, TunerService tunerService, + Lazy<StatusBarStateController> statusBarStateController) { mContext = context; mWakeLock = wakeLock; mHost = host; @@ -83,6 +102,8 @@ public class DozeUi implements DozeMachine.Part { mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler); keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback); mDozeLog = dozeLog; + tunerService.addTunable(this, Settings.Secure.DOZE_ALWAYS_ON); + mStatusBarStateController = statusBarStateController; } @Override @@ -238,4 +259,11 @@ public class DozeUi implements DozeMachine.Part { KeyguardUpdateMonitorCallback getKeyguardCallback() { return mKeyguardVisibilityCallback; } + + @Override + public void onTuningChanged(String key, String newValue) { + if (key.equals(Settings.Secure.DOZE_ALWAYS_ON)) { + updateAnimateScreenOff(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java index b77fcc822b88..073586e88a8b 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java @@ -16,24 +16,18 @@ package com.android.systemui.flags; -import android.annotation.NonNull; import android.content.res.Resources; -import android.provider.DeviceConfig; import android.util.SparseArray; import androidx.annotation.BoolRes; import androidx.annotation.Nullable; import com.android.systemui.R; -import com.android.systemui.assist.DeviceConfigHelper; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.util.wrapper.BuildInfo; -import java.util.concurrent.Executor; - import javax.inject.Inject; /** * Reads and caches feature flags for quick access @@ -60,23 +54,19 @@ import javax.inject.Inject; @SysUISingleton public class FeatureFlagReader { private final Resources mResources; - private final DeviceConfigHelper mDeviceConfig; private final boolean mAreFlagsOverrideable; - + private final SystemPropertiesHelper mSystemPropertiesHelper; private final SparseArray<CachedFlag> mCachedFlags = new SparseArray<>(); @Inject public FeatureFlagReader( @Main Resources resources, BuildInfo build, - DeviceConfigHelper deviceConfig, - @Background Executor executor) { + SystemPropertiesHelper systemPropertiesHelper) { mResources = resources; - mDeviceConfig = deviceConfig; + mSystemPropertiesHelper = systemPropertiesHelper; mAreFlagsOverrideable = build.isDebuggable() && mResources.getBoolean(R.bool.are_flags_overrideable); - - mDeviceConfig.addOnPropertiesChangedListener(executor, this::onPropertiesChanged); } /** @@ -93,7 +83,7 @@ public class FeatureFlagReader { String name = resourceIdToFlagName(resId); boolean value = mResources.getBoolean(resId); if (mAreFlagsOverrideable) { - value = mDeviceConfig.getBoolean(flagNameToStorageKey(name), value); + value = mSystemPropertiesHelper.getBoolean(flagNameToStorageKey(name), value); } cachedFlag = new CachedFlag(name, value); @@ -104,27 +94,6 @@ public class FeatureFlagReader { } } - private void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { - synchronized (mCachedFlags) { - for (String key : properties.getKeyset()) { - String flagName = storageKeyToFlagName(key); - if (flagName != null) { - clearCachedFlag(flagName); - } - } - } - } - - private void clearCachedFlag(String flagName) { - for (int i = 0; i < mCachedFlags.size(); i++) { - CachedFlag flag = mCachedFlags.valueAt(i); - if (flag.name.equals(flagName)) { - mCachedFlags.removeAt(i); - break; - } - } - } - private String resourceIdToFlagName(@BoolRes int resId) { String resName = mResources.getResourceEntryName(resId); if (resName.startsWith(RESNAME_PREFIX)) { @@ -160,6 +129,6 @@ public class FeatureFlagReader { } } - private static final String STORAGE_KEY_PREFIX = "flag_"; + private static final String STORAGE_KEY_PREFIX = "persist.systemui.flag_"; private static final String RESNAME_PREFIX = "flag_"; } diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt new file mode 100644 index 000000000000..28f63b07e584 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt @@ -0,0 +1,32 @@ +/* + * 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.os.SystemProperties +import com.android.systemui.dagger.SysUISingleton + +import javax.inject.Inject + +/** + * Proxy to make {@link SystemProperties} easily testable. + */ +@SysUISingleton +class SystemPropertiesHelper @Inject constructor() { + fun getBoolean(name: String, default: Boolean): Boolean { + return SystemProperties.getBoolean(name, default) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java index 1a0356c4446d..01a353ce8f1f 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java @@ -58,6 +58,14 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer { mWallpaper = new ImageGLWallpaper(mProgram); } + /** + * @hide + * @return + */ + public void useBitmap(Consumer<Bitmap> c) { + mTexture.use(c); + } + @Override public boolean isWcgContent() { return mTexture.isWcgContent(); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java index d4678f39e404..127128dda112 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java @@ -103,6 +103,11 @@ public class KeyguardIndicationRotateTextViewController extends */ public void updateIndication(@IndicationType int type, KeyguardIndication newIndication, boolean showImmediately) { + if (type == INDICATION_TYPE_NOW_PLAYING + || type == INDICATION_TYPE_REVERSE_CHARGING) { + // temporarily don't show here, instead use AmbientContainer b/181049781 + return; + } final boolean hasPreviousIndication = mIndicationMessages.get(type) != null; final boolean hasNewIndication = newIndication != null; if (!hasNewIndication) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 91cf7108c728..eef41e045948 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2395,6 +2395,9 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, return; } mDozing = dozing; + if (!dozing) { + mAnimatingScreenOff = false; + } setShowingLocked(mShowing); } @@ -2404,7 +2407,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, // is 1f), then show the activity lock screen. if (mAnimatingScreenOff && mDozing && linear == 1f) { mAnimatingScreenOff = false; - setShowingLocked(mShowing); + setShowingLocked(mShowing, true); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index a747edd0580a..de2e7c476e18 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -30,7 +30,6 @@ import com.android.keyguard.KeyguardDisplayManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardViewController; import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; -import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -64,11 +63,8 @@ import dagger.Provides; /** * Dagger Module providing {@link StatusBar}. */ -@Module(subcomponents = { - KeyguardQsUserSwitchComponent.class, - KeyguardStatusBarViewComponent.class, - KeyguardStatusViewComponent.class, - KeyguardUserSwitcherComponent.class}, +@Module(subcomponents = {KeyguardStatusViewComponent.class, + KeyguardQsUserSwitchComponent.class, KeyguardUserSwitcherComponent.class}, includes = {FalsingModule.class}) public class KeyguardModule { /** diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index dab4d0bb00c5..553623702aed 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -855,6 +855,14 @@ public class NavigationBar implements View.OnAttachStateChangeListener, @Override public void onRotationProposal(final int rotation, boolean isValid) { + if (mNavigationBarView == null) { + if (RotationContextButton.DEBUG_ROTATION) { + Log.v(TAG, "onRotationProposal proposedRotation=" + + Surface.rotationToString(rotation) + ", mNavigationBarView is null"); + } + return; + } + final int winRotation = mNavigationBarView.getDisplay().getRotation(); final boolean rotateSuggestionsDisabled = RotationButtonController .hasDisable2RotateSuggestionFlag(mDisabledFlags2); @@ -1116,7 +1124,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } // If an incoming call is ringing, HOME is totally disabled. // (The user is already on the InCallUI at this point, - // and his ONLY options are to answer or reject the call.) + // and their ONLY options are to answer or reject the call.) switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mHomeBlockedThisTouch = false; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index 27ea64f85b11..70b7b047eebc 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -25,7 +25,6 @@ import android.hardware.display.DisplayManager; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import android.util.SparseArray; import android.view.Display; @@ -54,7 +53,6 @@ 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.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -116,7 +114,7 @@ public class NavigationBarController implements Callbacks, // Tracks config changes that will actually recreate the nav bar private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE - | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS + | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_UI_MODE); @Inject @@ -171,6 +169,7 @@ public class NavigationBarController implements Callbacks, configurationController.addCallback(this); mConfigChanges.applyNewConfig(mContext.getResources()); mNavBarOverlayController = navBarOverlayController; + mNavigationModeController.addListener(this); } @Override @@ -188,17 +187,18 @@ public class NavigationBarController implements Callbacks, @Override public void onNavigationModeChanged(int mode) { - // Workaround for b/132825155, for secondary users, we currently don't receive configuration - // changes on overlay package change since SystemUI runs for the system user. In this case, - // trigger a new configuration change to ensure that the nav bar is updated in the same way. - int userId = ActivityManagerWrapper.getInstance().getCurrentUserId(); - if (userId != UserHandle.USER_SYSTEM) { - mHandler.post(() -> { - for (int i = 0; i < mNavigationBars.size(); i++) { - recreateNavigationBar(mNavigationBars.keyAt(i)); + mHandler.post(() -> { + for (int i = 0; i < mNavigationBars.size(); i++) { + NavigationBar navBar = mNavigationBars.valueAt(i); + if (navBar == null) { + continue; } - }); - } + NavigationBarView view = (NavigationBarView) mNavigationBars.get(i).getView(); + if (view != null) { + view.updateStates(); + } + } + }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java index c526c5d59552..62b9458447d8 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java @@ -21,6 +21,7 @@ import android.content.Context; import android.view.View; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.FeatureFlags; import java.util.function.Consumer; @@ -31,16 +32,22 @@ import javax.inject.Inject; public class NavigationBarOverlayController { protected final Context mContext; + protected final FeatureFlags mFeatureFlags; @Inject - public NavigationBarOverlayController(Context context) { + public NavigationBarOverlayController(Context context, FeatureFlags featureFlags) { mContext = context; + mFeatureFlags = featureFlags; } public Context getContext() { return mContext; } + public boolean isNavigationBarOverlayEnabled() { + return mFeatureFlags.isNavigationBarOverlayEnabled(); + } + /** * Initialize the controller with visibility change callback and light/dark icon color. */ diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java index b55fa4d612f9..61e1d61e7909 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java @@ -192,6 +192,7 @@ public final class NavigationBarTransitions extends BarTransitions implements buttonDispatchers.valueAt(i).setDarkIntensity(darkIntensity); } mView.getRotationButtonController().setDarkIntensity(darkIntensity); + Dependency.get(NavigationBarOverlayController.class).setDarkIntensity(darkIntensity); for (DarkIntensityListener listener : mDarkIntensityListeners) { listener.onDarkIntensity(darkIntensity); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index c07404c2e34d..19e32783f765 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -76,6 +76,7 @@ import com.android.systemui.navigationbar.buttons.ContextualButton; import com.android.systemui.navigationbar.buttons.ContextualButtonGroup; import com.android.systemui.navigationbar.buttons.DeadZone; import com.android.systemui.navigationbar.buttons.KeyButtonDrawable; +import com.android.systemui.navigationbar.buttons.NearestTouchFrame; import com.android.systemui.navigationbar.buttons.RotationContextButton; import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; import com.android.systemui.navigationbar.gestural.FloatingRotationButton; @@ -97,6 +98,8 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.Pip; import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; import java.util.function.Consumer; public class NavigationBarView extends FrameLayout implements @@ -129,6 +132,7 @@ public class NavigationBarView extends FrameLayout implements private final Region mTmpRegion = new Region(); private final int[] mTmpPosition = new int[2]; private Rect mTmpBounds = new Rect(); + private Map<View, Rect> mButtonFullTouchableRegions = new HashMap<>(); private KeyButtonDrawable mBackIcon; private KeyButtonDrawable mHomeDefaultIcon; @@ -167,6 +171,7 @@ public class NavigationBarView extends FrameLayout implements private NotificationPanelViewController mPanelView; private FloatingRotationButton mFloatingRotationButton; private RotationButtonController mRotationButtonController; + private NavigationBarOverlayController mNavBarOverlayController; /** * Helper that is responsible for showing the right toast when a disallowed activity operation @@ -335,8 +340,11 @@ public class NavigationBarView extends FrameLayout implements isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton, mRotationButtonListener); - Dependency.get(NavigationBarOverlayController.class).init( - mNavbarOverlayVisibilityChangeCallback, mLightIconColor, mDarkIconColor); + mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class); + if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) { + mNavBarOverlayController.init( + mNavbarOverlayVisibilityChangeCallback, mLightIconColor, mDarkIconColor); + } mConfiguration = new Configuration(); mTmpLastConfiguration = new Configuration(); @@ -427,8 +435,9 @@ public class NavigationBarView extends FrameLayout implements // The visibility of the navigation bar buttons is dependent on the transient state of // the navigation bar. - Dependency.get(NavigationBarOverlayController.class).setButtonState( - isTransient, /* force */ false); + if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) { + mNavBarOverlayController.setButtonState(isTransient, /* force */ false); + } } void onBarTransition(int newMode) { @@ -662,7 +671,9 @@ public class NavigationBarView extends FrameLayout implements } mImeVisible = visible; mRotationButtonController.getRotationButton().setCanShowRotationButton(!mImeVisible); - Dependency.get(NavigationBarOverlayController.class).setCanShow(!mImeVisible); + if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) { + mNavBarOverlayController.setCanShow(!mImeVisible); + } } public void setDisabledFlags(int disabledFlags) { @@ -973,9 +984,18 @@ public class NavigationBarView extends FrameLayout implements getButtonLocations(true /* includeFloatingRotationButton */, true /* inScreen */)); } + private void updateButtonTouchRegionCache() { + FrameLayout navBarLayout = mIsVertical + ? mNavigationInflaterView.mVertical + : mNavigationInflaterView.mHorizontal; + mButtonFullTouchableRegions = ((NearestTouchFrame) navBarLayout + .findViewById(R.id.nav_buttons)).getFullTouchableChildRegions(); + } + private Region getButtonLocations(boolean includeFloatingRotationButton, boolean inScreenSpace) { mTmpRegion.setEmpty(); + updateButtonTouchRegionCache(); updateButtonLocation(getBackButton(), inScreenSpace); updateButtonLocation(getHomeButton(), inScreenSpace); updateButtonLocation(getRecentsButton(), inScreenSpace); @@ -986,10 +1006,9 @@ public class NavigationBarView extends FrameLayout implements } else { updateButtonLocation(getRotateSuggestionButton(), inScreenSpace); } - final NavigationBarOverlayController navBarButtonsController = - Dependency.get(NavigationBarOverlayController.class); - if (navBarButtonsController.isVisible()) { - updateButtonLocation(navBarButtonsController.getCurrentView(), inScreenSpace); + if (mNavBarOverlayController.isNavigationBarOverlayEnabled() + && mNavBarOverlayController.isVisible()) { + updateButtonLocation(mNavBarOverlayController.getCurrentView(), inScreenSpace); } return mTmpRegion; } @@ -999,6 +1018,12 @@ public class NavigationBarView extends FrameLayout implements if (view == null || !button.isVisible()) { return; } + // If the button is tappable from perspective of NearestTouchFrame, then we'll + // include the regions where the tap is valid instead of just the button layout location + if (mButtonFullTouchableRegions.containsKey(view)) { + mTmpRegion.op(mButtonFullTouchableRegions.get(view), Op.UNION); + return; + } updateButtonLocation(view, inScreenSpace); } @@ -1211,7 +1236,9 @@ public class NavigationBarView extends FrameLayout implements if (mRotationButtonController != null) { mRotationButtonController.registerListeners(); } - Dependency.get(NavigationBarOverlayController.class).registerListeners(); + if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) { + mNavBarOverlayController.registerListeners(); + } getViewTreeObserver().addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener); updateNavButtonIcons(); @@ -1228,7 +1255,10 @@ public class NavigationBarView extends FrameLayout implements if (mRotationButtonController != null) { mRotationButtonController.unregisterListeners(); } - Dependency.get(NavigationBarOverlayController.class).unregisterListeners(); + + if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) { + mNavBarOverlayController.unregisterListeners(); + } mEdgeBackGestureHandler.onNavBarDetached(); getViewTreeObserver().removeOnComputeInternalInsetsListener( diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java index 65d4e23f7734..870e3bed2b7a 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java @@ -132,7 +132,7 @@ public class NavigationModeController implements Dumpable { Settings.Secure.putString(mCurrentUserContext.getContentResolver(), Secure.NAVIGATION_MODE, String.valueOf(mode))); if (DEBUG) { - Log.e(TAG, "updateCurrentInteractionMode: mode=" + mode); + Log.d(TAG, "updateCurrentInteractionMode: mode=" + mode); dumpAssetPaths(mCurrentUserContext); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java index 88c8fea085fb..8ff7b7ae8e59 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java @@ -18,8 +18,9 @@ package com.android.systemui.navigationbar.buttons; import android.content.Context; import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.graphics.Rect; import android.util.AttributeSet; -import android.util.Pair; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -27,8 +28,13 @@ import android.widget.FrameLayout; import androidx.annotation.VisibleForTesting; +import com.android.systemui.R; + import java.util.ArrayList; import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Redirects touches that aren't handled by any child view to the nearest @@ -36,11 +42,32 @@ import java.util.Comparator; */ public class NearestTouchFrame extends FrameLayout { - private final ArrayList<View> mClickableChildren = new ArrayList<>(); + private final List<View> mClickableChildren = new ArrayList<>(); + private final List<View> mAttachedChildren = new ArrayList<>(); private final boolean mIsActive; private final int[] mTmpInt = new int[2]; private final int[] mOffset = new int[2]; + private boolean mIsVertical; private View mTouchingChild; + private final Map<View, Rect> mTouchableRegions = new HashMap<>(); + /** + * Used to sort all child views either by their left position or their top position, + * depending on if this layout is used horizontally or vertically, respectively + */ + private final Comparator<View> mChildRegionComparator = + (view1, view2) -> { + int leftTopIndex = 0; + if (mIsVertical) { + // Compare view bound's "top" values + leftTopIndex = 1; + } + view1.getLocationInWindow(mTmpInt); + int startingCoordView1 = mTmpInt[leftTopIndex] - mOffset[leftTopIndex]; + view2.getLocationInWindow(mTmpInt); + int startingCoordView2 = mTmpInt[leftTopIndex] - mOffset[leftTopIndex]; + + return startingCoordView1 - startingCoordView2; + }; public NearestTouchFrame(Context context, AttributeSet attrs) { this(context, attrs, context.getResources().getConfiguration()); @@ -50,19 +77,100 @@ public class NearestTouchFrame extends FrameLayout { NearestTouchFrame(Context context, AttributeSet attrs, Configuration c) { super(context, attrs); mIsActive = c.smallestScreenWidthDp < 600; + int[] attrsArray = new int[] {R.attr.isVertical}; + TypedArray ta = context.obtainStyledAttributes(attrs, attrsArray); + mIsVertical = ta.getBoolean(0, false); + ta.recycle(); } @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); mClickableChildren.clear(); + mAttachedChildren.clear(); + mTouchableRegions.clear(); addClickableChildren(this); + getLocationInWindow(mOffset); + cacheClosestChildLocations(); } - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - getLocationInWindow(mOffset); + /** + * Populates {@link #mTouchableRegions} with the regions where each clickable child is the + * closest for a given point on this layout. + */ + private void cacheClosestChildLocations() { + if (getWidth() == 0 || getHeight() == 0) { + return; + } + + // Sort by either top or left depending on mIsVertical, then take out all children + // that are not attached to window + mClickableChildren.sort(mChildRegionComparator); + mClickableChildren.stream() + .filter(View::isAttachedToWindow) + .forEachOrdered(mAttachedChildren::add); + + // Cache bounds of children + // Mark coordinates where the actual child layout resides in this frame's window + for (int i = 0; i < mAttachedChildren.size(); i++) { + View child = mAttachedChildren.get(i); + if (!child.isAttachedToWindow()) { + continue; + } + Rect childRegion = getChildsBounds(child); + + // We compute closest child from this child to the previous one + if (i == 0) { + // First child, nothing to the left/top of it + if (mIsVertical) { + childRegion.top = 0; + } else { + childRegion.left = 0; + } + mTouchableRegions.put(child, childRegion); + continue; + } + + View previousChild = mAttachedChildren.get(i - 1); + Rect previousChildBounds = mTouchableRegions.get(previousChild); + int midPoint; + if (mIsVertical) { + int distance = childRegion.top - previousChildBounds.bottom; + midPoint = distance / 2; + childRegion.top -= midPoint; + previousChildBounds.bottom += midPoint - ((distance % 2) == 0 ? 1 : 0); + } else { + int distance = childRegion.left - previousChildBounds.right; + midPoint = distance / 2; + childRegion.left -= midPoint; + previousChildBounds.right += midPoint - ((distance % 2) == 0 ? 1 : 0); + } + + if (i == mClickableChildren.size() - 1) { + // Last child, nothing to right/bottom of it + if (mIsVertical) { + childRegion.bottom = getHeight(); + } else { + childRegion.right = getWidth(); + } + } + + mTouchableRegions.put(child, childRegion); + } + } + + @VisibleForTesting + void setIsVertical(boolean isVertical) { + mIsVertical = isVertical; + } + + private Rect getChildsBounds(View child) { + child.getLocationInWindow(mTmpInt); + int left = mTmpInt[0] - mOffset[0]; + int top = mTmpInt[1] - mOffset[1]; + int right = left + child.getWidth(); + int bottom = top + child.getHeight(); + return new Rect(left, top, right, bottom); } private void addClickableChildren(ViewGroup group) { @@ -77,47 +185,45 @@ public class NearestTouchFrame extends FrameLayout { } } + /** + * @return A Map where the key is the view object of the button and the value + * is the Rect where that button will receive a touch event if pressed. This Rect will + * usually be larger than the layout bounds for the button. + * The Rect is in screen coordinates. + */ + public Map<View, Rect> getFullTouchableChildRegions() { + Map<View, Rect> fullTouchRegions = new HashMap<>(mTouchableRegions.size()); + getLocationOnScreen(mTmpInt); + for (Map.Entry<View, Rect> entry : mTouchableRegions.entrySet()) { + View child = entry.getKey(); + Rect screenRegion = new Rect(entry.getValue()); + screenRegion.offset(mTmpInt[0], mTmpInt[1]); + fullTouchRegions.put(child, screenRegion); + } + return fullTouchRegions; + } + @Override public boolean onTouchEvent(MotionEvent event) { if (mIsActive) { + int x = (int) event.getX(); + int y = (int) event.getY(); if (event.getAction() == MotionEvent.ACTION_DOWN) { - mTouchingChild = findNearestChild(event); + mTouchingChild = mClickableChildren + .stream() + .filter(View::isAttachedToWindow) + .filter(view -> mTouchableRegions.get(view).contains(x, y)) + .findFirst() + .orElse(null); + } if (mTouchingChild != null) { - event.offsetLocation(mTouchingChild.getWidth() / 2 - event.getX(), - mTouchingChild.getHeight() / 2 - event.getY()); + event.offsetLocation(mTouchingChild.getWidth() / 2 - x, + mTouchingChild.getHeight() / 2 - y); return mTouchingChild.getVisibility() == VISIBLE && mTouchingChild.dispatchTouchEvent(event); } } return super.onTouchEvent(event); } - - private View findNearestChild(MotionEvent event) { - if (mClickableChildren.isEmpty()) { - return null; - } - return mClickableChildren - .stream() - .filter(View::isAttachedToWindow) - .map(v -> new Pair<>(distance(v, event), v)) - .min(Comparator.comparingInt(f -> f.first)) - .map(data -> data.second) - .orElse(null); - } - - private int distance(View v, MotionEvent event) { - v.getLocationInWindow(mTmpInt); - int left = mTmpInt[0] - mOffset[0]; - int top = mTmpInt[1] - mOffset[1]; - int right = left + v.getWidth(); - int bottom = top + v.getHeight(); - - int x = Math.min(Math.abs(left - (int) event.getX()), - Math.abs((int) event.getX() - right)); - int y = Math.min(Math.abs(top - (int) event.getY()), - Math.abs((int) event.getY() - bottom)); - - return Math.max(x, y); - } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index d7a3537a4a5f..088743cd0f94 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -38,6 +38,7 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.Choreographer; +import android.view.Display; import android.view.ISystemGestureExclusionListener; import android.view.InputDevice; import android.view.InputEvent; @@ -92,10 +93,16 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt( "gestures.back_timeout", 250); + private static final int MAX_NUM_LOGGED_PREDICTIONS = 10; + private static final int MAX_NUM_LOGGED_GESTURES = 10; + // Temporary log until b/176302696 is resolved static final boolean DEBUG_MISSING_GESTURE = true; static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture"; + private static final boolean ENABLE_PER_WINDOW_INPUT_ROTATION = + SystemProperties.getBoolean("persist.debug.per_window_input_rotation", false); + private ISystemGestureExclusionListener mGestureExclusionListener = new ISystemGestureExclusionListener.Stub() { @Override @@ -218,8 +225,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa private String mPackageName; private float mMLResults; - private static final int MAX_LOGGED_PREDICTIONS = 10; + // For debugging private ArrayDeque<String> mPredictionLog = new ArrayDeque<>(); + private ArrayDeque<String> mGestureLog = new ArrayDeque<>(); private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver; @@ -505,9 +513,19 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa } private void onInputEvent(InputEvent ev) { - if (ev instanceof MotionEvent) { - onMotionEvent((MotionEvent) ev); + if (!(ev instanceof MotionEvent)) return; + MotionEvent event = (MotionEvent) ev; + if (ENABLE_PER_WINDOW_INPUT_ROTATION) { + final Display display = mContext.getDisplay(); + int rotation = display.getRotation(); + if (rotation != Surface.ROTATION_0) { + Point sz = new Point(); + display.getRealSize(sz); + event = MotionEvent.obtain(event); + event.transform(MotionEvent.createRotateMatrix(rotation, sz.x, sz.y)); + } } + onMotionEvent(event); } private void updateMLModelState() { @@ -593,7 +611,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa } // Check if we are within the tightest bounds beyond which // we would not need to run the ML model. - boolean withinRange = x <= mMLEnableWidth + mLeftInset + boolean withinRange = x < mMLEnableWidth + mLeftInset || x >= (mDisplaySize.x - mMLEnableWidth - mRightInset); if (!withinRange) { int results = -1; @@ -603,17 +621,20 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa // Denotes whether we should proceed with the gesture. // Even if it is false, we may want to log it assuming // it is not invalid due to exclusion. - withinRange = x <= mEdgeWidthLeft + mLeftInset + withinRange = x < mEdgeWidthLeft + mLeftInset || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset); } } // For debugging purposes - if (mPredictionLog.size() >= MAX_LOGGED_PREDICTIONS) { + if (mPredictionLog.size() >= MAX_NUM_LOGGED_PREDICTIONS) { mPredictionLog.removeFirst(); } - mPredictionLog.addLast(String.format("[%d,%d,%d,%f,%d]", - x, y, app, mMLResults, withinRange ? 1 : 0)); + mPredictionLog.addLast(String.format("Prediction [%d,%d,%d,%d,%f,%d]", + System.currentTimeMillis(), x, y, app, mMLResults, withinRange ? 1 : 0)); + if (DEBUG_MISSING_GESTURE) { + Log.d(DEBUG_MISSING_GESTURE_TAG, mPredictionLog.peekLast()); + } // Always allow if the user is in a transient sticky immersive state if (mIsNavBarShownTransiently) { @@ -675,6 +696,10 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa private void onMotionEvent(MotionEvent ev) { int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) { + if (DEBUG_MISSING_GESTURE) { + Log.d(DEBUG_MISSING_GESTURE_TAG, "Start gesture: " + ev); + } + // Verify if this is in within the touch region and we aren't in immersive mode, and // either the bouncer is showing or the notification panel is hidden mInputEventReceiver.setBatchingEnabled(false); @@ -695,6 +720,19 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa mEndPoint.set(-1, -1); mThresholdCrossed = false; } + + // For debugging purposes + if (mGestureLog.size() >= MAX_NUM_LOGGED_GESTURES) { + mGestureLog.removeFirst(); + } + mGestureLog.addLast(String.format( + "Gesture [%d,alw=%B,%B, %B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]", + System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge, mIsBackGestureAllowed, + QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize, + mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion)); + if (DEBUG_MISSING_GESTURE) { + Log.d(DEBUG_MISSING_GESTURE_TAG, mGestureLog.peekLast()); + } } else if (mAllowGesture || mLogGesture) { if (!mThresholdCrossed) { mEndPoint.x = (int) ev.getX(); @@ -813,18 +851,29 @@ public class EdgeBackGestureHandler extends CurrentUserTracker implements Displa public void dump(PrintWriter pw) { pw.println("EdgeBackGestureHandler:"); pw.println(" mIsEnabled=" + mIsEnabled); + pw.println(" mIsAttached=" + mIsAttached); pw.println(" mIsBackGestureAllowed=" + mIsBackGestureAllowed); + pw.println(" mIsGesturalModeEnabled=" + mIsGesturalModeEnabled); + pw.println(" mIsNavBarShownTransiently=" + mIsNavBarShownTransiently); + pw.println(" mGestureBlockingActivityRunning=" + mGestureBlockingActivityRunning); pw.println(" mAllowGesture=" + mAllowGesture); + pw.println(" mUseMLModel=" + mUseMLModel); pw.println(" mDisabledForQuickstep=" + mDisabledForQuickstep); pw.println(" mStartingQuickstepRotation=" + mStartingQuickstepRotation); pw.println(" mInRejectedExclusion" + mInRejectedExclusion); pw.println(" mExcludeRegion=" + mExcludeRegion); pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion); - pw.println(" mIsAttached=" + mIsAttached); + pw.println(" mPipExcludedBounds=" + mPipExcludedBounds); pw.println(" mEdgeWidthLeft=" + mEdgeWidthLeft); pw.println(" mEdgeWidthRight=" + mEdgeWidthRight); - pw.println(" mIsNavBarShownTransiently=" + mIsNavBarShownTransiently); - pw.println(" mPredictionLog=" + String.join(";", mPredictionLog)); + pw.println(" mLeftInset=" + mLeftInset); + pw.println(" mRightInset=" + mRightInset); + pw.println(" mMLEnableWidth=" + mMLEnableWidth); + pw.println(" mMLModelThreshold=" + mMLModelThreshold); + pw.println(" mTouchSlop=" + mTouchSlop); + pw.println(" mBottomGestureHeight=" + mBottomGestureHeight); + pw.println(" mPredictionLog=" + String.join("\n", mPredictionLog)); + pw.println(" mGestureLog=" + String.join("\n", mGestureLog)); pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin); } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index 2f9b17aece8e..2ea8657f88e5 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -19,8 +19,6 @@ package com.android.systemui.people; import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID; import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID; -import static com.android.systemui.people.PeopleSpaceUtils.getUserHandle; - import android.app.Activity; import android.app.INotificationManager; import android.app.people.IPeopleManager; @@ -155,7 +153,7 @@ public class PeopleSpaceActivity extends Activity { if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId()); mLauncherApps.cacheShortcuts(tile.getPackageName(), Collections.singletonList(tile.getId()), - getUserHandle(tile), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); + tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); } catch (Exception e) { Log.w(TAG, "Exception caching shortcut:" + e); } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java index 9ae7847031aa..6f89332e66a9 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.pm.LauncherApps; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; -import android.os.UserHandle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -82,7 +81,7 @@ public class PeopleSpaceTileView extends LinearLayout { public void setOnClickListener(LauncherApps launcherApps, PeopleSpaceTile tile) { mTileView.setOnClickListener(v -> launcherApps.startShortcut(tile.getPackageName(), tile.getId(), null, null, - UserHandle.getUserHandleForUid(tile.getUid()))); + tile.getUserHandle())); } /** Sets the click listener of the tile directly. */ diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index cd1131ba3e79..41080afb3604 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -16,6 +16,7 @@ package com.android.systemui.people; +import static android.app.Notification.CATEGORY_MISSED_CALL; import static android.app.Notification.EXTRA_MESSAGES; import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY; import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY; @@ -54,7 +55,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; import android.os.ServiceManager; -import android.os.UserHandle; import android.provider.ContactsContract; import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; @@ -159,7 +159,6 @@ public class PeopleSpaceUtils { List<ConversationChannelWrapper> conversations = notificationManager.getConversations( false).getList(); - // Add priority conversations to tiles list. Stream<ShortcutInfo> priorityConversations = conversations.stream() .filter(c -> c.getNotificationChannel() != null @@ -189,7 +188,7 @@ public class PeopleSpaceUtils { tiles.addAll(recentTiles); } - tiles = augmentTilesFromVisibleNotifications(tiles, notificationEntryManager); + tiles = augmentTilesFromVisibleNotifications(context, tiles, notificationEntryManager); return tiles; } @@ -251,7 +250,11 @@ public class PeopleSpaceUtils { } // If tile is null, we need to retrieve from persisted storage. - if (DEBUG) Log.d(TAG, "Retrieving from storage after reboots"); + if (DEBUG) { + Log.d(TAG, + "Retrieving from storage after reboots: " + shortcutId + " user: " + userId + + " pkg: " + pkg); + } LauncherApps launcherApps = context.getSystemService(LauncherApps.class); ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId); if (channel == null) { @@ -357,8 +360,8 @@ public class PeopleSpaceUtils { && storedUserId == userId; } - static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(List<PeopleSpaceTile> tiles, - NotificationEntryManager notificationEntryManager) { + static List<PeopleSpaceTile> augmentTilesFromVisibleNotifications(Context context, + List<PeopleSpaceTile> tiles, NotificationEntryManager notificationEntryManager) { if (notificationEntryManager == null) { Log.w(TAG, "NotificationEntryManager is null"); return tiles; @@ -374,22 +377,23 @@ public class PeopleSpaceUtils { } return tiles .stream() - .map(entry -> augmentTileFromVisibleNotifications(entry, visibleNotifications)) + .map(entry -> augmentTileFromVisibleNotifications( + context, entry, visibleNotifications)) .collect(Collectors.toList()); } - static PeopleSpaceTile augmentTileFromVisibleNotifications(PeopleSpaceTile tile, - Map<String, NotificationEntry> visibleNotifications) { + static PeopleSpaceTile augmentTileFromVisibleNotifications(Context context, + PeopleSpaceTile tile, Map<String, NotificationEntry> visibleNotifications) { String shortcutId = tile.getId(); String packageName = tile.getPackageName(); - int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier(); + int userId = getUserId(tile); String key = getKey(shortcutId, packageName, userId); if (!visibleNotifications.containsKey(key)) { if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key); return tile; } if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key); - return augmentTileFromNotification(tile, visibleNotifications.get(key).getSbn()); + return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn()); } /** @@ -408,7 +412,7 @@ public class PeopleSpaceUtils { } if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId); - storedTile = augmentTileFromNotification(storedTile, sbn); + storedTile = augmentTileFromNotification(context, storedTile, sbn); } else { if (DEBUG) { Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId); @@ -418,23 +422,40 @@ public class PeopleSpaceUtils { .setNotificationKey(null) .setNotificationContent(null) .setNotificationDataUri(null) + .setNotificationCategory(null) .build(); } updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile); } - static PeopleSpaceTile augmentTileFromNotification(PeopleSpaceTile tile, + static PeopleSpaceTile augmentTileFromNotification(Context context, PeopleSpaceTile tile, StatusBarNotification sbn) { - Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(sbn); - if (message == null) { - if (DEBUG) Log.i(TAG, "Notification doesn't have content, skipping."); + Notification notification = sbn.getNotification(); + if (notification == null) { + if (DEBUG) Log.d(TAG, "Notification is null"); return tile; } + boolean isMissedCall = Objects.equals(notification.category, CATEGORY_MISSED_CALL); + Notification.MessagingStyle.Message message = getLastMessagingStyleMessage(notification); + + if (!isMissedCall && message == null) { + if (DEBUG) Log.d(TAG, "Notification has no content"); + return tile; + } + + // If it's a missed call notification and it doesn't include content, use fallback value, + // otherwise, use notification content. + boolean hasMessageText = message != null && !TextUtils.isEmpty(message.getText()); + CharSequence content = (isMissedCall && !hasMessageText) + ? context.getString(R.string.missed_call) : message.getText(); + Uri dataUri = message != null ? message.getDataUri() : null; + return tile .toBuilder() .setNotificationKey(sbn.getKey()) - .setNotificationContent(message.getText()) - .setNotificationDataUri(message.getDataUri()) + .setNotificationCategory(notification.category) + .setNotificationContent(content) + .setNotificationDataUri(dataUri) .build(); } @@ -462,6 +483,11 @@ public class PeopleSpaceUtils { * content, then birthdays, then the most recent status, and finally last interaction. */ private static RemoteViews getViewForTile(Context context, PeopleSpaceTile tile) { + if (Objects.equals(tile.getNotificationCategory(), CATEGORY_MISSED_CALL)) { + if (DEBUG) Log.d(TAG, "Create missed call view"); + return createMissedCallRemoteViews(context, tile); + } + if (tile.getNotificationKey() != null) { if (DEBUG) Log.d(TAG, "Create notification view"); return createNotificationRemoteViews(context, tile); @@ -617,7 +643,8 @@ public class PeopleSpaceUtils { activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId()); activityIntent.putExtra( PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName()); - activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid()); + activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE, + tile.getUserHandle()); views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity( context, appWidgetId, @@ -630,6 +657,16 @@ public class PeopleSpaceUtils { return views; } + private static RemoteViews createMissedCallRemoteViews(Context context, + PeopleSpaceTile tile) { + RemoteViews views = new RemoteViews( + context.getPackageName(), R.layout.people_space_small_avatar_tile); + views.setTextViewText(R.id.status, tile.getNotificationContent()); + views.setImageViewResource(R.id.status_defined_icon, R.drawable.ic_phone_missed); + views.setBoolean(R.id.content_background, "setClipToOutline", true); + return views; + } + private static RemoteViews createNotificationRemoteViews(Context context, PeopleSpaceTile tile) { RemoteViews views = new RemoteViews( @@ -715,8 +752,7 @@ public class PeopleSpaceUtils { /** Gets the most recent {@link Notification.MessagingStyle.Message} from the notification. */ @VisibleForTesting public static Notification.MessagingStyle.Message getLastMessagingStyleMessage( - StatusBarNotification sbn) { - Notification notification = sbn.getNotification(); + Notification notification) { if (notification == null) { return null; } @@ -755,7 +791,6 @@ public class PeopleSpaceUtils { Log.i(TAG, "ConversationChannel is null"); return null; } - PeopleSpaceTile tile = new PeopleSpaceTile.Builder(channel, launcherApps).build(); if (!PeopleSpaceUtils.shouldKeepConversation(tile)) { Log.i(TAG, "PeopleSpaceTile is not valid"); @@ -1036,11 +1071,6 @@ public class PeopleSpaceUtils { /** Returns the userId associated with a {@link PeopleSpaceTile} */ public static int getUserId(PeopleSpaceTile tile) { - return getUserHandle(tile).getIdentifier(); - } - - /** Returns the {@link UserHandle} associated with a {@link PeopleSpaceTile} */ - public static UserHandle getUserHandle(PeopleSpaceTile tile) { - return UserHandle.getUserHandleForUid(tile.getUid()); + return tile.getUserHandle().getIdentifier(); } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java index 358f3113ef88..13e30f920f42 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java @@ -41,7 +41,8 @@ public class LaunchConversationActivity extends Activity { Intent intent = getIntent(); String tileId = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID); String packageName = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME); - int uid = intent.getIntExtra(PeopleSpaceWidgetProvider.EXTRA_UID, 0); + UserHandle userHandle = intent.getParcelableExtra( + PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE); if (tileId != null && !tileId.isEmpty()) { if (DEBUG) { @@ -52,7 +53,7 @@ public class LaunchConversationActivity extends Activity { LauncherApps launcherApps = getApplicationContext().getSystemService(LauncherApps.class); launcherApps.startShortcut( - packageName, tileId, null, null, UserHandle.getUserHandleForUid(uid)); + packageName, tileId, null, null, userHandle); } catch (Exception e) { Log.e(TAG, "Exception starting shortcut:" + e); } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java index b188acbf30f3..3df264421d75 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetEnabler.java @@ -23,6 +23,7 @@ import android.util.Log; import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.people.PeopleSpaceActivity; import com.android.systemui.statusbar.FeatureFlags; import javax.inject.Inject; @@ -54,6 +55,12 @@ public class PeopleSpaceWidgetEnabler extends SystemUI { ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + mContext.getPackageManager().setComponentEnabledSetting( + new ComponentName(mContext, PeopleSpaceActivity.class), + showPeopleSpace + ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED + : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); } catch (Exception e) { Log.w(TAG, "Error enabling People Space widget:", e); } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index bee9889eaa4e..9e5c786b9a63 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -29,7 +29,6 @@ import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.Log; -import android.widget.RemoteViews; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.appwidget.IAppWidgetService; @@ -124,8 +123,6 @@ public class PeopleSpaceWidgetManager { */ public void updateWidgetWithNotificationChanged(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction) { - RemoteViews views = new RemoteViews( - mContext.getPackageName(), R.layout.people_space_small_avatar_tile); if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called"); boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java index 3d1055fdece2..90baf56e0137 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java @@ -48,7 +48,7 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { public static final String EXTRA_TILE_ID = "extra_tile_id"; public static final String EXTRA_PACKAGE_NAME = "extra_package_name"; - public static final String EXTRA_UID = "extra_uid"; + public static final String EXTRA_USER_HANDLE = "extra_user_handle"; public UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java index 80794cb64883..050352292b38 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java @@ -123,7 +123,8 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId()); fillInIntent.putExtra( PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName()); - fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid()); + fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE, + tile.getUserHandle()); personView.setOnClickFillInIntent(R.id.item, fillInIntent); } catch (Exception e) { Log.e(TAG, "Couldn't retrieve shortcut information", e); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java index c9d1b71bca77..0fa7b59d0e54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java +++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 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,9 +14,8 @@ * limitations under the License. */ -package com.android.systemui.statusbar.tv.micdisclosure; +package com.android.systemui.privacy.television; -import static android.provider.DeviceConfig.NAMESPACE_PRIVACY; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import android.animation.Animator; @@ -25,57 +24,56 @@ import android.animation.ObjectAnimator; import android.annotation.IntDef; import android.annotation.UiThread; import android.content.Context; +import android.content.res.Resources; +import android.graphics.Color; import android.graphics.PixelFormat; -import android.provider.DeviceConfig; -import android.text.TextUtils; -import android.util.ArraySet; +import android.graphics.drawable.Drawable; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.LinearLayout; import com.android.systemui.R; -import com.android.systemui.statusbar.tv.TvStatusBar; +import com.android.systemui.SystemUI; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.privacy.PrivacyChipBuilder; +import com.android.systemui.privacy.PrivacyItem; +import com.android.systemui.privacy.PrivacyItemController; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; -import java.util.Collections; import java.util.List; -import java.util.Set; + +import javax.inject.Inject; /** - * A component of {@link TvStatusBar} responsible for notifying the user whenever an application is - * recording audio. - * - * @see TvStatusBar + * A SystemUI component responsible for notifying the user whenever an application is + * recording audio, accessing the camera or accessing the location. */ -public class AudioRecordingDisclosureBar implements - AudioActivityObserver.OnAudioActivityStateChangeListener { - private static final String TAG = "AudioRecordingDisclosure"; +@SysUISingleton +public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemController.Callback { + private static final String TAG = "TvOngoingPrivacyChip"; static final boolean DEBUG = false; - // This title is used to test the microphone disclosure indicator in - // CtsSystemUiHostTestCases:TvMicrophoneCaptureIndicatorTest + // This title is used in CameraMicIndicatorsPermissionTest and + // RecognitionServiceMicIndicatorTest. private static final String LAYOUT_PARAMS_TITLE = "MicrophoneCaptureIndicator"; - private static final String ENABLED_FLAG = "mic_disclosure_enabled"; - private static final String EXEMPT_PACKAGES_LIST = "mic_disclosure_exempt_packages"; - private static final String FORCED_PACKAGES_LIST = "mic_disclosure_forced_packages"; - @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"STATE_"}, value = { - STATE_STOPPED, STATE_NOT_SHOWN, STATE_APPEARING, STATE_SHOWN, STATE_DISAPPEARING }) - public @interface State {} + public @interface State { + } - private static final int STATE_STOPPED = -1; private static final int STATE_NOT_SHOWN = 0; private static final int STATE_APPEARING = 1; private static final int STATE_SHOWN = 2; @@ -84,106 +82,89 @@ public class AudioRecordingDisclosureBar implements private static final int ANIMATION_DURATION_MS = 200; private final Context mContext; - private boolean mIsEnabled; + private final PrivacyItemController mPrivacyItemController; private View mIndicatorView; private boolean mViewAndWindowAdded; private ObjectAnimator mAnimator; - @State private int mState = STATE_STOPPED; + private boolean mAllIndicatorsFlagEnabled; + private boolean mMicCameraIndicatorFlagEnabled; + private boolean mLocationIndicatorEnabled; + private List<PrivacyItem> mPrivacyItems; - /** - * Array of the observers that monitor different aspects of the system, such as AppOps and - * microphone foreground services - */ - private AudioActivityObserver[] mAudioActivityObservers; - /** - * Set of applications for which we make an exception and do not show the indicator. This gets - * populated once - in {@link #AudioRecordingDisclosureBar(Context)}. - */ - private final Set<String> mExemptPackages = new ArraySet<>(); + private LinearLayout mIconsContainer; + private final int mIconSize; + private final int mIconMarginStart; - public AudioRecordingDisclosureBar(Context context) { + @State + private int mState = STATE_NOT_SHOWN; + + @Inject + public TvOngoingPrivacyChip(Context context, PrivacyItemController privacyItemController) { + super(context); + Log.d(TAG, "Privacy chip running without id"); mContext = context; + mPrivacyItemController = privacyItemController; - // Load configs - reloadExemptPackages(); + Resources res = mContext.getResources(); + mIconMarginStart = Math.round(res.getDimension(R.dimen.privacy_chip_icon_margin)); + mIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_icon_size); - mIsEnabled = DeviceConfig.getBoolean(NAMESPACE_PRIVACY, ENABLED_FLAG, true); - // Start if enabled - if (mIsEnabled) { - start(); - } + mAllIndicatorsFlagEnabled = privacyItemController.getAllIndicatorsAvailable(); + mMicCameraIndicatorFlagEnabled = privacyItemController.getMicCameraAvailable(); + mLocationIndicatorEnabled = privacyItemController.getLocationAvailable(); - // Set up a config change listener - DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_PRIVACY, mContext.getMainExecutor(), - mConfigChangeListener); + if (DEBUG) { + Log.d(TAG, "allIndicators: " + mAllIndicatorsFlagEnabled); + Log.d(TAG, "micCameraIndicators: " + mMicCameraIndicatorFlagEnabled); + Log.d(TAG, "locationIndicators: " + mLocationIndicatorEnabled); + } } - private void reloadExemptPackages() { - mExemptPackages.clear(); - mExemptPackages.addAll(Arrays.asList(mContext.getResources().getStringArray( - R.array.audio_recording_disclosure_exempt_apps))); - mExemptPackages.addAll( - splitByComma( - DeviceConfig.getString(NAMESPACE_PRIVACY, EXEMPT_PACKAGES_LIST, null))); - mExemptPackages.removeAll( - splitByComma( - DeviceConfig.getString(NAMESPACE_PRIVACY, FORCED_PACKAGES_LIST, null))); + @Override + public void start() { + mPrivacyItemController.addCallback(this); } - @UiThread - private void start() { - if (mState != STATE_STOPPED) { - return; - } - mState = STATE_NOT_SHOWN; - - if (mAudioActivityObservers == null) { - mAudioActivityObservers = new AudioActivityObserver[]{ - new RecordAudioAppOpObserver(mContext, this), - new MicrophoneForegroundServicesObserver(mContext, this), - }; - } - - for (int i = mAudioActivityObservers.length - 1; i >= 0; i--) { - mAudioActivityObservers[i].start(); - } + @Override + public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) { + if (DEBUG) Log.d(TAG, "PrivacyItemsChanged"); + mPrivacyItems = privacyItems; + updateUI(); } - @UiThread - private void stop() { - if (mState == STATE_STOPPED) { - return; - } - mState = STATE_STOPPED; - - for (int i = mAudioActivityObservers.length - 1; i >= 0; i--) { - mAudioActivityObservers[i].stop(); - } - - // Remove the view if shown. - if (mState != STATE_NOT_SHOWN) { - removeIndicatorView(); - } + @Override + public void onFlagAllChanged(boolean flag) { + if (DEBUG) Log.d(TAG, "all indicators enabled: " + flag); + mAllIndicatorsFlagEnabled = flag; } - @UiThread @Override - public void onAudioActivityStateChange(boolean active, String packageName) { - if (DEBUG) { - Log.d(TAG, - "onAudioActivityStateChange, packageName=" + packageName + ", active=" - + active); - } + public void onFlagMicCameraChanged(boolean flag) { + if (DEBUG) Log.d(TAG, "mic/camera indicators enabled: " + flag); + mMicCameraIndicatorFlagEnabled = flag; + } - if (mExemptPackages.contains(packageName)) { - if (DEBUG) Log.d(TAG, " - exempt package: ignoring"); - return; - } + @Override + public void onFlagLocationChanged(boolean flag) { + if (DEBUG) Log.d(TAG, "location indicators enabled: " + flag); + mLocationIndicatorEnabled = flag; + } - if (active) { - showIfNeeded(); + private void updateUI() { + if (DEBUG) Log.d(TAG, mPrivacyItems.size() + " privacy items"); + + if ((mMicCameraIndicatorFlagEnabled || mAllIndicatorsFlagEnabled + || mLocationIndicatorEnabled) && !mPrivacyItems.isEmpty()) { + if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) { + showIndicator(); + } else { + if (DEBUG) Log.d(TAG, "only updating icons"); + PrivacyChipBuilder builder = new PrivacyChipBuilder(mContext, mPrivacyItems); + setIcons(builder.generateIcons(), mIconsContainer); + mIconsContainer.requestLayout(); + } } else { hideIndicatorIfNeeded(); } @@ -191,12 +172,7 @@ public class AudioRecordingDisclosureBar implements @UiThread private void hideIndicatorIfNeeded() { - // If STOPPED, NOT_SHOWN or DISAPPEARING - nothing else for us to do here. - if (mState != STATE_SHOWN && mState != STATE_APPEARING) return; - - if (hasActiveRecorders()) { - return; - } + if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) return; if (mViewAndWindowAdded) { mState = STATE_DISAPPEARING; @@ -210,23 +186,12 @@ public class AudioRecordingDisclosureBar implements } @UiThread - private void showIfNeeded() { - // If STOPPED, SHOWN or APPEARING - nothing else for us to do here. - if (mState != STATE_NOT_SHOWN && mState != STATE_DISAPPEARING) return; - - if (DEBUG) Log.d(TAG, "Showing indicator"); - - final int prevState = mState; + private void showIndicator() { mState = STATE_APPEARING; - if (prevState == STATE_DISAPPEARING) { - animateAppearance(); - return; - } - // Inflate the indicator view mIndicatorView = LayoutInflater.from(mContext).inflate( - R.layout.tv_audio_recording_indicator, null); + R.layout.tv_ongoing_privacy_chip, null); // 1. Set alpha to 0. // 2. Wait until the window is shown and the view is laid out. @@ -239,7 +204,7 @@ public class AudioRecordingDisclosureBar implements @Override public void onGlobalLayout() { // State could have changed to NOT_SHOWN (if all the recorders are - // already gone) to STOPPED (if the indicator was disabled) + // already gone) if (mState != STATE_APPEARING) return; mViewAndWindowAdded = true; @@ -251,22 +216,41 @@ public class AudioRecordingDisclosureBar implements } }); - final boolean isLtr = mContext.getResources().getConfiguration().getLayoutDirection() - == View.LAYOUT_DIRECTION_LTR; + mIconsContainer = mIndicatorView.findViewById(R.id.icons_container); + PrivacyChipBuilder builder = new PrivacyChipBuilder(mContext, mPrivacyItems); + setIcons(builder.generateIcons(), mIconsContainer); + final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( WRAP_CONTENT, WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); - layoutParams.gravity = Gravity.TOP | (isLtr ? Gravity.RIGHT : Gravity.LEFT); + layoutParams.gravity = Gravity.TOP | Gravity.END; layoutParams.setTitle(LAYOUT_PARAMS_TITLE); layoutParams.packageName = mContext.getPackageName(); - final WindowManager windowManager = (WindowManager) mContext.getSystemService( - Context.WINDOW_SERVICE); + final WindowManager windowManager = mContext.getSystemService(WindowManager.class); windowManager.addView(mIndicatorView, layoutParams); + } + private void setIcons(List<Drawable> icons, ViewGroup iconsContainer) { + iconsContainer.removeAllViews(); + for (int i = 0; i < icons.size(); i++) { + Drawable icon = icons.get(i); + icon.mutate().setTint(Color.WHITE); + ImageView imageView = new ImageView(mContext); + imageView.setImageDrawable(icon); + imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); + mIconsContainer.addView(imageView, mIconSize, mIconSize); + if (i != 0) { + ViewGroup.MarginLayoutParams layoutParams = + (ViewGroup.MarginLayoutParams) imageView.getLayoutParams(); + layoutParams.setMarginStart(mIconMarginStart); + imageView.setLayoutParams(layoutParams); + } + } + } private void animateAppearance() { animateAlphaTo(1f); @@ -333,22 +317,13 @@ public class AudioRecordingDisclosureBar implements } } - private boolean hasActiveRecorders() { - for (int index = mAudioActivityObservers.length - 1; index >= 0; index--) { - for (String activePackage : mAudioActivityObservers[index].getActivePackages()) { - if (mExemptPackages.contains(activePackage)) continue; - return true; - } - } - return false; - } - private void removeIndicatorView() { if (DEBUG) Log.d(TAG, "removeIndicatorView"); - final WindowManager windowManager = (WindowManager) mContext.getSystemService( - Context.WINDOW_SERVICE); - windowManager.removeView(mIndicatorView); + final WindowManager windowManager = mContext.getSystemService(WindowManager.class); + if (windowManager != null) { + windowManager.removeView(mIndicatorView); + } mIndicatorView = null; mAnimator = null; @@ -356,26 +331,4 @@ public class AudioRecordingDisclosureBar implements mViewAndWindowAdded = false; } - private static List<String> splitByComma(String string) { - return TextUtils.isEmpty(string) ? Collections.emptyList() : Arrays.asList( - string.split(",")); - } - - private final DeviceConfig.OnPropertiesChangedListener mConfigChangeListener = - new DeviceConfig.OnPropertiesChangedListener() { - @Override - public void onPropertiesChanged(DeviceConfig.Properties properties) { - reloadExemptPackages(); - - // Check if was enabled/disabled - if (mIsEnabled != properties.getBoolean(ENABLED_FLAG, true)) { - mIsEnabled = !mIsEnabled; - if (mIsEnabled) { - start(); - } else { - stop(); - } - } - } - }; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index eaf212362320..fcb56a4ec75d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -71,7 +71,6 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { private int mMinRows = 1; private int mMaxColumns = TileLayout.NO_MAX_COLUMNS; - private boolean mShowLabels = true; private final boolean mSideLabels; public PagedTileLayout(Context context, AttributeSet attrs) { @@ -91,16 +90,6 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { } private int mLastMaxHeight = -1; - @Override - public void setShowLabels(boolean show) { - mShowLabels = show; - for (TileLayout p : mPages) { - p.setShowLabels(show); - } - mDistributeTiles = true; - requestLayout(); - } - public void saveInstanceState(Bundle outState) { outState.putInt(CURRENT_PAGE, getCurrentItem()); } @@ -239,7 +228,6 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { : R.layout.qs_paged_page, this, false); page.setMinRows(mMinRows); page.setMaxColumns(mMaxColumns); - page.setShowLabels(mShowLabels); return page; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index c8edaec98ee4..fe76668ab68b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -29,7 +29,6 @@ import com.android.systemui.qs.QSPanel.QSTileLayout; import com.android.systemui.qs.TouchAnimator.Builder; import com.android.systemui.qs.TouchAnimator.Listener; import com.android.systemui.qs.dagger.QSScope; -import com.android.systemui.qs.tileimpl.QSTileBaseView; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -56,7 +55,8 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private final ArrayList<View> mAllViews = new ArrayList<>(); /** * List of {@link View}s representing Quick Settings that are being animated from the quick QS - * position to the normal QS panel. + * position to the normal QS panel. These views will only show once the animation is complete, + * to prevent overlapping of semi transparent views */ private final ArrayList<View> mQuickQsViews = new ArrayList<>(); private final QuickQSPanel mQuickQsPanel; @@ -233,7 +233,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha // Quick tiles. QSTileView quickTileView = mQuickQSPanelController.getTileView(tile); if (quickTileView == null) continue; - View qqsBgCircle = ((QSTileBaseView) quickTileView).getBgCircle(); getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view); getRelativePosition(loc2, tileIcon, view); @@ -255,11 +254,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0); translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0); - if (mFeatureFlags.isQSLabelsEnabled()) { - firstPageBuilder.addFloat(qqsBgCircle, "alpha", 1, 1, 0); - mAllViews.add(qqsBgCircle); - } - } else { // These tiles disappear when expanding firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0); translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff); @@ -271,7 +265,11 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha translationX); } - mQuickQsViews.add(tileView.getIconWithBackground()); + if (mFeatureFlags.isQSLabelsEnabled()) { + mQuickQsViews.add(tileView); + } else { + mQuickQsViews.add(tileView.getIconWithBackground()); + } mAllViews.add(tileView.getIcon()); mAllViews.add(quickTileView); } else if (mFullRows && isIconInAnimatedRow(count)) { @@ -362,7 +360,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha if(view == parent || view == null) return; // Ignore tile pages as they can have some offset we don't want to take into account in // RTL. - if (!(view instanceof PagedTileLayout.TilePage || view instanceof SideLabelTileLayout)) { + if (!isAPage(view)) { loc1[0] += view.getLeft(); loc1[1] += view.getTop(); } @@ -374,6 +372,16 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha getRelativePositionInt(loc1, (View) view.getParent(), parent); } + // Returns true if the view is a possible page in PagedTileLayout + private boolean isAPage(View view) { + if (view instanceof PagedTileLayout.TilePage) { + return true; + } else if (view instanceof SideLabelTileLayout) { + return !(view instanceof QuickQSPanel.QQSSideLabelTileLayout); + } + return false; + } + public void setPosition(float position) { if (mNeedsAnimatorUpdate) { updateAnimators(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java index 8110fda1330c..2bea72cc0c7e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java @@ -16,6 +16,8 @@ package com.android.systemui.qs; +import static com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED; + import android.content.ClipData; import android.content.ClipboardManager; import android.content.Intent; @@ -41,6 +43,7 @@ import com.android.systemui.tuner.TunerService; import com.android.systemui.util.ViewController; import javax.inject.Inject; +import javax.inject.Named; /** * Controller for {@link QSFooterView}. @@ -63,6 +66,8 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme private final View mEdit; private final MultiUserSwitch mMultiUserSwitch; private final PageIndicator mPageIndicator; + private final View mPowerMenuLite; + private final boolean mShowPMLiteButton; private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener = new UserInfoController.OnUserInfoChangedListener() { @@ -95,7 +100,6 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme mActivityStarter.postQSRunnableDismissingKeyguard(() -> { if (isTunerEnabled()) { mTunerService.showResetRequest( - mUserTracker.getUserHandle(), () -> { // Relaunch settings so that the tuner disappears. startSettingsActivity(); @@ -103,7 +107,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme } else { Toast.makeText(getContext(), R.string.tuner_toast, Toast.LENGTH_LONG).show(); - mTunerService.setTunerEnabled(mUserTracker.getUserHandle(), true); + mTunerService.setTunerEnabled(true); } startSettingsActivity(); @@ -124,7 +128,8 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme DeviceProvisionedController deviceProvisionedController, UserTracker userTracker, QSPanelController qsPanelController, QSDetailDisplayer qsDetailDisplayer, QuickQSPanelController quickQSPanelController, - TunerService tunerService, MetricsLogger metricsLogger) { + TunerService tunerService, MetricsLogger metricsLogger, + @Named(PM_LITE_ENABLED) boolean showPMLiteButton) { super(view); mUserManager = userManager; mUserInfoController = userInfoController; @@ -142,10 +147,15 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme mEdit = mView.findViewById(android.R.id.edit); mMultiUserSwitch = mView.findViewById(R.id.multi_user_switch); mPageIndicator = mView.findViewById(R.id.footer_page_indicator); + mPowerMenuLite = mView.findViewById(R.id.pm_lite); + mShowPMLiteButton = showPMLiteButton; } @Override protected void onViewAttached() { + if (mShowPMLiteButton) { + mPowerMenuLite.setVisibility(View.VISIBLE); + } mView.addOnLayoutChangeListener( (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> mView.updateAnimator( @@ -238,6 +248,6 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme } private boolean isTunerEnabled() { - return mTunerService.isTunerEnabled(mUserTracker.getUserHandle()); + return mTunerService.isTunerEnabled(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 91ae571d1cfb..7657dcead583 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -112,7 +112,7 @@ public class QSPanel extends LinearLayout implements Tunable { private int mMediaTotalBottomMargin; private int mFooterMarginStartHorizontal; private Consumer<Boolean> mMediaVisibilityChangedListener; - private boolean mSideLabels; + protected boolean mSideLabels; public QSPanel(Context context, AttributeSet attrs) { super(context, attrs); @@ -201,16 +201,20 @@ public class QSPanel extends LinearLayout implements Tunable { mFooterPageIndicator.setNumPages(((PagedTileLayout) mTileLayout).getNumPages()); } - // Allow the UI to be as big as it want's to, we're in a scroll view - int newHeight = 10000; - int availableHeight = MeasureSpec.getSize(heightMeasureSpec); - int excessHeight = newHeight - availableHeight; - // Measure with EXACTLY. That way, The content will only use excess height and will - // be measured last, after other views and padding is accounted for. This only - // works because our Layouts in here remeasure themselves with the exact content - // height. - heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY); - ((PagedTileLayout) mTileLayout).setExcessHeight(excessHeight); + // In landscape, mTileLayout's parent is not the panel but a view that contains the + // tile layout and the media controls. + if (((View) mTileLayout).getParent() == this) { + // Allow the UI to be as big as it want's to, we're in a scroll view + int newHeight = 10000; + int availableHeight = MeasureSpec.getSize(heightMeasureSpec); + int excessHeight = newHeight - availableHeight; + // Measure with EXACTLY. That way, The content will only use excess height and will + // be measured last, after other views and padding is accounted for. This only + // works because our Layouts in here remeasure themselves with the exact content + // height. + heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY); + ((PagedTileLayout) mTileLayout).setExcessHeight(excessHeight); + } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -758,7 +762,7 @@ public class QSPanel extends LinearLayout implements Tunable { // Let's use 3 columns to match the current layout int columns; if (mSideLabels) { - columns = horizontal ? 1 : 2; + columns = horizontal ? 2 : 4; } else { columns = horizontal ? 3 : TileLayout.NO_MAX_COLUMNS; } @@ -843,8 +847,6 @@ public class QSPanel extends LinearLayout implements Tunable { default void setExpansion(float expansion) {} int getNumVisibleTiles(); - - default void setShowLabels(boolean show) {} } interface OnConfigurationChangedListener { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java index fcb35e2040ea..eda1abb0997e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -18,7 +18,6 @@ package com.android.systemui.qs; import static com.android.systemui.media.dagger.MediaModule.QS_PANEL; import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS; -import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG; import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER; import android.annotation.NonNull; @@ -66,7 +65,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { private BrightnessMirrorController mBrightnessMirrorController; private boolean mGridContentVisible = true; - private boolean mQsLabelsFlag; private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener = new QSPanel.OnConfigurationChangedListener() { @@ -93,7 +91,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory, BrightnessSlider.Factory brightnessSliderFactory, - @Named(QS_LABELS_FLAG) boolean qsLabelsFlag, FeatureFlags featureFlags) { super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags); @@ -108,9 +105,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { mView.setBrightnessView(mBrightnessSlider.getRootView()); mBrightnessController = brightnessControllerFactory.create(mBrightnessSlider); - - mQsLabelsFlag = qsLabelsFlag; - mSideLabels = qsLabelsFlag; } @Override @@ -130,7 +124,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { updateMediaDisappearParameters(); mTunerService.addTunable(mView, QS_SHOW_BRIGHTNESS); - mTunerService.addTunable(mTunable, QS_REMOVE_LABELS); mView.updateResources(); if (mView.isListening()) { refreshAllTiles(); @@ -144,13 +137,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { } @Override - boolean switchTileLayout(boolean force) { - boolean result = super.switchTileLayout(force); - getTileLayout().setShowLabels(mShowLabels); - return result; - } - - @Override protected QSTileRevealController createTileRevealController() { return mQsTileRevealControllerFactory.create( this, (PagedTileLayout) mView.createRegularTileLayout()); @@ -158,7 +144,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { @Override protected void onViewDetached() { - mTunerService.removeTunable(mTunable); mTunerService.removeTunable(mView); mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener); if (mBrightnessMirrorController != null) { @@ -324,22 +309,5 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { public boolean isExpanded() { return mView.isExpanded(); } - - private TunerService.Tunable mTunable = new TunerService.Tunable() { - @Override - public void onTuningChanged(String key, String newValue) { - if (QS_REMOVE_LABELS.equals(key)) { - if (!mQsLabelsFlag) return; - boolean newShowLabels = newValue == null || "0".equals(newValue); - if (mShowLabels == newShowLabels) return; - mShowLabels = newShowLabels; - for (TileRecord t : mRecords) { - t.tileView.setShowLabels(mShowLabels); - } - getTileLayout().setShowLabels(mShowLabels); - mView.requestLayout(); - } - } - }; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 9426e7122c1c..8ab17432524d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -75,7 +75,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr private final QSHost.Callback mQSHostCallback = this::setTiles; protected boolean mShowLabels = true; - protected boolean mSideLabels; + protected boolean mQSLabelFlag; private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener = new QSPanel.OnConfigurationChangedListener() { @@ -118,11 +118,12 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr mQSLogger = qsLogger; mDumpManager = dumpManager; mFeatureFlags = featureFlags; + mQSLabelFlag = featureFlags.isQSLabelsEnabled(); } @Override protected void onInit() { - mView.initialize(mSideLabels); + mView.initialize(mQSLabelFlag); mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), ""); } @@ -197,7 +198,6 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr final TileRecord r = new TileRecord(); r.tile = tile; r.tileView = mHost.createTileView(tile, collapsedView); - r.tileView.setShowLabels(mShowLabels); mView.addTile(r); mRecords.add(r); mCachedSpecs = getTilesSpecs(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index a29ac3bb77e9..9b66b59c06df 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -69,12 +69,22 @@ public class QuickQSPanel extends QSPanel { @Override public TileLayout createRegularTileLayout() { - return new QuickQSPanel.HeaderTileLayout(mContext); + if (mSideLabels) { + return new QQSSideLabelTileLayout(mContext); + } else { + return new QuickQSPanel.HeaderTileLayout(mContext); + } } @Override protected QSTileLayout createHorizontalTileLayout() { - return new DoubleLineTileLayout(mContext); + if (mSideLabels) { + TileLayout t = createRegularTileLayout(); + t.setMaxColumns(2); + return t; + } else { + return new DoubleLineTileLayout(mContext); + } } @Override @@ -331,4 +341,38 @@ public class QuickQSPanel extends QSPanel { } } } + + static class QQSSideLabelTileLayout extends SideLabelTileLayout { + QQSSideLabelTileLayout(Context context) { + super(context, null); + setClipChildren(false); + setClipToPadding(false); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER_HORIZONTAL; + setLayoutParams(lp); + setMaxColumns(4); + } + + @Override + public boolean updateResources() { + boolean b = super.updateResources(); + mMaxAllowedRows = 2; + return b; + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + updateResources(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // Make sure to always use the correct number of rows. As it's determined by the + // columns, just use as many as needed. + updateMaxRows(10000, mRecords.size()); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java index 383e932a6955..671f8f7dd2d1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -17,7 +17,6 @@ package com.android.systemui.qs; import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL; -import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG; import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER; import com.android.internal.logging.MetricsLogger; @@ -42,8 +41,6 @@ import javax.inject.Named; @QSScope public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> { - private boolean mUseSideLabels; - private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener = newConfig -> { int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); @@ -58,12 +55,10 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer, @Named(QUICK_QS_PANEL) MediaHost mediaHost, MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger, - DumpManager dumpManager, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag, - FeatureFlags featureFlags + DumpManager dumpManager, FeatureFlags featureFlags ) { super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags); - mUseSideLabels = qsLabelsFlag; } @Override @@ -104,19 +99,7 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> break; } } - if (mUseSideLabels) { - List<QSTile> newTiles = new ArrayList<>(); - for (int i = 0; i < tiles.size(); i += 2) { - newTiles.add(tiles.get(i)); - } - for (int i = 1; i < tiles.size(); i += 2) { - newTiles.add(tiles.get(i)); - } - super.setTiles(newTiles, true); - - } else { - super.setTiles(tiles, true); - } + super.setTiles(tiles, !mQSLabelFlag); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 87252ff2b908..a0bf5846ef53 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -90,6 +90,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn private OngoingPrivacyChip mPrivacyChip; private Space mSpace; private BatteryMeterView mBatteryRemainingIcon; + private TintedIconManager mTintedIconManager; // Used for RingerModeTracker private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); @@ -144,6 +145,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn } void onAttach(TintedIconManager iconManager) { + mTintedIconManager = iconManager; int fillColor = Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary); @@ -268,6 +270,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn android.R.attr.textColorSecondary); mTextColorPrimary = textColor; mClockView.setTextColor(textColor); + if (mTintedIconManager != null) { + mTintedIconManager.setTint(textColor); + } mBatteryRemainingIcon.updateColors(mTextColorPrimary, textColorSecondary, mTextColorPrimary); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt index 74a7ac1cb6dd..c3cc3af10e83 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt @@ -20,11 +20,13 @@ import android.content.Context import android.util.AttributeSet import com.android.systemui.R -open class SideLabelTileLayout(context: Context, attrs: AttributeSet) : TileLayout(context, attrs) { +open class SideLabelTileLayout( + context: Context, + attrs: AttributeSet? +) : TileLayout(context, attrs) { override fun updateResources(): Boolean { return super.updateResources().also { - mResourceColumns = 2 mMaxAllowedRows = 4 mCellMarginHorizontal = (mCellMarginHorizontal * 1.2).toInt() mCellMarginVertical = mCellMarginHorizontal @@ -32,8 +34,6 @@ open class SideLabelTileLayout(context: Context, attrs: AttributeSet) : TileLayo } } - override fun setShowLabels(show: Boolean) { } - override fun isFull(): Boolean { return mRecords.size >= maxTiles() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index d559e07f3ff6..c1ce4a577dda 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -36,7 +36,6 @@ public class TileLayout extends ViewGroup implements QSTileLayout { private int mCellMarginTop; protected boolean mListening; protected int mMaxAllowedRows = 3; - private boolean mShowLabels = true; // Prototyping with less rows private final boolean mLessRows; @@ -57,12 +56,6 @@ public class TileLayout extends ViewGroup implements QSTileLayout { } @Override - public void setShowLabels(boolean show) { - mShowLabels = show; - updateResources(); - } - - @Override public int getOffsetTop(TileRecord tile) { return getTop(); } @@ -124,15 +117,13 @@ public class TileLayout extends ViewGroup implements QSTileLayout { public boolean updateResources() { final Resources res = mContext.getResources(); mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns)); + updateColumns(); mMaxCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height); mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal); mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical); - if (!mShowLabels && mCellMarginVertical == 0) { - mCellMarginVertical = mCellMarginHorizontal; - } mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top); mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows)); - if (mLessRows && mShowLabels) mMaxAllowedRows = Math.max(mMinRows, mMaxAllowedRows - 1); + if (mLessRows) mMaxAllowedRows = Math.max(mMinRows, mMaxAllowedRows - 1); if (updateColumns()) { requestLayout(); return true; @@ -193,9 +184,8 @@ public class TileLayout extends ViewGroup implements QSTileLayout { + mCellMarginVertical; final int previousRows = mRows; mRows = availableHeight / (getCellHeight() + mCellMarginVertical); - final int minRows = mShowLabels ? mMinRows : mMinRows + 1; - if (mRows < minRows) { - mRows = minRows; + if (mRows < mMinRows) { + mRows = mMinRows; } else if (mRows >= mMaxAllowedRows) { mRows = mMaxAllowedRows; } @@ -215,7 +205,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { } protected int getCellHeight() { - return mShowLabels ? mMaxCellHeight : mMaxCellHeight / 2; + return mMaxCellHeight; } protected void layoutTileRecords(int numRecords) { 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 aa6bbbda04fb..0dc0b30748aa 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java @@ -34,7 +34,7 @@ import android.widget.TextView; import androidx.annotation.VisibleForTesting; -import com.android.keyguard.CarrierTextManager; +import com.android.keyguard.CarrierTextController; import com.android.settingslib.AccessibilityContentDescriptions; import com.android.settingslib.mobile.TelephonyIcons; import com.android.systemui.R; @@ -42,6 +42,7 @@ 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.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; import java.util.function.Consumer; @@ -58,7 +59,7 @@ public class QSCarrierGroupController { private final ActivityStarter mActivityStarter; private final Handler mBgHandler; private final NetworkController mNetworkController; - private final CarrierTextManager mCarrierTextManager; + private final CarrierTextController mCarrierTextController; private final TextView mNoSimTextView; private final H mMainHandler; private final Callback mCallback; @@ -74,30 +75,25 @@ public class QSCarrierGroupController { private final NetworkController.SignalCallback mSignalCallback = new NetworkController.SignalCallback() { @Override - public void setMobileDataIndicators(NetworkController.IconState statusIcon, - NetworkController.IconState qsIcon, int statusType, int qsType, - boolean activityIn, boolean activityOut, - CharSequence typeContentDescription, - CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming, boolean showTriangle) { + public void setMobileDataIndicators(MobileDataIndicators indicators) { if (mProviderModel) { return; } - int slotIndex = getSlotIndex(subId); + int slotIndex = getSlotIndex(indicators.subId); if (slotIndex >= SIM_SLOTS) { Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex); return; } if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { - Log.e(TAG, "Invalid SIM slot index for subscription: " + subId); + Log.e(TAG, "Invalid SIM slot index for subscription: " + indicators.subId); return; } mInfos[slotIndex] = new CellSignalState( - statusIcon.visible, - statusIcon.icon, - statusIcon.contentDescription, - typeContentDescription.toString(), - roaming + indicators.statusIcon.visible, + indicators.statusIcon.icon, + indicators.statusIcon.contentDescription, + indicators.typeContentDescription.toString(), + indicators.roaming ); mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget(); } @@ -153,7 +149,7 @@ public class QSCarrierGroupController { } }; - private static class Callback implements CarrierTextManager.CarrierTextCallback { + private static class Callback implements CarrierTextController.CarrierTextCallback { private H mHandler; Callback(H handler) { @@ -161,7 +157,7 @@ public class QSCarrierGroupController { } @Override - public void updateCarrierInfo(CarrierTextManager.CarrierTextCallbackInfo info) { + public void updateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { mHandler.obtainMessage(H.MSG_UPDATE_CARRIER_INFO, info).sendToTarget(); } } @@ -169,7 +165,7 @@ public class QSCarrierGroupController { private QSCarrierGroupController(QSCarrierGroup view, ActivityStarter activityStarter, @Background Handler bgHandler, @Main Looper mainLooper, NetworkController networkController, - CarrierTextManager.Builder carrierTextManagerBuilder, Context context) { + CarrierTextController.Builder carrierTextControllerBuilder, Context context) { if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) { mProviderModel = true; } else { @@ -178,7 +174,7 @@ public class QSCarrierGroupController { mActivityStarter = activityStarter; mBgHandler = bgHandler; mNetworkController = networkController; - mCarrierTextManager = carrierTextManagerBuilder + mCarrierTextController = carrierTextControllerBuilder .setShowAirplaneMode(false) .setShowMissingSim(false) .build(); @@ -196,6 +192,7 @@ public class QSCarrierGroupController { mMainHandler = new H(mainLooper, this::handleUpdateCarrierInfo, this::handleUpdateState); mCallback = new Callback(mMainHandler); + mCarrierGroups[0] = view.getCarrier1View(); mCarrierGroups[1] = view.getCarrier2View(); mCarrierGroups[2] = view.getCarrier3View(); @@ -246,10 +243,10 @@ public class QSCarrierGroupController { if (mNetworkController.hasVoiceCallingFeature()) { mNetworkController.addCallback(mSignalCallback); } - mCarrierTextManager.setListening(mCallback); + mCarrierTextController.setListening(mCallback); } else { mNetworkController.removeCallback(mSignalCallback); - mCarrierTextManager.setListening(null); + mCarrierTextController.setListening(null); } } @@ -276,7 +273,7 @@ public class QSCarrierGroupController { } @MainThread - private void handleUpdateCarrierInfo(CarrierTextManager.CarrierTextCallbackInfo info) { + private void handleUpdateCarrierInfo(CarrierTextController.CarrierTextCallbackInfo info) { if (!mMainHandler.getLooper().isCurrentThread()) { mMainHandler.obtainMessage(H.MSG_UPDATE_CARRIER_INFO, info).sendToTarget(); return; @@ -330,13 +327,13 @@ public class QSCarrierGroupController { } private static class H extends Handler { - private Consumer<CarrierTextManager.CarrierTextCallbackInfo> mUpdateCarrierInfo; + private Consumer<CarrierTextController.CarrierTextCallbackInfo> mUpdateCarrierInfo; private Runnable mUpdateState; static final int MSG_UPDATE_CARRIER_INFO = 0; static final int MSG_UPDATE_STATE = 1; H(Looper looper, - Consumer<CarrierTextManager.CarrierTextCallbackInfo> updateCarrierInfo, + Consumer<CarrierTextController.CarrierTextCallbackInfo> updateCarrierInfo, Runnable updateState) { super(looper); mUpdateCarrierInfo = updateCarrierInfo; @@ -348,7 +345,7 @@ public class QSCarrierGroupController { switch (msg.what) { case MSG_UPDATE_CARRIER_INFO: mUpdateCarrierInfo.accept( - (CarrierTextManager.CarrierTextCallbackInfo) msg.obj); + (CarrierTextController.CarrierTextCallbackInfo) msg.obj); break; case MSG_UPDATE_STATE: mUpdateState.run(); @@ -365,13 +362,13 @@ public class QSCarrierGroupController { private final Handler mHandler; private final Looper mLooper; private final NetworkController mNetworkController; - private final CarrierTextManager.Builder mCarrierTextControllerBuilder; + private final CarrierTextController.Builder mCarrierTextControllerBuilder; private final Context mContext; @Inject public Builder(ActivityStarter activityStarter, @Background Handler handler, @Main Looper looper, NetworkController networkController, - CarrierTextManager.Builder carrierTextControllerBuilder, Context context) { + CarrierTextController.Builder carrierTextControllerBuilder, Context context) { mActivityStarter = activityStarter; mHandler = handler; mLooper = looper; diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index 9fe949b15933..dce081f21581 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -25,7 +25,6 @@ import android.util.TypedValue; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.Menu; -import android.view.MenuItem; import android.view.View; import android.widget.LinearLayout; import android.widget.Toolbar; @@ -48,7 +47,6 @@ import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; public class QSCustomizer extends LinearLayout { static final int MENU_RESET = Menu.FIRST; - static final int MENU_REMOVE_LABELS = Menu.FIRST + 1; static final String EXTRA_QS_CUSTOMIZING = "qs_customizing"; private final QSDetailClipper mClipper; @@ -77,10 +75,6 @@ public class QSCustomizer extends LinearLayout { toolbar.getMenu().add(Menu.NONE, MENU_RESET, 0, mContext.getString(com.android.internal.R.string.reset)); - // Prototype menu item - toolbar.getMenu() - .add(Menu.NONE, MENU_REMOVE_LABELS, Menu.NONE, R.string.qs_remove_labels) - .setCheckable(true); toolbar.setTitle(R.string.qs_edit); mRecyclerView = findViewById(android.R.id.list); mTransparentView = findViewById(R.id.customizer_transparent_view); @@ -89,11 +83,6 @@ public class QSCustomizer extends LinearLayout { mRecyclerView.setItemAnimator(animator); } - MenuItem getRemoveItem() { - return ((Toolbar) findViewById(com.android.internal.R.id.action_bar)) - .getMenu().findItem(MENU_REMOVE_LABELS); - } - void updateResources() { LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams(); lp.height = mContext.getResources().getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java index d4bab2197249..f56a2bbefaf7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java @@ -17,9 +17,7 @@ package com.android.systemui.qs.customize; import static com.android.systemui.qs.customize.QSCustomizer.EXTRA_QS_CUSTOMIZING; -import static com.android.systemui.qs.customize.QSCustomizer.MENU_REMOVE_LABELS; import static com.android.systemui.qs.customize.QSCustomizer.MENU_RESET; -import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG; import android.content.res.Configuration; import android.os.Bundle; @@ -38,7 +36,6 @@ import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSEditEvent; import com.android.systemui.qs.QSFragment; -import com.android.systemui.qs.QSPanelController; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.statusbar.phone.LightBarController; @@ -46,14 +43,12 @@ import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.tuner.TunerService; import com.android.systemui.util.ViewController; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Named; /** {@link ViewController} for {@link QSCustomizer}. */ @QSScope @@ -67,8 +62,6 @@ public class QSCustomizerController extends ViewController<QSCustomizer> { private final ConfigurationController mConfigurationController; private final UiEventLogger mUiEventLogger; private final Toolbar mToolbar; - private final TunerService mTunerService; - private final boolean mQsLabelsFlag; private final OnMenuItemClickListener mOnMenuItemClickListener = new OnMenuItemClickListener() { @Override @@ -76,11 +69,6 @@ public class QSCustomizerController extends ViewController<QSCustomizer> { if (item.getItemId() == MENU_RESET) { mUiEventLogger.log(QSEditEvent.QS_EDIT_RESET); reset(); - } else if (item.getItemId() == MENU_REMOVE_LABELS) { - item.setChecked(!item.isChecked()); - mTunerService.setValue( - QSPanelController.QS_REMOVE_LABELS, item.isChecked() ? "1" : "0"); - return false; } return false; } @@ -111,20 +99,11 @@ public class QSCustomizerController extends ViewController<QSCustomizer> { } }; - private final TunerService.Tunable mTunable = new TunerService.Tunable() { - @Override - public void onTuningChanged(String key, String newValue) { - mToolbar.getMenu().findItem(MENU_REMOVE_LABELS) - .setChecked(newValue != null && !("0".equals(newValue))); - } - }; - @Inject protected QSCustomizerController(QSCustomizer view, TileQueryHelper tileQueryHelper, QSTileHost qsTileHost, TileAdapter tileAdapter, ScreenLifecycle screenLifecycle, KeyguardStateController keyguardStateController, LightBarController lightBarController, - ConfigurationController configurationController, UiEventLogger uiEventLogger, - TunerService tunerService, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag) { + ConfigurationController configurationController, UiEventLogger uiEventLogger) { super(view); mTileQueryHelper = tileQueryHelper; mQsTileHost = qsTileHost; @@ -136,21 +115,12 @@ public class QSCustomizerController extends ViewController<QSCustomizer> { mUiEventLogger = uiEventLogger; mToolbar = mView.findViewById(com.android.internal.R.id.action_bar); - mQsLabelsFlag = qsLabelsFlag; - - mTunerService = tunerService; } - @Override - protected void onInit() { - super.onInit(); - mView.getRemoveItem().setVisible(mQsLabelsFlag); - } @Override protected void onViewAttached() { mView.updateNavBackDrop(getResources().getConfiguration(), mLightBarController); - mTunerService.addTunable(mTunable, QSPanelController.QS_REMOVE_LABELS); mConfigurationController.addCallback(mConfigurationListener); @@ -181,7 +151,6 @@ public class QSCustomizerController extends ViewController<QSCustomizer> { @Override protected void onViewDetached() { - mTunerService.removeTunable(mTunable); mTileQueryHelper.setListener(null); mToolbar.setOnMenuItemClickListener(null); mConfigurationController.removeCallback(mConfigurationListener); diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 048fdc3a0e5a..7a91421b00a1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -75,6 +75,8 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private static final int ACTION_ADD = 1; private static final int ACTION_MOVE = 2; + private static final int NUM_COLUMNS_ID = R.integer.quick_settings_edit_num_columns; + private final Context mContext; private final Handler mHandler = new Handler(); @@ -87,6 +89,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private int mEditIndex; private int mTileDividerIndex; private int mFocusIndex; + private boolean mNeedsFocus; private List<String> mCurrentSpecs; private List<TileInfo> mOtherTiles; @@ -109,7 +112,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta mDecoration = new TileItemDecoration(context); mMarginDecoration = new MarginTileDecoration(); mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles); - mNumColumns = context.getResources().getInteger(R.integer.quick_settings_num_columns); + mNumColumns = context.getResources().getInteger(NUM_COLUMNS_ID); mAccessibilityDelegate = new TileAdapterDelegate(); } @@ -129,7 +132,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta * @return {@code true} if the number of columns changed, {@code false} otherwise */ public boolean updateNumColumns() { - int numColumns = mContext.getResources().getInteger(R.integer.quick_settings_num_columns); + int numColumns = mContext.getResources().getInteger(NUM_COLUMNS_ID); if (numColumns != mNumColumns) { mNumColumns = numColumns; return true; diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java index 35a8257bd5a7..10192bc20df9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java @@ -21,6 +21,7 @@ import android.hardware.display.ColorDisplayManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.util.settings.GlobalSettings; import javax.inject.Named; @@ -31,6 +32,8 @@ import dagger.Provides; public interface QSFlagsModule { String QS_LABELS_FLAG = "qs_labels_flag"; String RBC_AVAILABLE = "rbc_available"; + String PM_LITE_ENABLED = "pm_lite"; + String PM_LITE_SETTING = "sysui_pm_lite"; @Provides @SysUISingleton @@ -46,4 +49,11 @@ public interface QSFlagsModule { static boolean isReduceBrightColorsAvailable(Context context) { return ColorDisplayManager.isReduceBrightColorsAvailable(context); } + + @Provides + @SysUISingleton + @Named(PM_LITE_ENABLED) + static boolean isPMLiteEnabled(FeatureFlags featureFlags, GlobalSettings globalSettings) { + return featureFlags.isPMLiteEnabled() && globalSettings.getInt(PM_LITE_SETTING, 0) != 0; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java index a699e2ec7cfc..207b25d001b9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java @@ -105,22 +105,25 @@ public class QSTileView extends QSTileBaseView { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + mLabel.setSingleLine(false); super.onMeasure(widthMeasureSpec, heightMeasureSpec); - // Remeasure view if the primary label requires more then 2 lines or the secondary label - // text will be cut off. - if (mLabel.getLineCount() > mMaxLabelLines || !TextUtils.isEmpty(mSecondLine.getText()) - && mSecondLine.getLineHeight() > mSecondLine.getHeight()) { - if (!mLabel.isSingleLine()) { - mLabel.setSingleLine(); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } else { - if (mLabel.isSingleLine()) { - mLabel.setSingleLine(false); - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } + // Remeasure view if the primary label requires more than mMaxLabelLines lines or the + // secondary label text will be cut off. + if (shouldLabelBeSingleLine()) { + mLabel.setSingleLine(); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + + private boolean shouldLabelBeSingleLine() { + if (mLabel.getLineCount() > mMaxLabelLines) { + return true; + } else if (!TextUtils.isEmpty(mSecondLine.getText()) + && mLabel.getLineCount() > mMaxLabelLines - 1) { + return true; } + return false; } @Override @@ -171,9 +174,4 @@ public class QSTileView extends QSTileBaseView { mLabelContainer.setClickable(false); mLabelContainer.setLongClickable(false); } - - @Override - public void setShowLabels(boolean show) { - mHandler.post(() -> mLabelContainer.setVisibility(show ? VISIBLE : GONE)); - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt index dc81b702021f..07d48f32ff20 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.tileimpl +import android.animation.ValueAnimator import android.content.Context import android.content.res.ColorStateList import android.graphics.Color @@ -24,20 +25,24 @@ import android.graphics.drawable.PaintDrawable import android.graphics.drawable.RippleDrawable import android.service.quicksettings.Tile.STATE_ACTIVE import android.view.Gravity -import android.view.View import android.widget.LinearLayout +import android.widget.RelativeLayout import com.android.systemui.R import com.android.systemui.plugins.qs.QSIconView import com.android.systemui.plugins.qs.QSTile import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState +// Placeholder +private const val CORNER_RADIUS = 40f + class QSTileViewHorizontal( context: Context, icon: QSIconView ) : QSTileView(context, icon, false) { private var paintDrawable: PaintDrawable? = null - private var divider: View? = null + private var paintColor = Color.TRANSPARENT + private var paintAnimator: ValueAnimator? = null init { orientation = HORIZONTAL @@ -49,7 +54,12 @@ class QSTileViewHorizontal( override fun createLabel() { super.createLabel() - findViewById<LinearLayout>(R.id.label_group)?.gravity = Gravity.START + findViewById<LinearLayout>(R.id.label_group)?.apply { + gravity = Gravity.START + (layoutParams as? RelativeLayout.LayoutParams)?.apply { + removeRule(RelativeLayout.ALIGN_PARENT_TOP) + } + } mLabel.gravity = Gravity.START mLabel.textDirection = TEXT_DIRECTION_LOCALE mSecondLine.gravity = Gravity.START @@ -57,7 +67,7 @@ class QSTileViewHorizontal( val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding) mLabelContainer.setPaddingRelative(0, padding, padding, padding) (mLabelContainer.layoutParams as LayoutParams).gravity = - Gravity.CENTER_VERTICAL or Gravity.START + Gravity.CENTER_VERTICAL or Gravity.START } override fun updateRippleSize() { @@ -66,8 +76,8 @@ class QSTileViewHorizontal( override fun newTileBackground(): Drawable? { val d = super.newTileBackground() if (paintDrawable == null) { - paintDrawable = PaintDrawable(Color.WHITE).apply { - setCornerRadius(50f) + paintDrawable = PaintDrawable(paintColor).apply { + setCornerRadius(CORNER_RADIUS) } } if (d is RippleDrawable) { @@ -90,10 +100,39 @@ class QSTileViewHorizontal( override fun handleStateChanged(state: QSTile.State) { super.handleStateChanged(state) - paintDrawable?.setTint(getCircleColor(state.state)) mSecondLine.setTextColor(mLabel.textColors) mLabelContainer.background = null - divider?.backgroundTintList = mLabel.textColors + + val allowAnimations = animationsEnabled() && paintColor != Color.TRANSPARENT + val newColor = getCircleColor(state.state) + if (allowAnimations) { + animateToNewState(newColor) + } else { + if (newColor != paintColor) { + clearAnimator() + paintDrawable?.paint?.color = newColor + paintDrawable?.invalidateSelf() + } + } + paintColor = newColor + } + + private fun animateToNewState(newColor: Int) { + if (newColor != paintColor) { + clearAnimator() + paintAnimator = ValueAnimator.ofArgb(paintColor, newColor) + .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply { + addUpdateListener { animation: ValueAnimator -> + paintDrawable?.paint?.color = animation.animatedValue as Int + paintDrawable?.invalidateSelf() + } + start() + } + } + } + + private fun clearAnimator() { + paintAnimator?.cancel()?.also { paintAnimator = null } } override fun handleExpand(dualTarget: Boolean) {} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java index 3841daca7ebe..70287cd37d01 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java @@ -16,10 +16,13 @@ package com.android.systemui.qs.tiles; -import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA; +import static android.content.pm.PackageManager.FEATURE_CAMERA_TOGGLE; +import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; import static com.android.systemui.DejankUtils.whitelistIpcs; +import android.hardware.SensorPrivacyManager; +import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.os.Handler; import android.os.Looper; import android.provider.DeviceConfig; @@ -58,8 +61,8 @@ public class CameraToggleTile extends SensorPrivacyToggleTile { @Override public boolean isAvailable() { - return /*getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE) - && */whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + return getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE) + && whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "camera_toggle_enabled", false)); } @@ -75,7 +78,7 @@ public class CameraToggleTile extends SensorPrivacyToggleTile { } @Override - public int getSensorId() { + public @Sensor int getSensorId() { return CAMERA; } } 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 1dddc452ec1d..f03ce2c0b267 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -54,6 +54,7 @@ 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; @@ -269,13 +270,10 @@ public class CastTile extends QSTileImpl<BooleanState> { private final NetworkController.SignalCallback mSignalCallback = new NetworkController.SignalCallback() { @Override - public void setWifiIndicators(boolean enabled, - NetworkController.IconState statusIcon, - NetworkController.IconState qsIcon, boolean activityIn, boolean activityOut, - String description, boolean isTransient, String statusLabel) { + public void setWifiIndicators(WifiIndicators indicators) { // statusIcon.visible has the connected status information - boolean enabledAndConnected = - enabled && (qsIcon == null ? false : qsIcon.visible); + boolean enabledAndConnected = indicators.enabled + && (indicators.qsIcon == null ? false : indicators.qsIcon.visible); if (enabledAndConnected != mWifiConnected) { mWifiConnected = enabledAndConnected; // Hotspot is not connected, so changes here should update 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 720c5dc7026f..6a574d1d314b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -56,6 +56,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl; 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; @@ -264,21 +265,17 @@ public class CellularTile extends QSTileImpl<SignalState> { private final CallbackInfo mInfo = new CallbackInfo(); @Override - public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, - int qsType, boolean activityIn, boolean activityOut, - CharSequence typeContentDescription, - CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming, boolean showTriangle) { - if (qsIcon == null) { + public void setMobileDataIndicators(MobileDataIndicators indicators) { + if (indicators.qsIcon == null) { // Not data sim, don't display. return; } mInfo.dataSubscriptionName = mController.getMobileDataNetworkName(); - mInfo.dataContentDescription = - (description != null) ? typeContentDescriptionHtml : null; - mInfo.activityIn = activityIn; - mInfo.activityOut = activityOut; - mInfo.roaming = roaming; + mInfo.dataContentDescription = indicators.description != null + ? indicators.typeContentDescriptionHtml : null; + mInfo.activityIn = indicators.activityIn; + mInfo.activityOut = indicators.activityOut; + mInfo.roaming = indicators.roaming; mInfo.multipleSubs = mController.getNumberSubscriptions() > 1; refreshState(mInfo); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt index 41445917a011..3b9f5dc8ea03 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt @@ -25,7 +25,7 @@ import com.android.internal.logging.MetricsLogger import com.android.systemui.R import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.dagger.ControlsComponent -import com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE +import com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsDialog import com.android.systemui.dagger.qualifiers.Background @@ -92,7 +92,7 @@ class DeviceControlsTile @Inject constructor( override fun isAvailable(): Boolean { return featureFlags.isKeyguardLayoutEnabled && controlsLockscreen && - controlsComponent.getVisibility() != UNAVAILABLE + controlsComponent.getControlsController().isPresent } override fun newTileState(): QSTile.State { @@ -119,7 +119,7 @@ class DeviceControlsTile @Inject constructor( } override fun handleClick() { - if (state.state != Tile.STATE_UNAVAILABLE) { + if (state.state == Tile.STATE_ACTIVE) { mUiHandler.post { createDialog() controlsDialog?.show(controlsComponent.getControlsUiController().get()) @@ -129,15 +129,21 @@ class DeviceControlsTile @Inject constructor( override fun handleUpdateState(state: QSTile.State, arg: Any?) { state.label = tileLabel - state.secondaryLabel = "" - state.stateDescription = "" + state.contentDescription = state.label state.icon = icon - if (hasControlsApps.get()) { - state.state = Tile.STATE_ACTIVE + if (controlsComponent.isEnabled() && hasControlsApps.get()) { if (controlsDialog == null) { mUiHandler.post(this::createDialog) } + if (controlsComponent.getVisibility() == AVAILABLE) { + state.state = Tile.STATE_ACTIVE + state.secondaryLabel = "" + } else { + state.state = Tile.STATE_INACTIVE + state.secondaryLabel = mContext.getText(R.string.controls_tile_locked) + } + state.stateDescription = state.secondaryLabel } else { state.state = Tile.STATE_UNAVAILABLE dismissDialog() 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 0abff77c4e39..e1a1fd2679c5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java @@ -51,7 +51,9 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; 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 com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; import com.android.systemui.statusbar.policy.WifiIcons; import java.io.FileDescriptor; @@ -234,72 +236,44 @@ public class InternetTile extends QSTileImpl<SignalState> { @Override - public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, - boolean activityIn, boolean activityOut, String description, boolean isTransient, - String statusLabel) { + public void setWifiIndicators(WifiIndicators indicators) { if (DEBUG) { - Log.d(TAG, "setWifiIndicators: " - + "enabled = " + enabled + "," - + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + "," - + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + "," - + "activityIn = " + activityIn + "," - + "activityOut = " + activityOut + "," - + "description = " + description + "," - + "isTransient = " + isTransient + "," - + "statusLabel = " + statusLabel); + Log.d(TAG, "setWifiIndicators: " + indicators); } - // When airplane mode is enabled, we need to refresh the Internet Tile even if the WiFi - // is not the default network. - if (qsIcon == null) { + mWifiInfo.mEnabled = indicators.enabled; + if (indicators.qsIcon == null) { return; } - mWifiInfo.mConnected = qsIcon.visible; - mWifiInfo.mWifiSignalIconId = qsIcon.icon; - mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription; - mWifiInfo.mEnabled = enabled; - mWifiInfo.mSsid = description; - mWifiInfo.mActivityIn = activityIn; - mWifiInfo.mActivityOut = activityOut; - mWifiInfo.mIsTransient = isTransient; - mWifiInfo.mStatusLabel = statusLabel; + mWifiInfo.mConnected = indicators.qsIcon.visible; + mWifiInfo.mWifiSignalIconId = indicators.qsIcon.icon; + mWifiInfo.mWifiSignalContentDescription = indicators.qsIcon.contentDescription; + mWifiInfo.mEnabled = indicators.enabled; + mWifiInfo.mSsid = indicators.description; + mWifiInfo.mActivityIn = indicators.activityIn; + mWifiInfo.mActivityOut = indicators.activityOut; + mWifiInfo.mIsTransient = indicators.isTransient; + mWifiInfo.mStatusLabel = indicators.statusLabel; refreshState(mWifiInfo); } @Override - public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, - int qsType, boolean activityIn, boolean activityOut, - CharSequence typeContentDescription, - CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming, boolean showTriangle) { + public void setMobileDataIndicators(MobileDataIndicators indicators) { if (DEBUG) { - Log.d(TAG, "setMobileDataIndicators: " - + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + "," - + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + "," - + "statusType = " + statusType + "," - + "qsType = " + qsType + "," - + "activityIn = " + activityIn + "," - + "activityOut = " + activityOut + "," - + "typeContentDescription = " + typeContentDescription + "," - + "typeContentDescriptionHtml = " + typeContentDescriptionHtml + "," - + "description = " + description + "," - + "isWide = " + isWide + "," - + "subId = " + subId + "," - + "roaming = " + roaming + "," - + "showTriangle = " + showTriangle); + Log.d(TAG, "setMobileDataIndicators: " + indicators); } - if (qsIcon == null) { + if (indicators.qsIcon == null) { // Not data sim, don't display. return; } - mCellularInfo.mDataSubscriptionName = - description == null ? mController.getMobileDataNetworkName() : description; - mCellularInfo.mDataContentDescription = - (description != null) ? typeContentDescriptionHtml : null; - mCellularInfo.mMobileSignalIconId = qsIcon.icon; - mCellularInfo.mQsTypeIcon = qsType; - mCellularInfo.mActivityIn = activityIn; - mCellularInfo.mActivityOut = activityOut; - mCellularInfo.mRoaming = roaming; + mCellularInfo.mDataSubscriptionName = indicators.description == null + ? mController.getMobileDataNetworkName() : indicators.description; + mCellularInfo.mDataContentDescription = indicators.description != null + ? indicators.typeContentDescriptionHtml : null; + mCellularInfo.mMobileSignalIconId = indicators.qsIcon.icon; + mCellularInfo.mQsTypeIcon = indicators.qsType; + mCellularInfo.mActivityIn = indicators.activityIn; + mCellularInfo.mActivityOut = indicators.activityOut; + mCellularInfo.mRoaming = indicators.roaming; mCellularInfo.mMultipleSubs = mController.getNumberSubscriptions() > 1; refreshState(mCellularInfo); } @@ -465,14 +439,13 @@ public class InternetTile extends QSTileImpl<SignalState> { } minimalContentDescription.append( mContext.getString(R.string.quick_settings_internet_label)).append(","); - if (state.value) { - if (wifiConnected) { - minimalStateDescription.append(cb.mWifiSignalContentDescription); - minimalContentDescription.append(removeDoubleQuotes(cb.mSsid)); - } else if (!TextUtils.isEmpty(state.secondaryLabel)) { - minimalContentDescription.append(",").append(state.secondaryLabel); - } + if (state.value && wifiConnected) { + minimalStateDescription.append(cb.mWifiSignalContentDescription); + minimalContentDescription.append(removeDoubleQuotes(cb.mSsid)); + } else if (!TextUtils.isEmpty(state.secondaryLabel)) { + minimalContentDescription.append(",").append(state.secondaryLabel); } + state.stateDescription = minimalStateDescription.toString(); state.contentDescription = minimalContentDescription.toString(); state.dualLabelContentDescription = r.getString( diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java index 2f0071a1f198..e9b712df2154 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java @@ -16,10 +16,13 @@ package com.android.systemui.qs.tiles; -import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; +import static android.content.pm.PackageManager.FEATURE_MICROPHONE_TOGGLE; +import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; import static com.android.systemui.DejankUtils.whitelistIpcs; +import android.hardware.SensorPrivacyManager; +import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.os.Handler; import android.os.Looper; import android.provider.DeviceConfig; @@ -58,7 +61,9 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile { @Override public boolean isAvailable() { - return whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + return getHost().getContext().getPackageManager() + .hasSystemFeature(FEATURE_MICROPHONE_TOGGLE) + && whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "mic_toggle_enabled", false)); } @@ -74,7 +79,7 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile { } @Override - public int getSensorId() { + public @Sensor int getSensorId() { return MICROPHONE; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java index 00703e7f8403..0c582bdbe12f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java @@ -17,7 +17,7 @@ package com.android.systemui.qs.tiles; import android.content.Intent; -import android.hardware.SensorPrivacyManager.IndividualSensor; +import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.os.Handler; import android.os.Looper; import android.service.quicksettings.Tile; @@ -49,7 +49,7 @@ public abstract class SensorPrivacyToggleTile extends QSTileImpl<QSTile.BooleanS /** * @return Id of the sensor that will be toggled */ - public abstract @IndividualSensor int getSensorId(); + public abstract @Sensor int getSensorId(); /** * @return icon for the QS tile 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 a6fd01108fad..341e67c9393f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -51,8 +51,8 @@ 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.IconState; 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.wifitrackerlib.WifiEntry; @@ -303,22 +303,20 @@ public class WifiTile extends QSTileImpl<SignalState> { final CallbackInfo mInfo = new CallbackInfo(); @Override - public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, - boolean activityIn, boolean activityOut, String description, boolean isTransient, - String statusLabel) { - if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + enabled); - if (qsIcon == null) { + public void setWifiIndicators(WifiIndicators indicators) { + if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + indicators.enabled); + if (indicators.qsIcon == null) { return; } - mInfo.enabled = enabled; - mInfo.connected = qsIcon.visible; - mInfo.wifiSignalIconId = qsIcon.icon; - mInfo.ssid = description; - mInfo.activityIn = activityIn; - mInfo.activityOut = activityOut; - mInfo.wifiSignalContentDescription = qsIcon.contentDescription; - mInfo.isTransient = isTransient; - mInfo.statusLabel = statusLabel; + mInfo.enabled = indicators.enabled; + mInfo.connected = indicators.qsIcon.visible; + mInfo.wifiSignalIconId = indicators.qsIcon.icon; + mInfo.ssid = indicators.description; + mInfo.activityIn = indicators.activityIn; + mInfo.activityOut = indicators.activityOut; + mInfo.wifiSignalContentDescription = indicators.qsIcon.contentDescription; + mInfo.isTransient = indicators.isTransient; + mInfo.statusLabel = indicators.statusLabel; if (isShowingDetail()) { mDetailAdapter.updateItems(); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java index 1386ddfa7692..5b55864eed8a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java @@ -97,8 +97,8 @@ public class CropView extends View { float bottom = mBottomCrop + mBottomDelta; drawShade(canvas, 0, top); drawShade(canvas, bottom, 1f); - drawHandle(canvas, top); - drawHandle(canvas, bottom); + drawHandle(canvas, top, /* draw the handle tab down */ false); + drawHandle(canvas, bottom, /* draw the handle tab up */ true); } @Override @@ -122,7 +122,7 @@ public class CropView extends View { } else { // Bottom mBottomDelta = pixelsToFraction((int) MathUtils.constrain(delta, topPx + 2 * mCropTouchMargin - bottomPx, - getMeasuredHeight() - bottomPx)); + getHeight() - bottomPx)); } updateListener(event); invalidate(); @@ -140,6 +140,25 @@ public class CropView extends View { } /** + * Set the given boundary to the given value without animation. + */ + public void setBoundaryTo(CropBoundary boundary, float value) { + switch (boundary) { + case TOP: + mTopCrop = value; + break; + case BOTTOM: + mBottomCrop = value; + break; + case NONE: + Log.w(TAG, "No boundary selected for animation"); + break; + } + + invalidate(); + } + + /** * Animate the given boundary to the given value. */ public void animateBoundaryTo(CropBoundary boundary, float value) { @@ -212,21 +231,25 @@ public class CropView extends View { } private void drawShade(Canvas canvas, float fracStart, float fracEnd) { - canvas.drawRect(0, fractionToPixels(fracStart), getMeasuredWidth(), + canvas.drawRect(0, fractionToPixels(fracStart), getWidth(), fractionToPixels(fracEnd), mShadePaint); } - private void drawHandle(Canvas canvas, float frac) { + private void drawHandle(Canvas canvas, float frac, boolean handleTabUp) { int y = fractionToPixels(frac); - canvas.drawLine(0, y, getMeasuredWidth(), y, mHandlePaint); + canvas.drawLine(0, y, getWidth(), y, mHandlePaint); + float radius = 15 * getResources().getDisplayMetrics().density; + float x = getWidth() * .9f; + canvas.drawArc(x - radius, y - radius, x + radius, y + radius, handleTabUp ? 180 : 0, 180, + true, mHandlePaint); } private int fractionToPixels(float frac) { - return (int) (frac * getMeasuredHeight()); + return (int) (frac * getHeight()); } private float pixelsToFraction(int px) { - return px / (float) getMeasuredHeight(); + return px / (float) getHeight(); } private CropBoundary nearestBoundary(MotionEvent event, int topPx, int bottomPx) { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java new file mode 100644 index 000000000000..5a13ea55222d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -0,0 +1,329 @@ +/* + * 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.screenshot; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.HardwareRenderer; +import android.graphics.RecordingCanvas; +import android.graphics.Rect; +import android.graphics.RenderNode; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.SystemClock; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.widget.ImageView; + +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; + +import com.google.common.util.concurrent.ListenableFuture; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.time.ZonedDateTime; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +/** + * LongScreenshotActivity acquires bitmap data for a long screenshot and lets the user trim the top + * and bottom before saving/sharing/editing. + */ +public class LongScreenshotActivity extends Activity { + private static final String TAG = "LongScreenshotActivity"; + + private static final String IMAGE_PATH_KEY = "saved-image"; + private static final String TOP_BOUNDARY_KEY = "top-boundary"; + private static final String BOTTOM_BOUNDARY_KEY = "bottom-boundary"; + + private final UiEventLogger mUiEventLogger; + private final ScrollCaptureController mScrollCaptureController; + private final ScrollCaptureClient.Connection mConnection; + private final Executor mUiExecutor; + private final Executor mBackgroundExecutor; + private final ImageExporter mImageExporter; + + private String mSavedImagePath; + // If true, the activity is re-loading an image from storage, which should either succeed and + // populate the UI or fail and finish the activity. + private boolean mRestoringInstance; + + private ImageView mPreview; + private View mSave; + private View mCancel; + private View mEdit; + private View mShare; + private CropView mCropView; + private MagnifierView mMagnifierView; + + private enum PendingAction { + SHARE, + EDIT, + SAVE + } + + @Inject + public LongScreenshotActivity(UiEventLogger uiEventLogger, + ImageExporter imageExporter, + @Main Executor mainExecutor, + @Background Executor bgExecutor, + Context context) { + mUiEventLogger = uiEventLogger; + mUiExecutor = mainExecutor; + mBackgroundExecutor = bgExecutor; + mImageExporter = imageExporter; + + mScrollCaptureController = new ScrollCaptureController(context, mainExecutor, bgExecutor, + imageExporter); + + mConnection = ScreenshotController.takeScrollCaptureConnection(); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.long_screenshot); + + mPreview = findViewById(R.id.preview); + mSave = findViewById(R.id.save); + mCancel = findViewById(R.id.cancel); + mEdit = findViewById(R.id.edit); + mShare = findViewById(R.id.share); + mCropView = findViewById(R.id.crop_view); + mMagnifierView = findViewById(R.id.magnifier); + mCropView.setCropInteractionListener(mMagnifierView); + + mSave.setOnClickListener(this::onClicked); + mCancel.setOnClickListener(this::onClicked); + mEdit.setOnClickListener(this::onClicked); + mShare.setOnClickListener(this::onClicked); + + if (savedInstanceState != null) { + String imagePath = savedInstanceState.getString(IMAGE_PATH_KEY); + if (!TextUtils.isEmpty(imagePath)) { + mRestoringInstance = true; + mBackgroundExecutor.execute(() -> { + Bitmap bitmap = BitmapFactory.decodeFile(imagePath); + if (bitmap == null) { + Log.e(TAG, "Failed to read bitmap from " + imagePath); + finishAndRemoveTask(); + } else { + runOnUiThread(() -> { + BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap); + mPreview.setImageDrawable(drawable); + mMagnifierView.setDrawable(drawable, bitmap.getWidth(), + bitmap.getHeight()); + + mCropView.setBoundaryTo(CropView.CropBoundary.TOP, + savedInstanceState.getFloat(TOP_BOUNDARY_KEY, 0f)); + mCropView.setBoundaryTo(CropView.CropBoundary.BOTTOM, + savedInstanceState.getFloat(BOTTOM_BOUNDARY_KEY, 1f)); + mRestoringInstance = false; + // Reuse the same path for subsequent restoration. + mSavedImagePath = imagePath; + Log.d(TAG, "Loaded bitmap from " + imagePath); + }); + } + }); + } + } + } + + @Override + public void onStart() { + super.onStart(); + if (mPreview.getDrawable() == null && !mRestoringInstance) { + if (mConnection == null) { + Log.e(TAG, "Failed to get scroll capture connection, bailing out"); + finishAndRemoveTask(); + return; + } + doCapture(); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(IMAGE_PATH_KEY, mSavedImagePath); + outState.putFloat(TOP_BOUNDARY_KEY, mCropView.getTopBoundary()); + outState.putFloat(BOTTOM_BOUNDARY_KEY, mCropView.getBottomBoundary()); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (isFinishing() && !TextUtils.isEmpty(mSavedImagePath)) { + Log.d(TAG, "Deleting " + mSavedImagePath); + File file = new File(mSavedImagePath); + file.delete(); + } + } + + private void setButtonsEnabled(boolean enabled) { + mSave.setEnabled(enabled); + mCancel.setEnabled(enabled); + mEdit.setEnabled(enabled); + mShare.setEnabled(enabled); + } + + private void doEdit(Uri uri) { + String editorPackage = getString(R.string.config_screenshotEditor); + Intent intent = new Intent(Intent.ACTION_EDIT); + if (!TextUtils.isEmpty(editorPackage)) { + intent.setComponent(ComponentName.unflattenFromString(editorPackage)); + } + intent.setDataAndType(uri, "image/png"); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK + | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + + startActivityAsUser(intent, UserHandle.CURRENT); + finishAndRemoveTask(); + } + + private void doShare(Uri uri) { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("image/png"); + intent.putExtra(Intent.EXTRA_STREAM, uri); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK + | Intent.FLAG_GRANT_READ_URI_PERMISSION); + Intent sharingChooserIntent = Intent.createChooser(intent, null) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT); + } + + private void onClicked(View v) { + int id = v.getId(); + v.setPressed(true); + setButtonsEnabled(false); + if (id == R.id.save) { + startExport(PendingAction.SAVE); + } else if (id == R.id.cancel) { + finishAndRemoveTask(); + } else if (id == R.id.edit) { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EDIT); + startExport(PendingAction.EDIT); + } else if (id == R.id.share) { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE); + startExport(PendingAction.SHARE); + } + } + + private void startExport(PendingAction action) { + Drawable drawable = mPreview.getDrawable(); + + Rect croppedPortion = new Rect( + 0, + (int) (drawable.getIntrinsicHeight() * mCropView.getTopBoundary()), + drawable.getIntrinsicWidth(), + (int) (drawable.getIntrinsicHeight() * mCropView.getBottomBoundary())); + ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export( + mBackgroundExecutor, UUID.randomUUID(), getBitmap(croppedPortion, drawable), + ZonedDateTime.now()); + exportFuture.addListener(() -> { + try { + ImageExporter.Result result = exportFuture.get(); + setButtonsEnabled(true); + switch (action) { + case EDIT: + doEdit(result.uri); + break; + case SHARE: + doShare(result.uri); + break; + case SAVE: + // Nothing more to do + finishAndRemoveTask(); + break; + } + } catch (InterruptedException | ExecutionException e) { + Log.e(TAG, "failed to export", e); + setButtonsEnabled(true); + } + }, mUiExecutor); + } + + private Bitmap getBitmap(Rect bounds, Drawable drawable) { + final RenderNode output = new RenderNode("Bitmap Export"); + output.setPosition(0, 0, bounds.width(), bounds.height()); + RecordingCanvas canvas = output.beginRecording(); + // Translating the canvas instead of setting drawable bounds since the drawable is still + // used in the preview. + canvas.translate(0, -bounds.top); + drawable.draw(canvas); + output.endRecording(); + return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height()); + } + + private void saveCacheBitmap(ImageTileSet tileSet) { + long startTime = SystemClock.uptimeMillis(); + Bitmap bitmap = tileSet.toBitmap(); + // TODO(b/181562529) Remove this + mPreview.setImageDrawable(tileSet.getDrawable()); + try { + File file = File.createTempFile("long_screenshot", ".png", null); + FileOutputStream stream = new FileOutputStream(file); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + stream.flush(); + stream.close(); + mSavedImagePath = file.getAbsolutePath(); + Log.d(TAG, "Saved to " + file.getAbsolutePath() + " in " + + (SystemClock.uptimeMillis() - startTime) + "ms"); + } catch (IOException e) { + Log.e(TAG, "Failed to save bitmap", e); + } + } + + private void doCapture() { + mScrollCaptureController.start(mConnection, + new ScrollCaptureController.ScrollCaptureCallback() { + @Override + public void onError() { + Log.e(TAG, "Error capturing long screenshot!"); + finishAndRemoveTask(); + } + + @Override + public void onComplete(ImageTileSet imageTileSet) { + Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x " + + imageTileSet.getHeight()); + mPreview.setImageDrawable(imageTileSet.getDrawable()); + mMagnifierView.setDrawable(imageTileSet.getDrawable(), + imageTileSet.getWidth(), imageTileSet.getHeight()); + mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f); + mBackgroundExecutor.execute(() -> saveCacheBitmap(imageTileSet)); + } + }); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java index f8f1d3ac9a5b..7a0ec4c520b2 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java @@ -16,6 +16,7 @@ package com.android.systemui.screenshot; +import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; @@ -77,13 +78,12 @@ public class MagnifierView extends View implements CropView.CropInteractionListe mCheckerboardPaint.setColor(Color.GRAY); } - public void setImageTileset(ImageTileSet tiles) { - if (tiles != null) { - mDrawable = tiles.getDrawable(); - mDrawable.setBounds(0, 0, tiles.getWidth(), tiles.getHeight()); - } else { - mDrawable = null; - } + /** + * Set the drawable to be displayed by the magnifier. + */ + public void setDrawable(@NonNull Drawable drawable, int width, int height) { + mDrawable = drawable; + mDrawable.setBounds(0, 0, width, height); invalidate(); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 953b40b6e17b..805ac7cf1ec9 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -41,6 +41,7 @@ import android.app.Notification; import android.app.WindowContext; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; @@ -100,6 +101,8 @@ import javax.inject.Inject; public class ScreenshotController { private static final String TAG = logTag(ScreenshotController.class); + private static ScrollCaptureClient.Connection sScrollConnection; + /** * POD used in the AsyncTask which saves an image in the background. */ @@ -219,6 +222,12 @@ public class ScreenshotController { | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS); + public static @Nullable ScrollCaptureClient.Connection takeScrollCaptureConnection() { + ScrollCaptureClient.Connection connection = sScrollConnection; + sScrollConnection = null; + return connection; + } + @Inject ScreenshotController( Context context, @@ -316,6 +325,7 @@ public class ScreenshotController { attachWindow(); mWindow.setContentView(mScreenshotView); + mScreenshotView.requestApplyInsets(); mScreenshotView.takePartialScreenshot( rect -> takeScreenshotInternal(finisher, rect)); @@ -597,21 +607,12 @@ public class ScreenshotController { } private void runScrollCapture(ScrollCaptureClient.Connection connection) { - cancelTimeout(); - ScrollCaptureController controller = new ScrollCaptureController(mContext, connection, - mMainExecutor, mBgExecutor, mImageExporter, mUiEventLogger); - controller.attach(mWindow); - controller.start(new TakeScreenshotService.RequestCallback() { - @Override - public void reportError() { - } + sScrollConnection = connection; // For LongScreenshotActivity to pick up. - @Override - public void onFinish() { - Log.d(TAG, "onFinish from ScrollCaptureController"); - finishDismiss(); - } - }); + Intent intent = new Intent(mContext, LongScreenshotActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + mContext.startActivity(intent); + dismissScreenshot(false); } /** diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java index dc639dce4951..54b99bbe74cc 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java @@ -21,24 +21,25 @@ import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL; import static java.lang.Math.min; import static java.util.Objects.requireNonNull; +import android.annotation.BinderThread; import android.annotation.UiContext; import android.app.ActivityTaskManager; import android.content.Context; import android.graphics.PixelFormat; -import android.graphics.Point; import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.media.Image; import android.media.ImageReader; import android.os.IBinder; +import android.os.ICancellationSignal; import android.os.RemoteException; import android.util.Log; import android.view.IScrollCaptureCallbacks; import android.view.IScrollCaptureConnection; import android.view.IWindowManager; +import android.view.ScrollCaptureResponse; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.view.ScrollCaptureViewSupport; import java.util.function.Consumer; @@ -62,16 +63,19 @@ public class ScrollCaptureClient { */ public interface Connection { /** - * Session start should be deferred until UI is active because of resource allocation and - * potential visible side effects in the target window. - * + * Start a session. + * @param sessionConsumer listener to receive the session once active * @param maxPages the capture buffer size expressed as a multiple of the content height */ + // TODO ListenableFuture void start(Consumer<Session> sessionConsumer, float maxPages); /** - * Close the connection. + * Close the connection. Must end capture if started to avoid potential unwanted visual + * artifacts. + * + * @see Session#end(Runnable) */ void close(); } @@ -119,6 +123,7 @@ public class ScrollCaptureClient { * @param top the top (y) position of the tile to capture, in content rect space * @param consumer listener to be informed of the result */ + // TODO ListenableFuture void requestTile(int top, Consumer<CaptureResult> consumer); /** @@ -129,16 +134,31 @@ public class ScrollCaptureClient { */ int getMaxTiles(); + /** + * @return the height of each image tile + */ int getTileHeight(); + /** + * @return the height of scrollable content being captured + */ int getPageHeight(); + /** + * @return the width of the scrollable page + */ int getPageWidth(); /** + * @return the bounds on screen of the window being captured. + */ + Rect getWindowBounds(); + + /** * End the capture session, return the target app to original state. The listener * will be called when the target app is ready to before visible and interactive. */ + // TODO ListenableFuture void end(Runnable listener); } @@ -185,13 +205,13 @@ public class ScrollCaptureClient { + ", taskId=" + taskId + ", consumer=" + consumer + ")"); } mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId, - new ControllerCallbacks(consumer)); + new ClientCallbacks(consumer)); } catch (RemoteException e) { Log.e(TAG, "Ignored remote exception", e); } } - private static class ControllerCallbacks extends IScrollCaptureCallbacks.Stub implements + private static class ClientCallbacks extends IScrollCaptureCallbacks.Stub implements Connection, Session, IBinder.DeathRecipient { private IScrollCaptureConnection mConnection; @@ -206,46 +226,63 @@ public class ScrollCaptureClient { private int mTileWidth; private Rect mRequestRect; private boolean mStarted; + + private ICancellationSignal mCancellationSignal; + private Rect mWindowBounds; + private Rect mBoundsInWindow; private int mMaxTiles; - private ControllerCallbacks(Consumer<Connection> connectionConsumer) { + private ClientCallbacks(Consumer<Connection> connectionConsumer) { mConnectionConsumer = connectionConsumer; } - // IScrollCaptureCallbacks - + @BinderThread @Override - public void onConnected(IScrollCaptureConnection connection, Rect scrollBounds, - Point positionInWindow) throws RemoteException { + public void onScrollCaptureResponse(ScrollCaptureResponse response) throws RemoteException { if (DEBUG_SCROLL) { - Log.d(TAG, "onConnected(connection=" + connection + ", scrollBounds=" + scrollBounds - + ", positionInWindow=" + positionInWindow + ")"); + Log.d(TAG, "onScrollCaptureResponse(response=" + response + ")"); } - mConnection = connection; - mConnection.asBinder().linkToDeath(this, 0); - mScrollBounds = scrollBounds; - mConnectionConsumer.accept(this); - mConnectionConsumer = null; - - int pxPerPage = mScrollBounds.width() * mScrollBounds.height(); - int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE)); - mTileWidth = mScrollBounds.width(); - mTileHeight = pxPerTile / mScrollBounds.width(); - if (DEBUG_SCROLL) { - Log.d(TAG, "scrollBounds: " + mScrollBounds); - Log.d(TAG, "tile dimen: " + mTileWidth + "x" + mTileHeight); + if (response.isConnected()) { + mConnection = response.getConnection(); + mConnection.asBinder().linkToDeath(this, 0); + mWindowBounds = response.getWindowBounds(); + mBoundsInWindow = response.getBoundsInWindow(); + + int pxPerPage = mBoundsInWindow.width() * mBoundsInWindow.height(); + int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE)); + mTileWidth = mBoundsInWindow.width(); + mTileHeight = pxPerTile / mBoundsInWindow.width(); + if (DEBUG_SCROLL) { + Log.d(TAG, "boundsInWindow: " + mBoundsInWindow); + Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight); + Log.d(TAG, "maxHeight: " + (mMaxTiles * mTileHeight) + "px"); + } + mConnectionConsumer.accept(this); } + mConnectionConsumer = null; } @Override - public void onUnavailable() throws RemoteException { + public void start(Consumer<Session> sessionConsumer, float maxPages) { if (DEBUG_SCROLL) { - Log.d(TAG, "onUnavailable"); + Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + "," + + " maxPages=" + maxPages + ")"); + } + mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE); + mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888, + mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); + mSessionConsumer = sessionConsumer; + + try { + mCancellationSignal = mConnection.startCapture(mReader.getSurface()); + mStarted = true; + } catch (RemoteException e) { + Log.w(TAG, "Failed to start", e); + mReader.close(); } - // The targeted app does not support scroll capture - // or the window could not be found... etc etc. } + @BinderThread @Override public void onCaptureStarted() { if (DEBUG_SCROLL) { @@ -256,13 +293,25 @@ public class ScrollCaptureClient { } @Override - public void onCaptureBufferSent(long frameNumber, Rect contentArea) { - Image image = null; - if (frameNumber != ScrollCaptureViewSupport.NO_FRAME_PRODUCED) { - image = mReader.acquireNextImage(); + public void requestTile(int top, Consumer<CaptureResult> consumer) { + if (DEBUG_SCROLL) { + Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")"); + } + cancelPendingRequest(); + mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight); + mResultConsumer = consumer; + try { + mCancellationSignal = mConnection.requestImage(mRequestRect); + } catch (RemoteException e) { + Log.e(TAG, "Caught remote exception from requestImage", e); } + } + + @Override + public void onImageRequestCompleted(int flags, Rect contentArea) { + Image image = mReader.acquireLatestImage(); if (DEBUG_SCROLL) { - Log.d(TAG, "onCaptureBufferSent(frameNumber=" + frameNumber + Log.d(TAG, "onCaptureBufferSent(flags=" + flags + ", contentArea=" + contentArea + ") image=" + image); } // Save and clear first, since the consumer will likely request the next @@ -273,17 +322,49 @@ public class ScrollCaptureClient { } @Override - public void onConnectionClosed() { + public void end(Runnable listener) { if (DEBUG_SCROLL) { - Log.d(TAG, "onConnectionClosed()"); + Log.d(TAG, "end(listener=" + listener + ")"); } - disconnect(); + if (mStarted) { + mShutdownListener = listener; + mReader.close(); + try { + // listener called from onConnectionClosed callback + mConnection.endCapture(); + } catch (RemoteException e) { + Log.d(TAG, "Ignored exception from endCapture()", e); + disconnect(); + listener.run(); + } + } else { + disconnect(); + listener.run(); + } + } + + @BinderThread + @Override + public void onCaptureEnded() { + close(); if (mShutdownListener != null) { mShutdownListener.run(); mShutdownListener = null; } } + @Override + public void close() { + if (mConnection != null) { + try { + mConnection.close(); + } catch (RemoteException e) { + /* ignore */ + } + disconnect(); + } + } + // Misc private void disconnect() { @@ -293,63 +374,25 @@ public class ScrollCaptureClient { mConnection = null; } - // ScrollCaptureController.Connection - - @Override - public void start(Consumer<Session> sessionConsumer, float maxPages) { - if (DEBUG_SCROLL) { - Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + "," - + " maxPages=" + maxPages + ")" - + " [maxHeight: " + (mMaxTiles * mTileHeight) + "px]"); - } - mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE); - mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888, - mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); - mSessionConsumer = sessionConsumer; - try { - mConnection.startCapture(mReader.getSurface()); - mStarted = true; - } catch (RemoteException e) { - Log.w(TAG, "Failed to start", e); - } - } - - @Override - public void close() { - end(null); - } - - // ScrollCaptureController.Session - + /** + * The process hosting the window went away abruptly! + */ @Override - public void end(Runnable listener) { + public void binderDied() { if (DEBUG_SCROLL) { - Log.d(TAG, "end(listener=" + listener + ")"); - } - if (mStarted) { - mShutdownListener = listener; - try { - // listener called from onConnectionClosed callback - mConnection.endCapture(); - } catch (RemoteException e) { - Log.d(TAG, "Ignored exception from endCapture()", e); - disconnect(); - listener.run(); - } - } else { - disconnect(); - listener.run(); + Log.d(TAG, "binderDied()"); } + disconnect(); } @Override public int getPageHeight() { - return mScrollBounds.height(); + return mBoundsInWindow.height(); } @Override public int getPageWidth() { - return mScrollBounds.width(); + return mBoundsInWindow.width(); } @Override @@ -357,34 +400,24 @@ public class ScrollCaptureClient { return mTileHeight; } - @Override - public int getMaxTiles() { - return mMaxTiles; + public Rect getWindowBounds() { + return new Rect(mWindowBounds); } @Override - public void requestTile(int top, Consumer<CaptureResult> consumer) { - if (DEBUG_SCROLL) { - Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")"); - } - mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight); - mResultConsumer = consumer; - try { - mConnection.requestImage(mRequestRect); - } catch (RemoteException e) { - Log.e(TAG, "Caught remote exception from requestImage", e); - } + public int getMaxTiles() { + return mMaxTiles; } - /** - * The process hosting the window went away abruptly! - */ - @Override - public void binderDied() { - if (DEBUG_SCROLL) { - Log.d(TAG, "binderDied()"); + private void cancelPendingRequest() { + if (mCancellationSignal != null) { + try { + mCancellationSignal.cancel(); + } catch (RemoteException e) { + /* ignore */ + } + mCancellationSignal = null; } - disconnect(); } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index ad5e637b189e..bf65132166b6 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -16,41 +16,24 @@ package com.android.systemui.screenshot; -import android.annotation.IdRes; import android.annotation.UiThread; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.graphics.Rect; import android.net.Uri; -import android.os.UserHandle; import android.provider.Settings; -import android.text.TextUtils; import android.util.Log; -import android.view.View; -import android.view.ViewTreeObserver.InternalInsetsInfo; -import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; -import android.view.Window; -import android.widget.ImageView; -import com.android.internal.logging.UiEventLogger; -import com.android.systemui.R; import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult; import com.android.systemui.screenshot.ScrollCaptureClient.Connection; import com.android.systemui.screenshot.ScrollCaptureClient.Session; -import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback; - -import com.google.common.util.concurrent.ListenableFuture; import java.time.ZonedDateTime; import java.util.UUID; -import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; /** * Interaction controller between the UI and ScrollCaptureClient. */ -public class ScrollCaptureController implements OnComputeInternalInsetsListener { +public class ScrollCaptureController { private static final String TAG = "ScrollCaptureController"; private static final float MAX_PAGES_DEFAULT = 3f; @@ -64,188 +47,44 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener private boolean mAtTopEdge; private Session mSession; - // TODO: Support saving without additional action. - private enum PendingAction { - SHARE, - EDIT, - SAVE - } - public static final int MAX_HEIGHT = 12000; - private final Connection mConnection; private final Context mContext; private final Executor mUiExecutor; private final Executor mBgExecutor; private final ImageExporter mImageExporter; private final ImageTileSet mImageTileSet; - private final UiEventLogger mUiEventLogger; private ZonedDateTime mCaptureTime; private UUID mRequestId; - private RequestCallback mCallback; - private Window mWindow; - private ImageView mPreview; - private View mSave; - private View mCancel; - private View mEdit; - private View mShare; - private CropView mCropView; - private MagnifierView mMagnifierView; + private ScrollCaptureCallback mCaptureCallback; - public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor, - Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) { + public ScrollCaptureController(Context context, Executor uiExecutor, Executor bgExecutor, + ImageExporter exporter) { mContext = context; - mConnection = connection; mUiExecutor = uiExecutor; mBgExecutor = bgExecutor; mImageExporter = exporter; - mUiEventLogger = uiEventLogger; mImageTileSet = new ImageTileSet(context.getMainThreadHandler()); } /** - * @param window the window to display the preview - */ - public void attach(Window window) { - mWindow = window; - } - - /** * Run scroll capture! * + * @param connection connection to the remote window to be used * @param callback request callback to report back to the service */ - public void start(RequestCallback callback) { + public void start(Connection connection, ScrollCaptureCallback callback) { mCaptureTime = ZonedDateTime.now(); mRequestId = UUID.randomUUID(); - mCallback = callback; - - setContentView(R.layout.long_screenshot); - mWindow.getDecorView().getViewTreeObserver() - .addOnComputeInternalInsetsListener(this); - mPreview = findViewById(R.id.preview); - - mSave = findViewById(R.id.save); - mCancel = findViewById(R.id.cancel); - mEdit = findViewById(R.id.edit); - mShare = findViewById(R.id.share); - mCropView = findViewById(R.id.crop_view); - mMagnifierView = findViewById(R.id.magnifier); - mCropView.setCropInteractionListener(mMagnifierView); - - mSave.setOnClickListener(this::onClicked); - mCancel.setOnClickListener(this::onClicked); - mEdit.setOnClickListener(this::onClicked); - mShare.setOnClickListener(this::onClicked); + mCaptureCallback = callback; float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(), SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT); - mConnection.start(this::startCapture, maxPages); - } - - - /** Ensure the entire window is touchable */ - public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) { - inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); - } - - void disableButtons() { - mSave.setEnabled(false); - mCancel.setEnabled(false); - mEdit.setEnabled(false); - mShare.setEnabled(false); - } - - private void onClicked(View v) { - Log.d(TAG, "button clicked!"); - - int id = v.getId(); - v.setPressed(true); - disableButtons(); - if (id == R.id.save) { - startExport(PendingAction.SAVE); - } else if (id == R.id.cancel) { - doFinish(); - } else if (id == R.id.edit) { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EDIT); - startExport(PendingAction.EDIT); - } else if (id == R.id.share) { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE); - startExport(PendingAction.SHARE); - } - } - - private void doFinish() { - mPreview.setImageDrawable(null); - mMagnifierView.setImageTileset(null); - mImageTileSet.clear(); - mCallback.onFinish(); - mWindow.getDecorView().getViewTreeObserver() - .removeOnComputeInternalInsetsListener(this); - } - - private void startExport(PendingAction action) { - Rect croppedPortion = new Rect( - 0, - (int) (mImageTileSet.getHeight() * mCropView.getTopBoundary()), - mImageTileSet.getWidth(), - (int) (mImageTileSet.getHeight() * mCropView.getBottomBoundary())); - ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export( - mBgExecutor, mRequestId, mImageTileSet.toBitmap(croppedPortion), mCaptureTime); - exportFuture.addListener(() -> { - try { - ImageExporter.Result result = exportFuture.get(); - if (action == PendingAction.EDIT) { - doEdit(result.uri); - } else if (action == PendingAction.SHARE) { - doShare(result.uri); - } - doFinish(); - } catch (InterruptedException | ExecutionException e) { - Log.e(TAG, "failed to export", e); - mCallback.onFinish(); - } - }, mUiExecutor); - } - - private void doEdit(Uri uri) { - String editorPackage = mContext.getString(R.string.config_screenshotEditor); - Intent intent = new Intent(Intent.ACTION_EDIT); - if (!TextUtils.isEmpty(editorPackage)) { - intent.setComponent(ComponentName.unflattenFromString(editorPackage)); - } - intent.setType("image/png"); - intent.setData(uri); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - - mContext.startActivityAsUser(intent, UserHandle.CURRENT); + connection.start(this::startCapture, maxPages); } - private void doShare(Uri uri) { - Intent intent = new Intent(Intent.ACTION_SEND); - intent.setType("image/png"); - intent.setData(uri); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_GRANT_READ_URI_PERMISSION); - Intent sharingChooserIntent = Intent.createChooser(intent, null) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_GRANT_READ_URI_PERMISSION); - - mContext.startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT); - } - - private void setContentView(@IdRes int id) { - mWindow.setContentView(id); - } - - <T extends View> T findViewById(@IdRes int res) { - return mWindow.findViewById(res); - } - - private void onCaptureResult(CaptureResult result) { Log.d(TAG, "onCaptureResult: " + result); boolean emptyResult = result.captured.height() == 0; @@ -327,11 +166,26 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener Log.d(TAG, "afterCaptureComplete"); if (mImageTileSet.isEmpty()) { - session.end(mCallback::onFinish); + mCaptureCallback.onError(); } else { - mPreview.setImageDrawable(mImageTileSet.getDrawable()); - mMagnifierView.setImageTileset(mImageTileSet); - mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f); + mCaptureCallback.onComplete(mImageTileSet); } } + + /** + * Callback for image capture completion or error. + */ + public interface ScrollCaptureCallback { + void onComplete(ImageTileSet imageTileSet); + void onError(); + } + + /** + * Callback for image export completion or error. + */ + public interface ExportCallback { + void onExportComplete(Uri outputUri); + void onError(); + } + } diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt index 9f182e19efaf..658613796498 100644 --- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt @@ -25,8 +25,8 @@ import android.content.pm.PackageManager import android.content.res.Resources import android.hardware.SensorPrivacyManager import android.hardware.SensorPrivacyManager.EXTRA_SENSOR -import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA -import android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE +import android.hardware.SensorPrivacyManager.Sensors.CAMERA +import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE import android.os.Bundle import android.os.Handler import android.text.Html @@ -81,7 +81,7 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene dismiss() } } - if (!sensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor)) { + if (!sensorPrivacyManager.isSensorPrivacyEnabled(sensor)) { finish() return } @@ -89,9 +89,9 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene mAlertParams.apply { try { mMessage = Html.fromHtml(getString(when (sensor) { - INDIVIDUAL_SENSOR_MICROPHONE -> + MICROPHONE -> R.string.sensor_privacy_start_use_mic_dialog_content - INDIVIDUAL_SENSOR_CAMERA -> + CAMERA -> R.string.sensor_privacy_start_use_camera_dialog_content else -> Resources.ID_NULL }, packageManager.getApplicationInfo(sensorUsePackageName, 0) @@ -102,9 +102,9 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene } mIconId = when (sensor) { - INDIVIDUAL_SENSOR_MICROPHONE -> + MICROPHONE -> com.android.internal.R.drawable.perm_group_microphone - INDIVIDUAL_SENSOR_CAMERA -> com.android.internal.R.drawable.perm_group_camera + CAMERA -> com.android.internal.R.drawable.perm_group_camera else -> Resources.ID_NULL } @@ -121,7 +121,7 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene override fun onStart() { super.onStart() - sensorPrivacyManager.suppressIndividualSensorPrivacyReminders(sensorUsePackageName, true) + sensorPrivacyManager.suppressSensorPrivacyReminders(sensorUsePackageName, true) unsuppressImmediately = false } @@ -156,11 +156,11 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene if (unsuppressImmediately) { sensorPrivacyManager - .suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false) + .suppressSensorPrivacyReminders(sensorUsePackageName, false) } else { Handler(mainLooper).postDelayed({ sensorPrivacyManager - .suppressIndividualSensorPrivacyReminders(sensorUsePackageName, false) + .suppressSensorPrivacyReminders(sensorUsePackageName, false) }, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS) } } @@ -170,7 +170,7 @@ class SensorUseStartedActivity : AlertActivity(), DialogInterface.OnClickListene } private fun disableSensorPrivacy() { - sensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, false) + sensorPrivacyManager.setSensorPrivacyForProfileGroup(sensor, false) unsuppressImmediately = true setResult(RESULT_OK) } diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java index 0bfc8e5d554b..fea521f15b84 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java @@ -72,8 +72,6 @@ public class BrightnessController implements ToggleSlider.Listener { private static final Uri BRIGHTNESS_FOR_VR_FLOAT_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT); - private final float mMinimumBacklight; - private final float mMaximumBacklight; private final float mDefaultBacklight; private final float mMinimumBacklightForVr; private final float mMaximumBacklightForVr; @@ -314,10 +312,6 @@ public class BrightnessController implements ToggleSlider.Listener { mDisplayId = mContext.getDisplayId(); PowerManager pm = context.getSystemService(PowerManager.class); - mMinimumBacklight = pm.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); - mMaximumBacklight = pm.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); mDefaultBacklight = mContext.getDisplay().getBrightnessDefault(); mMinimumBacklightForVr = pm.getBrightnessConstraint( PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR); @@ -375,8 +369,8 @@ public class BrightnessController implements ToggleSlider.Listener { metric = mAutomatic ? MetricsEvent.ACTION_BRIGHTNESS_AUTO : MetricsEvent.ACTION_BRIGHTNESS; - minBacklight = mMinimumBacklight; - maxBacklight = mMaximumBacklight; + minBacklight = PowerManager.BRIGHTNESS_MIN; + maxBacklight = PowerManager.BRIGHTNESS_MAX; settingToChange = Settings.System.SCREEN_BRIGHTNESS_FLOAT; } final float valFloat = MathUtils.min(convertGammaToLinearFloat(value, @@ -439,8 +433,8 @@ public class BrightnessController implements ToggleSlider.Listener { min = mMinimumBacklightForVr; max = mMaximumBacklightForVr; } else { - min = mMinimumBacklight; - max = mMaximumBacklight; + min = PowerManager.BRIGHTNESS_MIN; + max = PowerManager.BRIGHTNESS_MAX; } // convertGammaToLinearFloat returns 0-1 if (BrightnessSynchronizer.floatEquals(brightnessValue, diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java index a6aec3b7b1b7..0b40e225fb2b 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java @@ -17,8 +17,6 @@ package com.android.systemui.settings.brightness; import android.content.Context; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -29,10 +27,8 @@ import android.widget.SeekBar; import androidx.annotation.Nullable; import com.android.settingslib.RestrictedLockUtils; -import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.statusbar.policy.BrightnessMirrorController; -import com.android.systemui.util.RoundedCornerProgressDrawable; import com.android.systemui.util.ViewController; import javax.inject.Inject; @@ -274,9 +270,6 @@ public class BrightnessSlider private BrightnessSlider fromTree(ViewGroup root, boolean useMirror) { BrightnessSliderView v = root.requireViewById(R.id.brightness_slider); - // TODO(175026098) Workaround. Remove when b/175026098 is fixed - applyTheme(v); - return new BrightnessSlider(root, v, useMirror); } @@ -286,32 +279,5 @@ public class BrightnessSlider ? R.layout.quick_settings_brightness_dialog_thick : R.layout.quick_settings_brightness_dialog; } - - private LayerDrawable findProgressClippableDrawable(BrightnessSliderView v) { - SeekBar b = v.requireViewById(R.id.slider); - if (b.getProgressDrawable() instanceof LayerDrawable) { - Drawable progress = ((LayerDrawable) b.getProgressDrawable()) - .findDrawableByLayerId(com.android.internal.R.id.progress); - if (progress instanceof RoundedCornerProgressDrawable) { - Drawable inner = ((RoundedCornerProgressDrawable) progress).getDrawable(); - if (inner instanceof LayerDrawable) { - return (LayerDrawable) inner; - } - } - } - return null; - } - - private void applyTheme(BrightnessSliderView v) { - LayerDrawable layer = findProgressClippableDrawable(v); - if (layer != null) { - layer.findDrawableByLayerId(R.id.slider_foreground).setTintList( - Utils.getColorAttr(v.getContext(), - com.android.internal.R.attr.colorControlActivated)); - layer.findDrawableByLayerId(R.id.slider_icon).setTintList( - Utils.getColorAttr(v.getContext(), - com.android.internal.R.attr.colorBackground)); - } - } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index 862c27907e0f..1d59257c9c4f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -82,4 +82,12 @@ public class FeatureFlags { public boolean isMonetEnabled() { return mFlagReader.isEnabled(R.bool.flag_monet); } + + public boolean isNavigationBarOverlayEnabled() { + return mFlagReader.isEnabled(R.bool.flag_navigation_bar_overlay); + } + + public boolean isPMLiteEnabled() { + return mFlagReader.isEnabled(R.bool.flag_pm_lite); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java index f2adaf042b2f..9ed9659c7ab8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java @@ -207,7 +207,7 @@ public class GestureRecorder { sb.append(g.toJson()); count++; } - mLastSaveLen = count; + mLastSaveLen += count; sb.append("]"); return sb.toString(); } @@ -249,7 +249,9 @@ public class GestureRecorder { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { save(); if (mLastSaveLen >= 0) { - pw.println(String.valueOf(mLastSaveLen) + " gestures written to " + mLogfile); + pw.println(String.valueOf(mLastSaveLen) + + " gestures since last dump written to " + mLogfile); + mLastSaveLen = 0; } else { pw.println("error writing gestures"); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 7e2d27ac728a..a4e97a1dc6d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.view.View.GONE; import static android.view.View.VISIBLE; @@ -41,6 +42,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.UserInfo; import android.content.res.ColorStateList; +import android.content.res.Resources; import android.graphics.Color; import android.hardware.biometrics.BiometricSourceType; import android.hardware.face.FaceManager; @@ -277,10 +279,7 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal // avoid calling this method since it has an IPC if (whitelistIpcs(this::isOrganizationOwnedDevice)) { final CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName(); - final CharSequence disclosure = organizationName != null - ? mContext.getResources().getString(R.string.do_disclosure_with_name, - organizationName) - : mContext.getResources().getText(R.string.do_disclosure_generic); + final CharSequence disclosure = getDisclosureText(organizationName); mRotateTextViewController.updateIndication( INDICATION_TYPE_DISCLOSURE, new KeyguardIndication.Builder() @@ -297,6 +296,22 @@ public class KeyguardIndicationController implements KeyguardStateController.Cal } } + private CharSequence getDisclosureText(@Nullable CharSequence organizationName) { + final Resources packageResources = mContext.getResources(); + if (organizationName == null) { + return packageResources.getText(R.string.do_disclosure_generic); + } else if (mDevicePolicyManager.isDeviceManaged() + && mDevicePolicyManager.getDeviceOwnerType( + mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()) + == DEVICE_OWNER_TYPE_FINANCED) { + return packageResources.getString(R.string.do_financed_disclosure_with_name, + organizationName); + } else { + return packageResources.getString(R.string.do_disclosure_with_name, + organizationName); + } + } + private void updateOwnerInfo() { if (!isKeyguardLayoutEnabled()) { mRotateTextViewController.hideIndication(INDICATION_TYPE_OWNER_INFO); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index cfadcd7f01ea..34b29ca9721b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -133,10 +133,11 @@ public class NotificationRemoteInputManager implements Dumpable { protected Callback mCallback; protected final ArrayList<NotificationLifetimeExtender> mLifetimeExtenders = new ArrayList<>(); - private final RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() { + private final RemoteViews.InteractionHandler + mInteractionHandler = new RemoteViews.InteractionHandler() { @Override - public boolean onClickHandler( + public boolean onInteraction( View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) { mStatusBarLazy.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view, "NOTIFICATION_CLICK"); @@ -164,11 +165,11 @@ public class NotificationRemoteInputManager implements Dumpable { Notification.Action action = getActionFromView(view, entry, pendingIntent); return mCallback.handleRemoteViewClick(view, pendingIntent, action == null ? false : action.isAuthenticationRequired(), () -> { - Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view); - options.second.setLaunchWindowingMode( - WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); - mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent); - return RemoteViews.startPendingIntent(view, pendingIntent, options); + Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view); + options.second.setLaunchWindowingMode( + WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); + mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent); + return RemoteViews.startPendingIntent(view, pendingIntent, options); }); } @@ -690,8 +691,8 @@ public class NotificationRemoteInputManager implements Dumpable { * * @return on-click handler */ - public RemoteViews.OnClickHandler getRemoteViewsOnClickHandler() { - return mOnClickHandler; + public RemoteViews.InteractionHandler getRemoteViewsOnClickHandler() { + return mInteractionHandler; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 20efa32d63c6..d2ddd212bda6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -1002,10 +1002,6 @@ public class NotificationShelf extends ActivatableNotificationView implements return false; } - public void onUiModeChanged() { - updateBackgroundColors(); - } - public void setController(NotificationShelfController notificationShelfController) { mController = notificationShelfController; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java index d562726681f1..138c811e9084 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java @@ -24,7 +24,6 @@ import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; import android.content.Context; import android.content.res.ColorStateList; -import android.graphics.Color; import android.graphics.Rect; import android.util.AttributeSet; import android.util.FeatureFlagUtils; @@ -238,11 +237,7 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, @Override public void setStaticDrawableColor(int color) { ColorStateList list = ColorStateList.valueOf(color); - float intensity = color == Color.WHITE ? 0 : 1; - // We want the ability to change the theme from the one set by SignalDrawable in certain - // surfaces. In this way, we can pass a theme to the view. - mMobileDrawable.setTintList( - ColorStateList.valueOf(mDualToneHandler.getSingleColor(intensity))); + mMobileDrawable.setTintList(list); mIn.setImageTintList(list); mOut.setImageTintList(list); mMobileType.setImageTintList(list); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java index edcf6d4eddb3..4b4e51383b7e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java @@ -109,10 +109,10 @@ public class AssistantFeedbackController { && newImportance < NotificationManager.IMPORTANCE_DEFAULT) { return STATUS_SILENCED; } else if (oldImportance < newImportance - || ranking.getRankingAdjustment() == ranking.RANKING_PROMOTED) { + || ranking.getRankingAdjustment() == Ranking.RANKING_PROMOTED) { return STATUS_PROMOTED; } else if (oldImportance > newImportance - || ranking.getRankingAdjustment() == ranking.RANKING_DEMOTED) { + || ranking.getRankingAdjustment() == Ranking.RANKING_DEMOTED) { return STATUS_DEMOTED; } else { return STATUS_UNCHANGED; 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 7c3b791aed09..c8c0755344a4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -548,8 +548,6 @@ public class NotificationEntryManager implements try { mStatusBarService.onNotificationClear( notification.getPackageName(), - notification.getTag(), - notification.getId(), notification.getUser().getIdentifier(), notification.getKey(), dismissedByUserStats.dismissalSurface, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java index 73c7fd1b64a3..f21771a89c9e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java @@ -128,15 +128,6 @@ public class NotificationFilter { // this is a foreground-service disclosure for a user that does not need to show one return true; } - if (getFsc().isSystemAlertNotification(sbn)) { - final String[] apps = sbn.getNotification().extras.getStringArray( - Notification.EXTRA_FOREGROUND_APPS); - if (apps != null && apps.length >= 1) { - if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) { - return true; - } - } - } if (mIsMediaFlagEnabled && isMediaNotification(sbn)) { return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index e391250dc8fd..50cbbd5d4852 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -264,6 +264,20 @@ class NotificationWakeUpCoordinator @Inject constructor( } override fun onStateChanged(newState: Int) { + if (dozeParameters.shouldControlUnlockedScreenOff()) { + if (animatingScreenOff && + state == StatusBarState.KEYGUARD && + newState == StatusBarState.SHADE) { + // If we're animating the screen off and going from KEYGUARD back to SHADE, the + // animation was cancelled and we are unlocking. Override the doze amount to 0f (not + // dozing) so that the notifications are no longer hidden. + setDozeAmount(0f, 0f) + } + + animatingScreenOff = + state == StatusBarState.SHADE && newState == StatusBarState.KEYGUARD + } + overrideDozeAmountIfBypass() if (bypassController.bypassEnabled && newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED && @@ -273,13 +287,6 @@ class NotificationWakeUpCoordinator @Inject constructor( setNotificationsVisible(visible = false, increaseSpeed = false, animate = true) } - // If we want to control the screen off animation, check whether we are going from SHADE to - // KEYGUARD. - if (dozeParameters.shouldControlUnlockedScreenOff()) { - animatingScreenOff = - state == StatusBarState.SHADE && newState == StatusBarState.KEYGUARD - } - this.state = newState } @@ -386,8 +393,6 @@ class NotificationWakeUpCoordinator @Inject constructor( override fun onDozingChanged(isDozing: Boolean) { if (isDozing) { setNotificationsVisible(visible = false, animate = false, increaseSpeed = false) - } else { - animatingScreenOff = false } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index d617dff372da..6b96ee4fc8e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -254,8 +254,6 @@ public class NotifCollection implements Dumpable { try { mStatusBarService.onNotificationClear( entry.getSbn().getPackageName(), - entry.getSbn().getTag(), - entry.getSbn().getId(), entry.getSbn().getUser().getIdentifier(), entry.getSbn().getKey(), stats.dismissalSurface, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java index 998ae9e55313..3a87f6853bcf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java @@ -95,18 +95,6 @@ public class AppOpsCoordinator implements Coordinator { sbn.getUser().getIdentifier())) { return true; } - - // Filters out system alert notifications when unneeded - if (mForegroundServiceController.isSystemAlertNotification(sbn)) { - final String[] apps = sbn.getNotification().extras.getStringArray( - Notification.EXTRA_FOREGROUND_APPS); - if (apps != null && apps.length >= 1) { - if (!mForegroundServiceController.isSystemAlertWarningNeeded( - sbn.getUser().getIdentifier(), apps[0])) { - return true; - } - } - } return false; } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 724921b3f7c8..31d052d75998 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -191,7 +191,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView initDimens(); } - protected void updateBackgroundColors() { + /** + * Reload background colors from resources and invalidate views. + */ + public void updateBackgroundColors() { updateColors(); initBackground(); updateBackgroundTint(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 845d321416ee..1251b58171da 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -104,7 +104,7 @@ import com.android.systemui.statusbar.notification.stack.SwipeableView; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions; +import com.android.systemui.statusbar.policy.InflatedSmartReplyState; import com.android.systemui.wmshell.BubblesManager; import java.io.FileDescriptor; @@ -3196,8 +3196,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView /** * Returns the Smart Suggestions backing the smart suggestion buttons in the notification. */ - public SmartRepliesAndActions getExistingSmartRepliesAndActions() { - return mPrivateLayout.getCurrentSmartRepliesAndActions(); + public InflatedSmartReplyState getExistingSmartReplyState() { + return mPrivateLayout.getCurrentSmartReplyState(); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java index 35f35615e1c1..14683eca5080 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java @@ -16,8 +16,14 @@ package com.android.systemui.statusbar.notification.row; +import static android.service.notification.NotificationAssistantService.FEEDBACK_RATING; + +import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_ALERTED; +import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_DEMOTED; +import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_PROMOTED; +import static com.android.systemui.statusbar.notification.AssistantFeedbackController.STATUS_SILENCED; + import android.annotation.SuppressLint; -import android.app.Notification; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -36,20 +42,17 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.logging.NotificationLogger; public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsContent { private static final String TAG = "FeedbackInfo"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final String FEEDBACK_KEY = "feedback_key"; private NotificationGuts mGutsContainer; private NotificationListenerService.Ranking mRanking; @@ -146,15 +149,15 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC sb.append(String.format( "[DEBUG]: oldImportance=%d, newImportance=%d, ranking=%d\n\n", mRanking.getChannel().getImportance(), mRanking.getImportance(), - mRanking.getRankingAdjustment())); + mRanking.getRankingScore())); } - if (status == mFeedbackController.STATUS_ALERTED) { + if (status == STATUS_ALERTED) { sb.append(mContext.getText(R.string.feedback_alerted)); - } else if (status == mFeedbackController.STATUS_SILENCED) { + } else if (status == STATUS_SILENCED) { sb.append(mContext.getText(R.string.feedback_silenced)); - } else if (status == mFeedbackController.STATUS_PROMOTED) { + } else if (status == STATUS_PROMOTED) { sb.append(mContext.getText(R.string.feedback_promoted)); - } else if (status == mFeedbackController.STATUS_DEMOTED) { + } else if (status == STATUS_DEMOTED) { sb.append(mContext.getText(R.string.feedback_demoted)); } sb.append(" "); @@ -182,7 +185,8 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC private void handleFeedback(boolean positive) { Bundle feedback = new Bundle(); - feedback.putBoolean(FEEDBACK_KEY, positive); + feedback.putInt(FEEDBACK_RATING, positive ? 1 : -1); + sendFeedbackToAssistant(feedback); } @@ -191,19 +195,8 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC return; } - //TODO(b/154257994): remove this when feedback apis are in place - final int count = mNotificationEntryManager.getActiveNotificationsCount(); - final int rank = mEntry.getRanking().getRank(); - NotificationVisibility.NotificationLocation location = - NotificationLogger.getNotificationLocation(mEntry); - final NotificationVisibility nv = NotificationVisibility.obtain( - mEntry.getKey(), rank, count, true, location); - Notification.Action action = new Notification.Action.Builder(null, null, - null) - .addExtras(feedback) - .build(); try { - mStatusBarService.onNotificationActionClick(mRanking.getKey(), -1, action, nv, true); + mStatusBarService.onNotificationFeedbackReceived(mRanking.getKey(), feedback); } catch (RemoteException e) { if (DEBUG) { Log.e(TAG, "Failed to send feedback to assistant", e); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index c2c4590fa6cb..fdd8f347c248 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -48,9 +48,9 @@ import com.android.systemui.statusbar.notification.MediaNotificationProcessor; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.policy.InflatedSmartReplies; -import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions; -import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflater; +import com.android.systemui.statusbar.policy.InflatedSmartReplyState; +import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; +import com.android.systemui.statusbar.policy.SmartReplyStateInflater; import com.android.systemui.util.Assert; import java.util.HashMap; @@ -74,7 +74,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final NotifRemoteViewCache mRemoteViewCache; private final ConversationNotificationProcessor mConversationProcessor; private final Executor mBgExecutor; - private final SmartRepliesAndActionsInflater mSmartRepliesAndActionsInflater; + private final SmartReplyStateInflater mSmartReplyStateInflater; @Inject NotificationContentInflater( @@ -83,13 +83,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder ConversationNotificationProcessor conversationProcessor, MediaFeatureFlag mediaFeatureFlag, @Background Executor bgExecutor, - SmartRepliesAndActionsInflater smartRepliesInflater) { + SmartReplyStateInflater smartRepliesInflater) { mRemoteViewCache = remoteViewCache; mRemoteInputManager = remoteInputManager; mConversationProcessor = conversationProcessor; mIsMediaInQS = mediaFeatureFlag.getEnabled(); mBgExecutor = bgExecutor; - mSmartRepliesAndActionsInflater = smartRepliesInflater; + mSmartReplyStateInflater = smartRepliesInflater; } @Override @@ -133,7 +133,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder callback, mRemoteInputManager.getRemoteViewsOnClickHandler(), mIsMediaInQS, - mSmartRepliesAndActionsInflater); + mSmartReplyStateInflater); if (mInflateSynchronously) { task.onPostExecute(task.doInBackground()); } else { @@ -150,7 +150,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder @InflationFlag int reInflateFlags, Notification.Builder builder, Context packageContext, - SmartRepliesAndActionsInflater smartRepliesInflater) { + SmartReplyStateInflater smartRepliesInflater) { InflationProgress result = createRemoteViews(reInflateFlags, builder, bindParams.isLowPriority, @@ -160,7 +160,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder result = inflateSmartReplyViews(result, reInflateFlags, entry, row.getContext(), packageContext, - row.getExistingSmartRepliesAndActions(), + row.getExistingSmartReplyState(), smartRepliesInflater); apply( @@ -268,15 +268,26 @@ public class NotificationContentInflater implements NotificationRowContentBinder NotificationEntry entry, Context context, Context packageContext, - SmartRepliesAndActions previousSmartRepliesAndActions, - SmartRepliesAndActionsInflater inflater) { - if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) { - result.expandedInflatedSmartReplies = inflater.inflateSmartReplies( - context, packageContext, entry, previousSmartRepliesAndActions); - } - if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0 && result.newHeadsUpView != null) { - result.headsUpInflatedSmartReplies = inflater.inflateSmartReplies( - context, packageContext, entry, previousSmartRepliesAndActions); + InflatedSmartReplyState previousSmartReplyState, + SmartReplyStateInflater inflater) { + boolean inflateContracted = (reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0 + && result.newContentView != null; + boolean inflateExpanded = (reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 + && result.newExpandedView != null; + boolean inflateHeadsUp = (reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0 + && result.newHeadsUpView != null; + if (inflateContracted || inflateExpanded || inflateHeadsUp) { + result.inflatedSmartReplyState = inflater.inflateSmartReplyState(entry); + } + if (inflateExpanded) { + result.expandedInflatedSmartReplies = inflater.inflateSmartReplyViewHolder( + context, packageContext, entry, previousSmartReplyState, + result.inflatedSmartReplyState); + } + if (inflateHeadsUp) { + result.headsUpInflatedSmartReplies = inflater.inflateSmartReplyViewHolder( + context, packageContext, entry, previousSmartReplyState, + result.inflatedSmartReplyState); } return result; } @@ -317,7 +328,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder NotifRemoteViewCache remoteViewCache, NotificationEntry entry, ExpandableNotificationRow row, - RemoteViews.OnClickHandler remoteViewClickHandler, + RemoteViews.InteractionHandler remoteViewClickHandler, @Nullable InflationCallback callback) { NotificationContentView privateLayout = row.getPrivateLayout(); NotificationContentView publicLayout = row.getPublicLayout(); @@ -442,7 +453,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder final NotificationEntry entry, final ExpandableNotificationRow row, boolean isNewView, - RemoteViews.OnClickHandler remoteViewClickHandler, + RemoteViews.InteractionHandler remoteViewClickHandler, @Nullable final InflationCallback callback, NotificationContentView parentLayout, View existingView, @@ -566,6 +577,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder NotificationContentView privateLayout = row.getPrivateLayout(); NotificationContentView publicLayout = row.getPublicLayout(); if (runningInflations.isEmpty()) { + boolean setRepliesAndActions = true; if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) { if (result.inflatedContentView != null) { // New view case @@ -578,6 +590,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder remoteViewCache.putCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView); } + setRepliesAndActions = true; } if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) { @@ -599,6 +612,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder privateLayout.setExpandedInflatedSmartReplies(null); } row.setExpandable(result.newExpandedView != null); + setRepliesAndActions = true; } if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) { @@ -619,6 +633,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder } else { privateLayout.setHeadsUpInflatedSmartReplies(null); } + setRepliesAndActions = true; + } + if (setRepliesAndActions) { + privateLayout.setInflatedSmartReplyState(result.inflatedSmartReplyState); } if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) { @@ -705,11 +723,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final Executor mBgExecutor; private ExpandableNotificationRow mRow; private Exception mError; - private RemoteViews.OnClickHandler mRemoteViewClickHandler; + private RemoteViews.InteractionHandler mRemoteViewClickHandler; private CancellationSignal mCancellationSignal; private final ConversationNotificationProcessor mConversationProcessor; private final boolean mIsMediaInQS; - private final SmartRepliesAndActionsInflater mSmartRepliesInflater; + private final SmartReplyStateInflater mSmartRepliesInflater; private AsyncInflationTask( Executor bgExecutor, @@ -723,9 +741,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, InflationCallback callback, - RemoteViews.OnClickHandler remoteViewClickHandler, + RemoteViews.InteractionHandler remoteViewClickHandler, boolean isMediaFlagEnabled, - SmartRepliesAndActionsInflater smartRepliesInflater) { + SmartReplyStateInflater smartRepliesInflater) { mEntry = entry; mRow = row; mBgExecutor = bgExecutor; @@ -776,15 +794,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder InflationProgress inflationProgress = createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, packageContext); - SmartRepliesAndActions repliesAndActions = - mRow.getExistingSmartRepliesAndActions(); + InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState(); return inflateSmartReplyViews( inflationProgress, mReInflateFlags, mEntry, mContext, packageContext, - repliesAndActions, + previousSmartReplyState, mSmartRepliesInflater); } catch (Exception e) { mError = e; @@ -879,8 +896,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder private CharSequence headsUpStatusBarText; private CharSequence headsUpStatusBarTextPublic; - private InflatedSmartReplies expandedInflatedSmartReplies; - private InflatedSmartReplies headsUpInflatedSmartReplies; + private InflatedSmartReplyState inflatedSmartReplyState; + private InflatedSmartReplyViewHolder expandedInflatedSmartReplies; + private InflatedSmartReplyViewHolder headsUpInflatedSmartReplies; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index d2774df330da..76917612b910 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -17,15 +17,11 @@ package com.android.systemui.statusbar.notification.row; -import static android.provider.Settings.Global.NOTIFICATION_BUBBLES; -import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE; - import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; -import android.content.res.Resources; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; @@ -58,16 +54,18 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; -import com.android.systemui.statusbar.policy.InflatedSmartReplies; -import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions; +import com.android.systemui.statusbar.policy.InflatedSmartReplyState; +import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; import com.android.systemui.statusbar.policy.RemoteInputView; -import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflaterKt; import com.android.systemui.statusbar.policy.SmartReplyConstants; +import com.android.systemui.statusbar.policy.SmartReplyStateInflaterKt; import com.android.systemui.statusbar.policy.SmartReplyView; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * A frame layout containing the actual payload of the notification, including the contracted, @@ -106,9 +104,9 @@ public class NotificationContentView extends FrameLayout { private SmartReplyView mExpandedSmartReplyView; private SmartReplyView mHeadsUpSmartReplyView; private SmartReplyController mSmartReplyController; - private InflatedSmartReplies mExpandedInflatedSmartReplies; - private InflatedSmartReplies mHeadsUpInflatedSmartReplies; - private SmartRepliesAndActions mCurrentSmartRepliesAndActions; + private InflatedSmartReplyViewHolder mExpandedInflatedSmartReplies; + private InflatedSmartReplyViewHolder mHeadsUpInflatedSmartReplies; + private InflatedSmartReplyState mCurrentSmartReplyState; private NotificationViewWrapper mContractedWrapper; private NotificationViewWrapper mExpandedWrapper; @@ -1189,29 +1187,19 @@ public class NotificationContentView extends FrameLayout { applyRemoteInput(entry, hasFreeformRemoteInput(entry)); - if (mExpandedInflatedSmartReplies == null && mHeadsUpInflatedSmartReplies == null) { + if (mCurrentSmartReplyState == null) { if (DEBUG) { - Log.d(TAG, "Both expanded, and heads-up InflatedSmartReplies are null, " - + "don't add smart replies."); + Log.d(TAG, "InflatedSmartReplies are null, don't add smart replies."); } return; } - // The inflated smart-reply objects for the expanded view and the heads-up view both contain - // the same SmartRepliesAndActions to avoid discrepancies between the two views. We here - // reuse that object for our local SmartRepliesAndActions to avoid discrepancies between - // this class and the InflatedSmartReplies classes. - mCurrentSmartRepliesAndActions = mExpandedInflatedSmartReplies != null - ? mExpandedInflatedSmartReplies.getSmartRepliesAndActions() - : mHeadsUpInflatedSmartReplies.getSmartRepliesAndActions(); if (DEBUG) { Log.d(TAG, String.format("Adding suggestions for %s, %d actions, and %d replies.", entry.getSbn().getKey(), - mCurrentSmartRepliesAndActions.smartActions == null ? 0 : - mCurrentSmartRepliesAndActions.smartActions.actions.size(), - mCurrentSmartRepliesAndActions.smartReplies == null ? 0 : - mCurrentSmartRepliesAndActions.smartReplies.choices.size())); + mCurrentSmartReplyState.getSmartActionsList().size(), + mCurrentSmartReplyState.getSmartRepliesList().size())); } - applySmartReplyView(mCurrentSmartRepliesAndActions, entry); + applySmartReplyView(mCurrentSmartReplyState, entry); } private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) { @@ -1325,7 +1313,7 @@ public class NotificationContentView extends FrameLayout { private boolean isBubblesEnabled() { return Settings.Global.getInt(mContext.getContentResolver(), - NOTIFICATION_BUBBLES, 0) == 1; + Settings.Global.NOTIFICATION_BUBBLES, 0) == 1; } /** @@ -1381,27 +1369,26 @@ public class NotificationContentView extends FrameLayout { } ImageView snoozeButton = layout.findViewById(com.android.internal.R.id.snooze_button); View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container); - LinearLayout actionContainerLayout = - layout.findViewById(com.android.internal.R.id.actions_container_layout); - if (snoozeButton == null || actionContainer == null || actionContainerLayout == null) { + if (snoozeButton == null || actionContainer == null) { return; } final boolean showSnooze = Settings.Secure.getInt(mContext.getContentResolver(), - SHOW_NOTIFICATION_SNOOZE, 0) == 1; - if (!showSnooze) { + Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1; + // Notification.Builder can 'disable' the snooze button to prevent it from being shown here + boolean snoozeDisabled = !snoozeButton.isEnabled(); + if (!showSnooze || snoozeDisabled) { snoozeButton.setVisibility(GONE); return; } - Resources res = mContext.getResources(); - Drawable snoozeDrawable = res.getDrawable(R.drawable.ic_snooze); + Drawable snoozeDrawable = mContext.getDrawable(R.drawable.ic_snooze); mContainingNotification.updateNotificationColor(); snoozeDrawable.setTint(mContainingNotification.getNotificationColor()); snoozeButton.setImageDrawable(snoozeDrawable); final NotificationSnooze snoozeGuts = (NotificationSnooze) LayoutInflater.from(mContext) .inflate(R.layout.notification_snooze, null, false); - final String snoozeDescription = res.getString( + final String snoozeDescription = mContext.getString( R.string.notification_menu_snooze_description); final NotificationMenuRowPlugin.MenuItem snoozeMenuItem = new NotificationMenuRow.NotificationMenuItem( @@ -1415,41 +1402,74 @@ public class NotificationContentView extends FrameLayout { } private void applySmartReplyView( - SmartRepliesAndActions smartRepliesAndActions, + InflatedSmartReplyState state, NotificationEntry entry) { + if (mContractedChild != null) { + applyExternalSmartReplyState(mContractedChild, state); + } if (mExpandedChild != null) { - mExpandedSmartReplyView = applySmartReplyView(mExpandedChild, smartRepliesAndActions, + applyExternalSmartReplyState(mExpandedChild, state); + mExpandedSmartReplyView = applySmartReplyView(mExpandedChild, state, entry, mExpandedInflatedSmartReplies); if (mExpandedSmartReplyView != null) { - if (smartRepliesAndActions.smartReplies != null - || smartRepliesAndActions.smartActions != null) { - int numSmartReplies = smartRepliesAndActions.smartReplies == null - ? 0 : smartRepliesAndActions.smartReplies.choices.size(); - int numSmartActions = smartRepliesAndActions.smartActions == null - ? 0 : smartRepliesAndActions.smartActions.actions.size(); - boolean fromAssistant = smartRepliesAndActions.smartReplies == null - ? smartRepliesAndActions.smartActions.fromAssistant - : smartRepliesAndActions.smartReplies.fromAssistant; - boolean editBeforeSending = smartRepliesAndActions.smartReplies != null + SmartReplyView.SmartReplies smartReplies = state.getSmartReplies(); + SmartReplyView.SmartActions smartActions = state.getSmartActions(); + if (smartReplies != null || smartActions != null) { + int numSmartReplies = smartReplies == null ? 0 : smartReplies.choices.size(); + int numSmartActions = smartActions == null ? 0 : smartActions.actions.size(); + boolean fromAssistant = smartReplies == null + ? smartActions.fromAssistant + : smartReplies.fromAssistant; + boolean editBeforeSending = smartReplies != null && mSmartReplyConstants.getEffectiveEditChoicesBeforeSending( - smartRepliesAndActions.smartReplies.remoteInput - .getEditChoicesBeforeSending()); + smartReplies.remoteInput.getEditChoicesBeforeSending()); mSmartReplyController.smartSuggestionsAdded(entry, numSmartReplies, numSmartActions, fromAssistant, editBeforeSending); } } } - if (mHeadsUpChild != null && mSmartReplyConstants.getShowInHeadsUp()) { - mHeadsUpSmartReplyView = applySmartReplyView(mHeadsUpChild, smartRepliesAndActions, - entry, mHeadsUpInflatedSmartReplies); + if (mHeadsUpChild != null) { + applyExternalSmartReplyState(mHeadsUpChild, state); + if (mSmartReplyConstants.getShowInHeadsUp()) { + mHeadsUpSmartReplyView = applySmartReplyView(mHeadsUpChild, state, + entry, mHeadsUpInflatedSmartReplies); + } + } + } + + private void applyExternalSmartReplyState(View view, InflatedSmartReplyState state) { + boolean hasPhishingAlert = state != null && state.getHasPhishingAction(); + View phishingAlertIcon = view.findViewById(com.android.internal.R.id.phishing_alert); + if (phishingAlertIcon != null) { + if (DEBUG) { + Log.d(TAG, "Setting 'phishing_alert' view visible=" + hasPhishingAlert + "."); + } + phishingAlertIcon.setVisibility(hasPhishingAlert ? View.VISIBLE : View.GONE); + } + List<Integer> suppressedActionIndices = state != null + ? state.getSuppressedActionIndices() + : Collections.emptyList(); + ViewGroup actionsList = view.findViewById(com.android.internal.R.id.actions); + if (actionsList != null) { + if (DEBUG && !suppressedActionIndices.isEmpty()) { + Log.d(TAG, "Suppressing actions with indices: " + suppressedActionIndices); + } + for (int i = 0; i < actionsList.getChildCount(); i++) { + View actionBtn = actionsList.getChildAt(i); + Object actionIndex = + actionBtn.getTag(com.android.internal.R.id.notification_action_index_tag); + boolean suppressAction = actionIndex instanceof Integer + && suppressedActionIndices.contains(actionIndex); + actionBtn.setVisibility(suppressAction ? View.GONE : View.VISIBLE); + } } } @Nullable private SmartReplyView applySmartReplyView(View view, - SmartRepliesAndActions smartRepliesAndActions, - NotificationEntry entry, InflatedSmartReplies inflatedSmartReplyView) { + InflatedSmartReplyState smartReplyState, + NotificationEntry entry, InflatedSmartReplyViewHolder inflatedSmartReplyViewHolder) { View smartReplyContainerCandidate = view.findViewById( com.android.internal.R.id.smart_reply_container); if (!(smartReplyContainerCandidate instanceof LinearLayout)) { @@ -1457,8 +1477,7 @@ public class NotificationContentView extends FrameLayout { } LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate; - if (!SmartRepliesAndActionsInflaterKt - .shouldShowSmartReplyView(entry, smartRepliesAndActions)) { + if (!SmartReplyStateInflaterKt.shouldShowSmartReplyView(entry, smartReplyState)) { smartReplyContainer.setVisibility(View.GONE); return null; } @@ -1471,15 +1490,15 @@ public class NotificationContentView extends FrameLayout { smartReplyContainer.removeAllViews(); } if (smartReplyContainer.getChildCount() == 0 - && inflatedSmartReplyView != null - && inflatedSmartReplyView.getSmartReplyView() != null) { - smartReplyView = inflatedSmartReplyView.getSmartReplyView(); + && inflatedSmartReplyViewHolder != null + && inflatedSmartReplyViewHolder.getSmartReplyView() != null) { + smartReplyView = inflatedSmartReplyViewHolder.getSmartReplyView(); smartReplyContainer.addView(smartReplyView); } if (smartReplyView != null) { smartReplyView.resetSmartSuggestions(smartReplyContainer); smartReplyView.addPreInflatedButtons( - inflatedSmartReplyView.getSmartSuggestionButtons()); + inflatedSmartReplyViewHolder.getSmartSuggestionButtons()); // Ensure the colors of the smart suggestion buttons are up-to-date. smartReplyView.setBackgroundTintColor(entry.getRow().getCurrentBackgroundTint()); smartReplyContainer.setVisibility(View.VISIBLE); @@ -1495,7 +1514,7 @@ public class NotificationContentView extends FrameLayout { * {@link SmartReplyView} related to the expanded notification state is cleared. */ public void setExpandedInflatedSmartReplies( - @Nullable InflatedSmartReplies inflatedSmartReplies) { + @Nullable InflatedSmartReplyViewHolder inflatedSmartReplies) { mExpandedInflatedSmartReplies = inflatedSmartReplies; if (inflatedSmartReplies == null) { mExpandedSmartReplyView = null; @@ -1510,7 +1529,7 @@ public class NotificationContentView extends FrameLayout { * {@link SmartReplyView} related to the heads-up notification state is cleared. */ public void setHeadsUpInflatedSmartReplies( - @Nullable InflatedSmartReplies inflatedSmartReplies) { + @Nullable InflatedSmartReplyViewHolder inflatedSmartReplies) { mHeadsUpInflatedSmartReplies = inflatedSmartReplies; if (inflatedSmartReplies == null) { mHeadsUpSmartReplyView = null; @@ -1518,10 +1537,21 @@ public class NotificationContentView extends FrameLayout { } /** + * Set pre-inflated replies and actions for the notification. + * This can be relevant to any state of the notification, even contracted, because smart actions + * may cause a phishing alert to be made visible. + * @param smartReplyState the pre-inflated list of replies and actions + */ + public void setInflatedSmartReplyState( + @NonNull InflatedSmartReplyState smartReplyState) { + mCurrentSmartReplyState = smartReplyState; + } + + /** * Returns the smart replies and actions currently shown in the notification. */ - @Nullable public SmartRepliesAndActions getCurrentSmartRepliesAndActions() { - return mCurrentSmartRepliesAndActions; + @Nullable public InflatedSmartReplyState getCurrentSmartReplyState() { + return mCurrentSmartReplyState; } public void closeRemoteInput() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java index 1d36ad3f13d5..7248bcef621c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapper.java @@ -40,8 +40,6 @@ public class NotificationBigPictureTemplateViewWrapper extends NotificationTempl public void onContentUpdated(ExpandableNotificationRow row) { super.onContentUpdated(row); updateImageTag(row.getEntry().getSbn()); - // Round the corners of the big picture content - mView.findViewById(com.android.internal.R.id.big_picture).setClipToOutline(true); } private void updateImageTag(StatusBarNotification notification) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java index 414d62092ab2..222735aeb35a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java @@ -49,7 +49,7 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper { // Custom views will most likely use just white or black as their text color. // We need to scan through and replace these colors by Material NEXT colors. - ensureThemeOnChildren(); + ensureThemeOnChildren(mView); // Let's invert the notification colors when we're in night mode and // the notification background isn't colorized. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java index 301c3726793a..d21ae13a1e01 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java @@ -64,7 +64,7 @@ public class NotificationDecoratedCustomViewWrapper extends NotificationTemplate // Custom views will most likely use just white or black as their text color. // We need to scan through and replace these colors by Material NEXT colors. - ensureThemeOnChildren(); + ensureThemeOnChildren(mWrappedView); if (needsInversion(resolveBackgroundColor(), mWrappedView)) { invertViewLuminosity(mWrappedView); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 34bc5370e8f1..eb79e3c8a69a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -67,8 +67,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private ImageView mWorkProfileImage; private View mAudiblyAlertedIcon; private View mFeedbackIcon; - private View mLeftIcon; - private View mRightIcon; private boolean mIsLowPriority; private boolean mTransformLowPriorityTitle; @@ -113,8 +111,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); mAltExpandTarget = mView.findViewById(com.android.internal.R.id.alternate_expand_target); mIconContainer = mView.findViewById(com.android.internal.R.id.conversation_icon_container); - mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon); - mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); mNotificationTopLine = mView.findViewById(com.android.internal.R.id.notification_top_line); @@ -160,12 +156,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { updateCropToPaddingForImageViews(); Notification notification = row.getEntry().getSbn().getNotification(); mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon()); - if (mLeftIcon != null) { - mLeftIcon.setClipToOutline(true); - } - if (mRightIcon != null) { - mRightIcon.setClipToOutline(true); - } // We need to reset all views that are no longer transforming in case a view was previously // transformed, but now we decided to transform its container instead. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 5fff8c83048f..89babf0835c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -36,12 +36,12 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.settingslib.Utils; +import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.TransformState; @@ -58,9 +58,11 @@ public abstract class NotificationViewWrapper implements TransformableView { private final Rect mTmpRect = new Rect(); protected int mBackgroundColor = 0; - private int mLightTextColor; - private int mDarkTextColor; - private int mDefaultTextColor; + private int mMaterialTextColorPrimary; + private int mMaterialTextColorSecondary; + private int mThemedTextColorPrimary; + private int mThemedTextColorSecondary; + private boolean mAdjustTheme; public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) { if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) { @@ -97,6 +99,8 @@ public abstract class NotificationViewWrapper implements TransformableView { mView = view; mRow = row; onReinflated(); + mAdjustTheme = ctx.getResources().getBoolean( + R.bool.config_adjustThemeOnNotificationCustomViews); } /** @@ -121,15 +125,22 @@ public abstract class NotificationViewWrapper implements TransformableView { mBackgroundColor = backgroundColor; mView.setBackground(new ColorDrawable(Color.TRANSPARENT)); } - mLightTextColor = mView.getContext().getColor( - com.android.internal.R.color.notification_primary_text_color_light); - mDarkTextColor = mView.getContext().getColor( - R.color.notification_primary_text_color_dark); + + Context materialTitleContext = new ContextThemeWrapper(mView.getContext(), + com.android.internal.R.style.TextAppearance_Material_Notification_Title); + mMaterialTextColorPrimary = Utils.getColorAttr(materialTitleContext, + com.android.internal.R.attr.textColor).getDefaultColor(); + Context materialContext = new ContextThemeWrapper(mView.getContext(), + com.android.internal.R.style.TextAppearance_Material_Notification); + mMaterialTextColorSecondary = Utils.getColorAttr(materialContext, + com.android.internal.R.attr.textColor).getDefaultColor(); Context themedContext = new ContextThemeWrapper(mView.getContext(), - R.style.Theme_DeviceDefault_DayNight); - mDefaultTextColor = Utils.getColorAttr(themedContext, R.attr.textColorPrimary) - .getDefaultColor(); + com.android.internal.R.style.Theme_DeviceDefault_DayNight); + mThemedTextColorPrimary = Utils.getColorAttr(themedContext, + com.android.internal.R.attr.textColorPrimary).getDefaultColor(); + mThemedTextColorSecondary = Utils.getColorAttr(themedContext, + com.android.internal.R.attr.textColorSecondary).getDefaultColor(); } protected boolean needsInversion(int defaultBackgroundColor, View view) { @@ -207,38 +218,35 @@ public abstract class NotificationViewWrapper implements TransformableView { return false; } - protected void ensureThemeOnChildren() { - if (mView == null) { + protected void ensureThemeOnChildren(View rootView) { + if (!mAdjustTheme || mView == null || rootView == null) { return; } // Notifications with custom backgrounds should not be adjusted if (mBackgroundColor != Color.TRANSPARENT - || getBackgroundColor(mView) != Color.TRANSPARENT) { + || getBackgroundColor(mView) != Color.TRANSPARENT + || getBackgroundColor(rootView) != Color.TRANSPARENT) { return; } // Now let's check if there's unprotected text somewhere, and apply the theme if we find it. - if (!(mView instanceof ViewGroup)) { - return; - } - processChildrenTextColor((ViewGroup) mView); + processTextColorRecursive(rootView); } - private void processChildrenTextColor(ViewGroup viewGroup) { - if (viewGroup == null) { - return; - } - - for (int i = 0; i < viewGroup.getChildCount(); i++) { - View child = viewGroup.getChildAt(i); - if (child instanceof TextView) { - int foreground = ((TextView) child).getCurrentTextColor(); - if (foreground == mLightTextColor || foreground == mDarkTextColor) { - ((TextView) child).setTextColor(mDefaultTextColor); - } - } else if (child instanceof ViewGroup) { - processChildrenTextColor((ViewGroup) child); + private void processTextColorRecursive(View view) { + if (view instanceof TextView) { + TextView textView = (TextView) view; + int foreground = textView.getCurrentTextColor(); + if (foreground == mMaterialTextColorPrimary) { + textView.setTextColor(mThemedTextColorPrimary); + } else if (foreground == mMaterialTextColorSecondary) { + textView.setTextColor(mThemedTextColorSecondary); + } + } else if (view instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) view; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + processTextColorRecursive(viewGroup.getChildAt(i)); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 2c7c5cc91120..417ff5e9aed0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -21,6 +21,7 @@ import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT; import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; +import static com.android.systemui.util.Utils.shouldUseSplitNotificationShade; import static java.lang.annotation.RetentionPolicy.SOURCE; @@ -83,6 +84,7 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper.DragDownCallback; import com.android.systemui.statusbar.EmptyShadeView; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelfController; @@ -453,6 +455,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private NotificationEntry mTopHeadsUpEntry; private long mNumHeadsUp; private NotificationStackScrollLayoutController.TouchHandler mTouchHandler; + private final FeatureFlags mFeatureFlags; private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener = new ExpandableView.OnHeightChangedListener() { @@ -492,8 +495,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable GroupMembershipManager groupMembershipManager, GroupExpansionManager groupExpansionManager, SysuiStatusBarStateController statusbarStateController, - AmbientState ambientState - ) { + AmbientState ambientState, + FeatureFlags featureFlags) { super(context, attrs, 0, 0); Resources res = getResources(); mSectionsManager = notificationSectionsManager; @@ -530,6 +533,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mGroupMembershipManager = groupMembershipManager; mGroupExpansionManager = groupExpansionManager; mStatusbarStateController = statusbarStateController; + mFeatureFlags = featureFlags; } void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) { @@ -622,7 +626,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating) .getDefaultColor(); updateBackgroundDimming(); - mShelf.onUiModeChanged(); + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof ActivatableNotificationView) { + ((ActivatableNotificationView) child).updateBackgroundColors(); + } + } } @ShadeViewRefactor(RefactorComponent.DECORATOR) @@ -1156,8 +1165,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (stackStartPosition <= stackEndPosition) { stackHeight = stackEndPosition; } else { - stackHeight = (int) NotificationUtils.interpolate(stackStartPosition, - stackEndPosition, mQsExpansionFraction); + if (shouldUseSplitNotificationShade(mFeatureFlags, getResources())) { + // This prevents notifications from being collapsed when QS is expanded. + stackHeight = (int) height; + } else { + stackHeight = (int) NotificationUtils.interpolate(stackStartPosition, + stackEndPosition, mQsExpansionFraction); + } } } else { stackHeight = (int) height; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index a51674280c1c..399702869d70 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -223,6 +223,7 @@ public class NotificationStackScrollLayoutController { updateShowEmptyShadeView(); mView.updateCornerRadius(); mView.updateBgColor(); + mView.updateDecorViews(); mView.reinflateViews(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 8cdaa63994e4..f4830fbb0028 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -134,7 +134,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mStackScrollerController = stackScrollerController; mNotificationPanelViewController = notificationPanelViewController; notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp); - notificationPanelViewController.addVerticalTranslationListener(mUpdatePanelTranslation); + notificationPanelViewController.setVerticalTranslationListener(mUpdatePanelTranslation); notificationPanelViewController.setHeadsUpAppearanceController(this); mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight); mStackScrollerController.addOnLayoutChangeListener(mStackScrollLayoutChangeListener); @@ -171,7 +171,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null); mWakeUpCoordinator.removeListener(this); mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp); - mNotificationPanelViewController.removeVerticalTranslationListener(mUpdatePanelTranslation); + mNotificationPanelViewController.setVerticalTranslationListener(null); mNotificationPanelViewController.setHeadsUpAppearanceController(null); mStackScrollerController.removeOnExpandedHeightChangedListener(mSetExpandedHeight); mStackScrollerController.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index a6daed5a0850..d6380199e844 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -89,7 +89,8 @@ public class KeyguardClockPositionAlgorithm { private int mNotificationStackHeight; /** - * Minimum top margin to avoid overlap with status bar. + * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher + * avatar. */ private int mMinTopMargin; @@ -186,15 +187,15 @@ public class KeyguardClockPositionAlgorithm { /** * Sets up algorithm values. */ - public void setup(int statusBarMinHeight, int maxShadeBottom, int notificationStackHeight, - float panelExpansion, int parentHeight, int keyguardStatusHeight, - int userSwitchHeight, int clockPreferredY, int userSwitchPreferredY, - boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount, - boolean bypassEnabled, int unlockedStackScrollerPadding, boolean showLockIcon, - float qsExpansion, int cutoutTopInset) { - mMinTopMargin = statusBarMinHeight + (showLockIcon - ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon) - + userSwitchHeight; + public void setup(int keyguardStatusBarHeaderHeight, int maxShadeBottom, + int notificationStackHeight, float panelExpansion, int parentHeight, + int keyguardStatusHeight, int userSwitchHeight, int clockPreferredY, + int userSwitchPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark, + float emptyDragAmount, boolean bypassEnabled, int unlockedStackScrollerPadding, + boolean showLockIcon, float qsExpansion, int cutoutTopInset) { + mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(showLockIcon + ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon, + userSwitchHeight); mMaxShadeBottom = maxShadeBottom; mNotificationStackHeight = notificationStackHeight; mPanelExpansion = panelExpansion; @@ -344,8 +345,11 @@ public class KeyguardClockPositionAlgorithm { } private float burnInPreventionOffsetX() { - return getBurnInOffset(mBurnInPreventionOffsetX * 2, true /* xAxis */) - - mBurnInPreventionOffsetX; + if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) { + return getBurnInOffset(mBurnInPreventionOffsetX * 2, true /* xAxis */) + - mBurnInPreventionOffsetX; + } + return getBurnInOffset(mBurnInPreventionOffsetX, true /* xAxis */); } public static class Result { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java deleted file mode 100644 index 377fb92ac6ba..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.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.systemui.statusbar.phone; - -import com.android.keyguard.CarrierTextController; -import com.android.systemui.util.ViewController; - -import javax.inject.Inject; - -/** View Controller for {@link com.android.systemui.statusbar.phone.KeyguardStatusBarView}. */ -public class KeyguardStatusBarViewController extends ViewController<KeyguardStatusBarView> { - private final CarrierTextController mCarrierTextController; - - @Inject - public KeyguardStatusBarViewController( - KeyguardStatusBarView view, CarrierTextController carrierTextController) { - super(view); - mCarrierTextController = carrierTextController; - } - - @Override - protected void onInit() { - super.onInit(); - mCarrierTextController.init(); - } - - @Override - protected void onViewAttached() { - } - - @Override - protected void onViewDetached() { - } -} 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 2f9fa9e6ec41..83c347b05012 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -83,7 +83,6 @@ import com.android.keyguard.KeyguardStatusViewController; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; -import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.systemui.DejankUtils; @@ -197,7 +196,8 @@ public class NotificationPanelViewController extends PanelViewController { new MyOnHeadsUpChangedListener(); private final HeightListener mHeightListener = new HeightListener(); private final ConfigurationListener mConfigurationListener = new ConfigurationListener(); - private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener(); + @VisibleForTesting final StatusBarStateListener mStatusBarStateListener = + new StatusBarStateListener(); private final ExpansionCallback mExpansionCallback = new ExpansionCallback(); private final BiometricUnlockController mBiometricUnlockController; private final NotificationPanelView mView; @@ -281,37 +281,12 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onKeyguardVisibilityChanged(boolean showing) { - if (mDisabledUdfpsController == null - && mAuthController.getUdfpsRegion() != null - && mAuthController.isUdfpsEnrolled( - KeyguardUpdateMonitor.getCurrentUser())) { - mLayoutInflater.inflate(R.layout.disabled_udfps_view, mView); - mDisabledUdfpsController = new DisabledUdfpsController( - mView.findViewById(R.id.disabled_udfps_view), - mStatusBarStateController, - mUpdateMonitor, - mAuthController, - mStatusBarKeyguardViewManager); - mDisabledUdfpsController.init(); + if (showing) { + updateDisabledUdfpsController(); } } }; - final KeyguardUserSwitcherController.KeyguardUserSwitcherListener - mKeyguardUserSwitcherListener = - new KeyguardUserSwitcherController.KeyguardUserSwitcherListener() { - @Override - public void onKeyguardUserSwitcherChanged(boolean open) { - if (mKeyguardUserSwitcherController == null) { - updateUserSwitcherVisibility(false); - } else if (!mKeyguardUserSwitcherController.isSimpleUserSwitcher()) { - updateUserSwitcherVisibility(open - && mKeyguardStateController.isShowing() - && !mKeyguardStateController.isKeyguardFadingAway()); - } - } - }; - private final LayoutInflater mLayoutInflater; private final PowerManager mPowerManager; private final AccessibilityManager mAccessibilityManager; @@ -326,7 +301,6 @@ public class NotificationPanelViewController extends PanelViewController { private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; private final KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory; private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory; - private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; private final QSDetailDisplayer mQSDetailDisplayer; private final FeatureFlags mFeatureFlags; private final ScrimController mScrimController; @@ -340,10 +314,8 @@ public class NotificationPanelViewController extends PanelViewController { private KeyguardAffordanceHelper mAffordanceHelper; private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController; - private boolean mKeyguardUserSwitcherIsShowing; private KeyguardUserSwitcherController mKeyguardUserSwitcherController; private KeyguardStatusBarView mKeyguardStatusBar; - private KeyguardStatusBarViewController mKeyguarStatusBarViewController; private ViewGroup mBigClockContainer; private QS mQs; private FrameLayout mQsFrame; @@ -386,6 +358,7 @@ public class NotificationPanelViewController extends PanelViewController { private ValueAnimator mQsExpansionAnimator; private FlingAnimationUtils mFlingAnimationUtils; private int mStatusBarMinHeight; + private int mStatusBarHeaderHeightKeyguard; private int mNotificationsHeaderCollideDistance; private float mEmptyDragAmount; private float mDownX; @@ -476,7 +449,7 @@ public class NotificationPanelViewController extends PanelViewController { private ArrayList<Consumer<ExpandableNotificationRow>> mTrackingHeadsUpListeners = new ArrayList<>(); - private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>(); + private Runnable mVerticalTranslationListener; private HeadsUpAppearanceController mHeadsUpAppearanceController; private int mPanelAlpha; @@ -597,7 +570,6 @@ public class NotificationPanelViewController extends PanelViewController { KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory, KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory, - KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory, QSDetailDisplayer qsDetailDisplayer, NotificationGroupManagerLegacy groupManager, NotificationIconAreaController notificationIconAreaController, @@ -624,7 +596,6 @@ public class NotificationPanelViewController extends PanelViewController { mGroupManager = groupManager; mNotificationIconAreaController = notificationIconAreaController; mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; - mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory; mFeatureFlags = featureFlags; mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory; mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory; @@ -634,6 +605,7 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardQsUserSwitchEnabled = mKeyguardUserSwitcherEnabled && mResources.getBoolean( R.bool.config_keyguard_user_switch_opens_qs_details); + keyguardUpdateMonitor.setKeyguardQsUserSwitchEnabled(mKeyguardQsUserSwitchEnabled); mView.setWillNotDraw(!DEBUG); mLayoutInflater = layoutInflater; mFalsingManager = falsingManager; @@ -728,9 +700,7 @@ public class NotificationPanelViewController extends PanelViewController { } updateViewControllers(mView.findViewById(R.id.keyguard_status_view), - userAvatarView, - mKeyguardStatusBar, - keyguardUserSwitcherView); + userAvatarView, keyguardUserSwitcherView); mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); NotificationStackScrollLayout stackScrollLayout = mView.findViewById( R.id.notification_stack_scroller); @@ -788,6 +758,8 @@ public class NotificationPanelViewController extends PanelViewController { .setMaxLengthSeconds(0.4f).build(); mStatusBarMinHeight = mResources.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height); + mStatusBarHeaderHeightKeyguard = mResources.getDimensionPixelSize( + R.dimen.status_bar_header_height_keyguard); mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height); mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize( R.dimen.header_notifications_collide_distance); @@ -808,21 +780,13 @@ public class NotificationPanelViewController extends PanelViewController { } private void updateViewControllers(KeyguardStatusView keyguardStatusView, - UserAvatarView userAvatarView, - KeyguardStatusBarView keyguardStatusBarView, - KeyguardUserSwitcherView keyguardUserSwitcherView) { + UserAvatarView userAvatarView, KeyguardUserSwitcherView keyguardUserSwitcherView) { // Re-associate the KeyguardStatusViewController KeyguardStatusViewComponent statusViewComponent = mKeyguardStatusViewComponentFactory.build(keyguardStatusView); mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController(); mKeyguardStatusViewController.init(); - KeyguardStatusBarViewComponent statusBarViewComponent = - mKeyguardStatusBarViewComponentFactory.build(keyguardStatusBarView); - mKeyguarStatusBarViewController = - statusBarViewComponent.getKeyguardStatusBarViewController(); - mKeyguarStatusBarViewController.init(); - // Re-associate the clock container with the keyguard clock switch. KeyguardClockSwitchController keyguardClockSwitchController = statusViewComponent.getKeyguardClockSwitchController(); @@ -832,7 +796,6 @@ public class NotificationPanelViewController extends PanelViewController { // Try to close the switcher so that callbacks are triggered if necessary. // Otherwise, NPV can get into a state where some of the views are still hidden mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false); - mKeyguardUserSwitcherController.removeCallback(); } mKeyguardQsUserSwitchController = null; @@ -852,7 +815,6 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView); mKeyguardUserSwitcherController = userSwitcherComponent.getKeyguardUserSwitcherController(); - mKeyguardUserSwitcherController.setCallback(mKeyguardUserSwitcherListener); mKeyguardUserSwitcherController.init(); mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true); } else { @@ -882,25 +844,16 @@ public class NotificationPanelViewController extends PanelViewController { public void updateResources() { int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width); - ViewGroup.LayoutParams lp = mQsFrame.getLayoutParams(); - if (lp.width != qsWidth) { - lp.width = qsWidth; - mQsFrame.setLayoutParams(lp); - } - int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width); - lp = mNotificationStackScrollLayoutController.getLayoutParams(); - if (lp.width != panelWidth) { - lp.width = panelWidth; - mNotificationStackScrollLayoutController.setLayoutParams(lp); - } - // In order to change the constraints at runtime, all children of the Constraint Layout - // must have ids. + // To change the constraints at runtime, all children of the ConstraintLayout must have ids ensureAllViewsHaveIds(mNotificationContainerParent); ConstraintSet constraintSet = new ConstraintSet(); constraintSet.clone(mNotificationContainerParent); if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) { + // width = 0 to take up all available space within constraints + qsWidth = 0; + panelWidth = 0; constraintSet.connect(R.id.qs_frame, END, R.id.qs_edge_guideline, END); constraintSet.connect( R.id.notification_stack_scroller, START, @@ -909,6 +862,8 @@ public class NotificationPanelViewController extends PanelViewController { constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END); constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START); } + constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth; + constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth; constraintSet.applyTo(mNotificationContainerParent); } @@ -970,8 +925,7 @@ public class NotificationPanelViewController extends PanelViewController { showKeyguardUserSwitcher /* enabled */); mBigClockContainer.removeAllViews(); - updateViewControllers( - keyguardStatusView, userAvatarView, mKeyguardStatusBar, keyguardUserSwitcherView); + updateViewControllers(keyguardStatusView, userAvatarView, keyguardUserSwitcherView); // Update keyguard bottom area index = mView.indexOfChild(mKeyguardBottomArea); @@ -1101,7 +1055,7 @@ public class NotificationPanelViewController extends PanelViewController { int totalHeight = mView.getHeight(); int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight); - int userSwitcherPreferredY = mStatusBarMinHeight; + int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard; boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); @@ -1110,7 +1064,8 @@ public class NotificationPanelViewController extends PanelViewController { ? mKeyguardQsUserSwitchController.getUserIconHeight() : (mKeyguardUserSwitcherController != null ? mKeyguardUserSwitcherController.getUserIconHeight() : 0); - mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding, + mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard, + totalHeight - bottomPadding, mNotificationStackScrollLayoutController.getIntrinsicContentHeight(), getExpandedFraction(), totalHeight, @@ -1868,7 +1823,7 @@ public class NotificationPanelViewController extends PanelViewController { } } - private void setQsExpanded(boolean expanded) { + @VisibleForTesting void setQsExpanded(boolean expanded) { boolean changed = mQsExpanded != expanded; if (changed) { mQsExpanded = expanded; @@ -1971,8 +1926,10 @@ public class NotificationPanelViewController extends PanelViewController { private void updateQsState() { mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded); mNotificationStackScrollLayoutController.setScrollingEnabled( - mBarState != KEYGUARD && (!mQsExpanded - || mQsExpansionFromOverscroll)); + mBarState != KEYGUARD + && (!mQsExpanded + || mQsExpansionFromOverscroll + || Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources))); if (mKeyguardUserSwitcherController != null && mQsExpanded && !mStackScrollerOverscrolling) { @@ -2252,13 +2209,16 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected boolean canCollapsePanelOnTouch() { - if (!isInSettings()) { - return mBarState == KEYGUARD - || mIsPanelCollapseOnQQS - || mNotificationStackScrollLayoutController.isScrolledToBottom(); - } else { + if (!isInSettings() && mBarState == KEYGUARD) { return true; } + + if (mNotificationStackScrollLayoutController.isScrolledToBottom()) { + return true; + } + + return !Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources) + && (isInSettings() || mIsPanelCollapseOnQQS); } @Override @@ -2936,7 +2896,8 @@ public class NotificationPanelViewController extends PanelViewController { * @param x the x-coordinate the touch event */ protected void updateHorizontalPanelPosition(float x) { - if (mNotificationStackScrollLayoutController.getWidth() * 1.75f > mView.getWidth()) { + if (mNotificationStackScrollLayoutController.getWidth() * 1.75f > mView.getWidth() + || Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) { resetHorizontalPanelPosition(); return; } @@ -2964,9 +2925,8 @@ public class NotificationPanelViewController extends PanelViewController { protected void setHorizontalPanelTranslation(float translation) { mNotificationStackScrollLayoutController.setTranslationX(translation); mQsFrame.setTranslationX(translation); - int size = mVerticalTranslationListener.size(); - for (int i = 0; i < size; i++) { - mVerticalTranslationListener.get(i).run(); + if (mVerticalTranslationListener != null) { + mVerticalTranslationListener.run(); } } @@ -3259,12 +3219,8 @@ public class NotificationPanelViewController extends PanelViewController { mTrackingHeadsUpListeners.remove(listener); } - public void addVerticalTranslationListener(Runnable verticalTranslationListener) { - mVerticalTranslationListener.add(verticalTranslationListener); - } - - public void removeVerticalTranslationListener(Runnable verticalTranslationListener) { - mVerticalTranslationListener.remove(verticalTranslationListener); + public void setVerticalTranslationListener(Runnable verticalTranslationListener) { + mVerticalTranslationListener = verticalTranslationListener; } public void setHeadsUpAppearanceController( @@ -3554,31 +3510,22 @@ public class NotificationPanelViewController extends PanelViewController { return false; } - private void updateUserSwitcherVisibility(boolean open) { - // Do not update if previously called with the same state. - if (mKeyguardUserSwitcherIsShowing == open) { - return; - } - mKeyguardUserSwitcherIsShowing = open; - - if (open) { - animateKeyguardStatusBarOut(); - mKeyguardStatusViewController.setKeyguardStatusViewVisibility( - mBarState, - true /* keyguardFadingAway */, - true /* goingToFullShade */, - mBarState); - setKeyguardBottomAreaVisibility(mBarState, true); - mNotificationContainerParent.setVisibility(View.GONE); - } else { - animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); - mKeyguardStatusViewController.setKeyguardStatusViewVisibility( - StatusBarState.KEYGUARD, - false, - false, - StatusBarState.SHADE_LOCKED); - setKeyguardBottomAreaVisibility(mBarState, false); - mNotificationContainerParent.setVisibility(View.VISIBLE); + private void updateDisabledUdfpsController() { + final boolean udfpsEnrolled = mAuthController.getUdfpsRegion() != null + && mAuthController.isUdfpsEnrolled( + KeyguardUpdateMonitor.getCurrentUser()); + if (mDisabledUdfpsController == null && udfpsEnrolled) { + mLayoutInflater.inflate(R.layout.disabled_udfps_view, mView); + mDisabledUdfpsController = new DisabledUdfpsController( + mView.findViewById(R.id.disabled_udfps_view), + mStatusBarStateController, + mUpdateMonitor, + mAuthController, + mStatusBarKeyguardViewManager); + mDisabledUdfpsController.init(); + } else if (mDisabledUdfpsController != null && !udfpsEnrolled) { + mDisabledUdfpsController.destroy(); + mDisabledUdfpsController = null; } } @@ -3631,6 +3578,10 @@ public class NotificationPanelViewController extends PanelViewController { NotificationStackScrollLayout.OnOverscrollTopChangedListener { @Override public void onOverscrollTopChanged(float amount, boolean isRubberbanded) { + // When in split shade, overscroll shouldn't carry through to QS + if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) { + return; + } cancelQsAnimation(); if (!mQsExpansionEnabled) { amount = 0f; @@ -3997,6 +3948,7 @@ public class NotificationPanelViewController extends PanelViewController { FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener); mStatusBarStateController.addCallback(mStatusBarStateListener); mConfigurationController.addCallback(mConfigurationListener); + updateDisabledUdfpsController(); mUpdateMonitor.registerCallback(mKeyguardUpdateCallback); // Theme might have changed between inflating this view and attaching it to the // window, so diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java index e394ebc65a6b..0c9ed661925c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java @@ -18,14 +18,12 @@ package com.android.systemui.statusbar.phone; import android.app.Fragment; import android.content.Context; -import android.content.res.Configuration; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; import android.view.WindowInsets; import android.widget.FrameLayout; -import androidx.annotation.DimenRes; import androidx.constraintlayout.widget.ConstraintLayout; import com.android.systemui.R; @@ -83,22 +81,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout } @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - reloadWidth(mQsFrame, R.dimen.qs_panel_width); - reloadWidth(mStackScroller, R.dimen.notification_panel_width); - } - - /** - * Loads the given width resource and sets it on the given View. - */ - private void reloadWidth(View view, @DimenRes int width) { - LayoutParams params = (LayoutParams) view.getLayoutParams(); - params.width = getResources().getDimensionPixelSize(width); - view.setLayoutParams(params); - } - - @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { mBottomPadding = insets.getStableInsetBottom(); setPadding(0, 0, 0, mBottomPadding); 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 041a97e1d404..b25fced6a212 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -78,6 +78,7 @@ import android.media.AudioAttributes; import android.metrics.LogMaker; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -276,7 +277,8 @@ public class StatusBar extends SystemUI implements DemoMode, public static final boolean DEBUG = false; public static final boolean SPEW = false; public static final boolean DUMPTRUCK = true; // extra dumpsys info - public static final boolean DEBUG_GESTURES = false; + public static final boolean DEBUG_GESTURES = Build.IS_DEBUGGABLE; // TODO(b/178277858) + public static final boolean DEBUG_GESTURES_VERBOSE = true; public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false; public static final boolean DEBUG_CAMERA_LIFT = false; @@ -456,9 +458,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final DisplayMetrics mDisplayMetrics; // XXX: gesture research - private final GestureRecorder mGestureRec = DEBUG_GESTURES - ? new GestureRecorder("/sdcard/statusbar_gestures.dat") - : null; + private GestureRecorder mGestureRec = null; private final ScreenPinningRequest mScreenPinningRequest; @@ -856,6 +856,10 @@ public class StatusBar extends SystemUI implements DemoMode, mActivityIntentHelper = new ActivityIntentHelper(mContext); DateTimeView.setReceiverHandler(timeTickHandler); + + if (DEBUG_GESTURES) { + mGestureRec = new GestureRecorder(mContext.getCacheDir() + "/statusbar_gestures.dat"); + } } @Override @@ -2267,7 +2271,7 @@ public class StatusBar extends SystemUI implements DemoMode, public boolean interceptTouchEvent(MotionEvent event) { if (DEBUG_GESTURES) { - if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { + if (DEBUG_GESTURES_VERBOSE || event.getActionMasked() != MotionEvent.ACTION_MOVE) { EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH, event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled1, mDisabled2); 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 7bc1bb39642b..9ee7b09589d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -27,6 +27,8 @@ import com.android.systemui.Dependency; import com.android.systemui.R; 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.policy.SecurityController; import com.android.systemui.tuner.TunerService; @@ -40,7 +42,7 @@ import java.util.Objects; public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback, SecurityController.SecurityControllerCallback, Tunable { private static final String TAG = "StatusBarSignalPolicy"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.INFO); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final String mSlotAirplane; private final String mSlotMobile; @@ -143,24 +145,14 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba } @Override - public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, - boolean activityIn, boolean activityOut, String description, boolean isTransient, - String statusLabel) { + public void setWifiIndicators(WifiIndicators indicators) { if (DEBUG) { - Log.d(TAG, "setWifiIndicators: " - + "enabled = " + enabled + "," - + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + "," - + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + "," - + "activityIn = " + activityIn + "," - + "activityOut = " + activityOut + "," - + "description = " + description + "," - + "isTransient = " + isTransient + "," - + "statusLabel = " + statusLabel); - } - boolean visible = statusIcon.visible && !mHideWifi; - boolean in = activityIn && mActivityEnabled && visible; - boolean out = activityOut && mActivityEnabled && visible; - mIsWifiEnabled = enabled; + Log.d(TAG, "setWifiIndicators: " + indicators); + } + boolean visible = indicators.statusIcon.visible && !mHideWifi; + boolean in = indicators.activityIn && mActivityEnabled && visible; + boolean out = indicators.activityOut && mActivityEnabled && visible; + mIsWifiEnabled = indicators.enabled; WifiIconState newState = mWifiIconState.copy(); @@ -174,10 +166,10 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba newState.resId = R.drawable.ic_qs_no_internet_available; } else { newState.visible = visible; - newState.resId = statusIcon.icon; + newState.resId = indicators.statusIcon.icon; newState.activityIn = in; newState.activityOut = out; - newState.contentDescription = statusIcon.contentDescription; + newState.contentDescription = indicators.statusIcon.contentDescription; MobileIconState first = getFirstMobileState(); newState.signalSpacerVisible = first != null && first.typeId != 0; } @@ -225,44 +217,28 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba } @Override - public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, - int qsType, boolean activityIn, boolean activityOut, - CharSequence typeContentDescription, - CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming, boolean showTriangle) { + public void setMobileDataIndicators(MobileDataIndicators indicators) { if (DEBUG) { - Log.d(TAG, "setMobileDataIndicators: " - + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + "," - + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + "," - + "statusType = " + statusType + "," - + "qsType = " + qsType + "," - + "activityIn = " + activityIn + "," - + "activityOut = " + activityOut + "," - + "typeContentDescription = " + typeContentDescription + "," - + "typeContentDescriptionHtml = " + typeContentDescriptionHtml + "," - + "description = " + description + "," - + "isWide = " + isWide + "," - + "subId = " + subId + "," - + "roaming = " + roaming + "," - + "showTriangle = " + showTriangle); - } - MobileIconState state = getState(subId); + Log.d(TAG, "setMobileDataIndicators: " + indicators); + } + MobileIconState state = getState(indicators.subId); if (state == null) { return; } // Visibility of the data type indicator changed - boolean typeChanged = statusType != state.typeId && (statusType == 0 || state.typeId == 0); - - state.visible = statusIcon.visible && !mHideMobile; - state.strengthId = statusIcon.icon; - state.typeId = statusType; - state.contentDescription = statusIcon.contentDescription; - state.typeContentDescription = typeContentDescription; - state.showTriangle = showTriangle; - state.roaming = roaming; - state.activityIn = activityIn && mActivityEnabled; - state.activityOut = activityOut && mActivityEnabled; + boolean typeChanged = indicators.statusType != state.typeId + && (indicators.statusType == 0 || state.typeId == 0); + + state.visible = indicators.statusIcon.visible && !mHideMobile; + state.strengthId = indicators.statusIcon.icon; + state.typeId = indicators.statusType; + state.contentDescription = indicators.statusIcon.contentDescription; + state.typeContentDescription = indicators.typeContentDescription; + state.showTriangle = indicators.showTriangle; + state.roaming = indicators.roaming; + state.activityIn = indicators.activityIn && mActivityEnabled; + state.activityOut = indicators.activityOut && mActivityEnabled; if (DEBUG) { Log.d(TAG, "MobileIconStates: " diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java index 5ff897029543..b96cb5e36c82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java @@ -23,7 +23,9 @@ import android.telephony.SubscriptionInfo; import com.android.internal.annotations.VisibleForTesting; 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 java.io.PrintWriter; import java.text.SimpleDateFormat; @@ -56,6 +58,7 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa private final String[] mHistory = new String[HISTORY_SIZE]; // Where to copy the next state into. private int mHistoryIndex; + private String mLastCallback; public CallbackHandler() { super(Looper.getMainLooper()); @@ -118,63 +121,29 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa } @Override - public void setWifiIndicators(final boolean enabled, final IconState statusIcon, - final IconState qsIcon, final boolean activityIn, final boolean activityOut, - final String description, boolean isTransient, String secondaryLabel) { + public void setWifiIndicators(final WifiIndicators indicators) { String log = new StringBuilder() .append(SSDF.format(System.currentTimeMillis())).append(",") - .append("setWifiIndicators: ") - .append("enabled=").append(enabled).append(",") - .append("statusIcon=").append(statusIcon).append(",") - .append("qsIcon=").append(qsIcon).append(",") - .append("activityIn=").append(activityIn).append(",") - .append("activityOut=").append(activityOut).append(",") - .append("description=").append(description).append(",") - .append("isTransient=").append(isTransient).append(",") - .append("secondaryLabel=").append(secondaryLabel) + .append(indicators) .toString(); recordLastCallback(log); post(() -> { for (SignalCallback callback : mSignalCallbacks) { - callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut, - description, isTransient, secondaryLabel); + callback.setWifiIndicators(indicators); } }); - - } @Override - public void setMobileDataIndicators(final IconState statusIcon, final IconState qsIcon, - final int statusType, final int qsType, final boolean activityIn, - final boolean activityOut, final CharSequence typeContentDescription, - CharSequence typeContentDescriptionHtml, final CharSequence description, - final boolean isWide, final int subId, boolean roaming, boolean showTriangle) { + public void setMobileDataIndicators(final MobileDataIndicators indicators) { String log = new StringBuilder() .append(SSDF.format(System.currentTimeMillis())).append(",") - .append("setMobileDataIndicators: ") - .append("statusIcon=").append(statusIcon).append(",") - .append("qsIcon=").append(qsIcon).append(",") - .append("statusType=").append(statusType).append(",") - .append("qsType=").append(qsType).append(",") - .append("activityIn=").append(activityIn).append(",") - .append("activityOut=").append(activityOut).append(",") - .append("typeContentDescription=").append(typeContentDescription).append(",") - .append("typeContentDescriptionHtml=").append(typeContentDescriptionHtml) - .append(",") - .append("description=").append(description).append(",") - .append("isWide=").append(isWide).append(",") - .append("subId=").append(subId).append(",") - .append("roaming=").append(roaming).append(",") - .append("showTriangle=").append(showTriangle) + .append(indicators) .toString(); recordLastCallback(log); post(() -> { for (SignalCallback signalCluster : mSignalCallbacks) { - signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType, - activityIn, activityOut, typeContentDescription, - typeContentDescriptionHtml, description, isWide, subId, roaming, - showTriangle); + signalCluster.setMobileDataIndicators(indicators); } }); } @@ -182,14 +151,20 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa @Override public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork, boolean noNetworksAvailable) { - String log = new StringBuilder() - .append(SSDF.format(System.currentTimeMillis())).append(",") + String currentCallback = new StringBuilder() .append("setConnectivityStatus: ") .append("noDefaultNetwork=").append(noDefaultNetwork).append(",") .append("noValidatedNetwork=").append(noValidatedNetwork).append(",") .append("noNetworksAvailable=").append(noNetworksAvailable) .toString(); - recordLastCallback(log); + if (!currentCallback.equals(mLastCallback)) { + mLastCallback = currentCallback; + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append(currentCallback).append(",") + .toString(); + recordLastCallback(log); + } post(() -> { for (SignalCallback signalCluster : mSignalCallbacks) { signalCluster.setConnectivityStatus( @@ -200,13 +175,19 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa @Override public void setCallIndicator(IconState statusIcon, int subId) { - String log = new StringBuilder() - .append(SSDF.format(System.currentTimeMillis())).append(",") + String currentCallback = new StringBuilder() .append("setCallIndicator: ") .append("statusIcon=").append(statusIcon).append(",") .append("subId=").append(subId) .toString(); - recordLastCallback(log); + if (!currentCallback.equals(mLastCallback)) { + mLastCallback = currentCallback; + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append(currentCallback).append(",") + .toString(); + recordLastCallback(log); + } post(() -> { for (SignalCallback signalCluster : mSignalCallbacks) { signalCluster.setCallIndicator(statusIcon, subId); @@ -216,12 +197,18 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa @Override public void setSubs(List<SubscriptionInfo> subs) { - String log = new StringBuilder() - .append(SSDF.format(System.currentTimeMillis())).append(",") + String currentCallback = new StringBuilder() .append("setSubs: ") .append("subs=").append(subs == null ? "" : subs.toString()) .toString(); - recordLastCallback(log); + if (!currentCallback.equals(mLastCallback)) { + mLastCallback = currentCallback; + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append(currentCallback).append(",") + .toString(); + recordLastCallback(log); + } obtainMessage(MSG_SUBS_CHANGED, subs).sendToTarget(); } @@ -253,12 +240,18 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa @Override public void setIsAirplaneMode(IconState icon) { - String log = new StringBuilder() - .append(SSDF.format(System.currentTimeMillis())).append(",") + String currentCallback = new StringBuilder() .append("setIsAirplaneMode: ") .append("icon=").append(icon) .toString(); - recordLastCallback(log); + if (!currentCallback.equals(mLastCallback)) { + mLastCallback = currentCallback; + String log = new StringBuilder() + .append(SSDF.format(System.currentTimeMillis())).append(",") + .append(currentCallback).append(",") + .toString(); + recordLastCallback(log); + } obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java index a76d08a438f2..7f935d28285f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java @@ -16,17 +16,17 @@ package com.android.systemui.statusbar.policy; -import android.hardware.SensorPrivacyManager.IndividualSensor; +import android.hardware.SensorPrivacyManager.Sensors.Sensor; public interface IndividualSensorPrivacyController extends CallbackController<IndividualSensorPrivacyController.Callback> { void init(); - boolean isSensorBlocked(@IndividualSensor int sensor); + boolean isSensorBlocked(@Sensor int sensor); - void setSensorBlocked(@IndividualSensor int sensor, boolean blocked); + void setSensorBlocked(@Sensor int sensor, boolean blocked); interface Callback { - void onSensorBlockedChanged(@IndividualSensor int sensor, boolean blocked); + void onSensorBlockedChanged(@Sensor int sensor, boolean blocked); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java index 32d15ed41648..295df05797ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.policy; -import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA; -import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE; +import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; +import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; import android.hardware.SensorPrivacyManager; -import android.hardware.SensorPrivacyManager.IndividualSensor; +import android.hardware.SensorPrivacyManager.Sensors.Sensor; import android.util.ArraySet; import android.util.SparseBooleanArray; @@ -30,8 +30,7 @@ import java.util.Set; public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController { - private static final int[] SENSORS = new int[] {INDIVIDUAL_SENSOR_CAMERA, - INDIVIDUAL_SENSOR_MICROPHONE}; + private static final int[] SENSORS = new int[] {CAMERA, MICROPHONE}; private final @NonNull SensorPrivacyManager mSensorPrivacyManager; private final SparseBooleanArray mState = new SparseBooleanArray(); @@ -48,18 +47,18 @@ public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPr mSensorPrivacyManager.addSensorPrivacyListener(sensor, (enabled) -> onSensorPrivacyChanged(sensor, enabled)); - mState.put(sensor, mSensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor)); + mState.put(sensor, mSensorPrivacyManager.isSensorPrivacyEnabled(sensor)); } } @Override - public boolean isSensorBlocked(@IndividualSensor int sensor) { + public boolean isSensorBlocked(@Sensor int sensor) { return mState.get(sensor, false); } @Override - public void setSensorBlocked(@IndividualSensor int sensor, boolean blocked) { - mSensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, blocked); + public void setSensorBlocked(@Sensor int sensor, boolean blocked) { + mSensorPrivacyManager.setSensorPrivacyForProfileGroup(sensor, blocked); } @Override @@ -72,7 +71,7 @@ public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPr mCallbacks.remove(listener); } - private void onSensorPrivacyChanged(@IndividualSensor int sensor, boolean blocked) { + private void onSensorPrivacyChanged(@Sensor int sensor, boolean blocked) { mState.put(sensor, blocked); for (Callback callback : mCallbacks) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java deleted file mode 100644 index cbc8405cc057..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java +++ /dev/null @@ -1,79 +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.systemui.statusbar.policy; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.Notification; -import android.widget.Button; - -import java.util.Collections; -import java.util.List; - -/** - * Holder for inflated smart replies and actions. These objects should be inflated on a background - * thread, to later be accessed and modified on the (performance critical) UI thread. - */ -public class InflatedSmartReplies { - @Nullable private final SmartReplyView mSmartReplyView; - @Nullable private final List<Button> mSmartSuggestionButtons; - @NonNull private final SmartRepliesAndActions mSmartRepliesAndActions; - - public InflatedSmartReplies( - @Nullable SmartReplyView smartReplyView, - @Nullable List<Button> smartSuggestionButtons, - @NonNull SmartRepliesAndActions smartRepliesAndActions) { - mSmartReplyView = smartReplyView; - mSmartSuggestionButtons = smartSuggestionButtons; - mSmartRepliesAndActions = smartRepliesAndActions; - } - - @Nullable public SmartReplyView getSmartReplyView() { - return mSmartReplyView; - } - - @Nullable public List<Button> getSmartSuggestionButtons() { - return mSmartSuggestionButtons; - } - - @NonNull public SmartRepliesAndActions getSmartRepliesAndActions() { - return mSmartRepliesAndActions; - } - - /** - * A storage for smart replies and smart action. - */ - public static class SmartRepliesAndActions { - @Nullable public final SmartReplyView.SmartReplies smartReplies; - @Nullable public final SmartReplyView.SmartActions smartActions; - - SmartRepliesAndActions( - @Nullable SmartReplyView.SmartReplies smartReplies, - @Nullable SmartReplyView.SmartActions smartActions) { - this.smartReplies = smartReplies; - this.smartActions = smartActions; - } - - @NonNull public List<CharSequence> getSmartReplies() { - return smartReplies == null ? Collections.emptyList() : smartReplies.choices; - } - - @NonNull public List<Notification.Action> getSmartActions() { - return smartActions == null ? Collections.emptyList() : smartActions.actions; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyState.kt new file mode 100644 index 000000000000..1f8b84ff1ec5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyState.kt @@ -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. + */ +package com.android.systemui.statusbar.policy + +import android.app.Notification +import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions +import com.android.systemui.statusbar.policy.SmartReplyView.SmartReplies + +/** + * A storage for smart replies, smart actions, and related state + */ +class InflatedSmartReplyState internal constructor( + val smartReplies: SmartReplies?, + val smartActions: SmartActions?, + val suppressedActions: SuppressedActions?, + val hasPhishingAction: Boolean +) { + val smartRepliesList: List<CharSequence> + get() = smartReplies?.choices ?: emptyList() + val smartActionsList: List<Notification.Action> + get() = smartActions?.actions ?: emptyList() + val suppressedActionIndices: List<Int> + get() = suppressedActions?.suppressedActionIndices ?: emptyList() + + /** + * Data class for standard actions suppressed by the smart actions. + */ + class SuppressedActions(val suppressedActionIndices: List<Int>) +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyViewHolder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyViewHolder.kt new file mode 100644 index 000000000000..4e5c461b9643 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyViewHolder.kt @@ -0,0 +1,27 @@ +/* + * 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.policy + +import android.widget.Button + +/** + * Holder for inflated smart replies and actions. These objects should be inflated on a background + * thread, to later be accessed and modified on the (performance critical) UI thread. + */ +class InflatedSmartReplyViewHolder( + val smartReplyView: SmartReplyView?, + val smartSuggestionButtons: List<Button>? +)
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java index b76e451cb681..8845a05cf6f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java @@ -19,9 +19,13 @@ package com.android.systemui.statusbar.policy; import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA; import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; import android.database.DataSetObserver; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.os.UserHandle; @@ -50,7 +54,6 @@ import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.ViewController; -import java.lang.ref.WeakReference; import java.util.ArrayList; import javax.inject.Inject; @@ -73,9 +76,10 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS private final KeyguardUserAdapter mAdapter; private final KeyguardStateController mKeyguardStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private WeakReference<KeyguardUserSwitcherListener> mKeyguardUserSwitcherCallback; protected final SysuiStatusBarStateController mStatusBarStateController; private final KeyguardVisibilityHelper mKeyguardVisibilityHelper; + private ObjectAnimator mBgAnimator; + private final KeyguardUserSwitcherScrim mBackground; // Child views of KeyguardUserSwitcherView private KeyguardUserSwitcherListView mListView; @@ -171,6 +175,7 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS mUserSwitcherController, this); mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController, dozeParameters); + mBackground = new KeyguardUserSwitcherScrim(context); } @Override @@ -204,6 +209,9 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS mKeyguardUpdateMonitor.registerCallback(mInfoCallback); mStatusBarStateController.addCallback(mStatusBarStateListener); mScreenLifecycle.addObserver(mScreenObserver); + mView.addOnLayoutChangeListener(mBackground); + mView.setBackground(mBackground); + mBackground.setAlpha(0); } @Override @@ -217,6 +225,9 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS mKeyguardUpdateMonitor.removeCallback(mInfoCallback); mStatusBarStateController.removeCallback(mStatusBarStateListener); mScreenLifecycle.removeObserver(mScreenObserver); + mView.removeOnLayoutChangeListener(mBackground); + mView.setBackground(null); + mBackground.setAlpha(0); } /** @@ -338,6 +349,13 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS animate); PropertyAnimator.setProperty(mListView, AnimatableProperty.TRANSLATION_X, -Math.abs(x), ANIMATION_PROPERTIES, animate); + + Rect r = new Rect(); + mListView.getDrawingRect(r); + mView.offsetDescendantRectToMyCoords(mListView, r); + mBackground.setGradientCenter( + (int) (mListView.getTranslationX() + r.left + r.width() / 2), + (int) (mListView.getTranslationY() + r.top + r.height() / 2)); } /** @@ -372,49 +390,52 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS } /** - * Remove the callback if it exists. - */ - public void removeCallback() { - if (DEBUG) Log.d(TAG, "removeCallback"); - mKeyguardUserSwitcherCallback = null; - } - - /** - * Register to receive notifications about keyguard user switcher state - * (see {@link KeyguardUserSwitcherListener}. - * - * Only one callback can be used at a time. - * - * @param callback The callback to register - */ - public void setCallback(KeyguardUserSwitcherListener callback) { - if (DEBUG) Log.d(TAG, "setCallback"); - mKeyguardUserSwitcherCallback = new WeakReference<>(callback); - } - - /** - * If user switcher state changes, notifies all {@link KeyguardUserSwitcherListener}. - * Switcher state is updatd before animations finish. + * NOTE: switcher state is updated before animations finish. * * @param animate true to animate transition. The user switcher state (i.e. * {@link #isUserSwitcherOpen()}) is updated before animation is finished. */ private void setUserSwitcherOpened(boolean open, boolean animate) { - boolean wasOpen = mUserSwitcherOpen; if (DEBUG) { - Log.d(TAG, String.format("setUserSwitcherOpened: %b -> %b (animate=%b)", wasOpen, - open, animate)); + Log.d(TAG, + String.format("setUserSwitcherOpened: %b -> %b (animate=%b)", + mUserSwitcherOpen, open, animate)); } mUserSwitcherOpen = open; - if (mUserSwitcherOpen != wasOpen) { - notifyUserSwitcherStateChanged(); - } updateVisibilities(animate); } private void updateVisibilities(boolean animate) { if (DEBUG) Log.d(TAG, String.format("updateVisibilities: animate=%b", animate)); mEndGuestButton.animate().cancel(); + if (mBgAnimator != null) { + mBgAnimator.cancel(); + } + + if (mUserSwitcherOpen) { + mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255); + mBgAnimator.setDuration(400); + mBgAnimator.setInterpolator(Interpolators.ALPHA_IN); + mBgAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBgAnimator = null; + } + }); + mBgAnimator.start(); + } else { + mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 255, 0); + mBgAnimator.setDuration(400); + mBgAnimator.setInterpolator(Interpolators.ALPHA_OUT); + mBgAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBgAnimator = null; + } + }); + mBgAnimator.start(); + } + if (mUserSwitcherOpen && mCurrentUserIsGuest) { // Show the "End guest session" button mEndGuestButton.setVisibility(View.VISIBLE); @@ -459,34 +480,6 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS return mUserSwitcherOpen; } - private void notifyUserSwitcherStateChanged() { - if (DEBUG) { - Log.d(TAG, String.format("notifyUserSwitcherStateChanged: mUserSwitcherOpen=%b", - mUserSwitcherOpen)); - } - if (mKeyguardUserSwitcherCallback != null) { - KeyguardUserSwitcherListener cb = mKeyguardUserSwitcherCallback.get(); - if (cb != null) { - cb.onKeyguardUserSwitcherChanged(mUserSwitcherOpen); - } - } - } - - /** - * Callback for keyguard user switcher state information - */ - public interface KeyguardUserSwitcherListener { - - /** - * Called when the keyguard enters or leaves user switcher mode. This will be called - * before the animations are finished. - * - * @param open if true, keyguard is showing the user switcher or transitioning from/to user - * switcher mode. - */ - void onKeyguardUserSwitcherChanged(boolean open); - } - static class KeyguardUserAdapter extends UserSwitcherController.BaseUserAdapter implements View.OnClickListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java index 49f5bcdd5a44..1d9d33d2aab1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java @@ -26,7 +26,6 @@ import android.graphics.RadialGradient; import android.graphics.Rect; import android.graphics.Shader; import android.graphics.drawable.Drawable; -import android.util.LayoutDirection; import android.view.View; import com.android.systemui.R; @@ -38,13 +37,14 @@ public class KeyguardUserSwitcherScrim extends Drawable implements View.OnLayoutChangeListener { private static final float OUTER_EXTENT = 2.5f; - private static final float INNER_EXTENT = 0.75f; + private static final float INNER_EXTENT = 0.25f; private int mDarkColor; - private int mTop; private int mAlpha = 255; private Paint mRadialGradientPaint = new Paint(); - private int mLayoutWidth; + private int mCircleX; + private int mCircleY; + private int mSize; public KeyguardUserSwitcherScrim(Context context) { mDarkColor = context.getColor( @@ -53,14 +53,11 @@ public class KeyguardUserSwitcherScrim extends Drawable @Override public void draw(Canvas canvas) { - boolean isLtr = getLayoutDirection() == LayoutDirection.LTR; + if (mAlpha == 0) { + return; + } Rect bounds = getBounds(); - float width = bounds.width() * OUTER_EXTENT; - float height = (mTop + bounds.height()) * OUTER_EXTENT; - canvas.translate(0, -mTop); - canvas.scale(1, height / width); - canvas.drawRect(isLtr ? bounds.right - width : 0, 0, - isLtr ? bounds.right : bounds.left + width, width, mRadialGradientPaint); + canvas.drawRect(bounds.left, bounds.top, bounds.right, bounds.bottom, mRadialGradientPaint); } @Override @@ -88,24 +85,36 @@ public class KeyguardUserSwitcherScrim extends Drawable public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) { - mLayoutWidth = right - left; - mTop = top; + int width = right - left; + int height = bottom - top; + mSize = Math.max(width, height); updatePaint(); } } private void updatePaint() { - if (mLayoutWidth == 0) { + if (mSize == 0) { return; } - float radius = mLayoutWidth * OUTER_EXTENT; - boolean isLtr = getLayoutDirection() == LayoutDirection.LTR; + float outerRadius = mSize * OUTER_EXTENT; mRadialGradientPaint.setShader( - new RadialGradient(isLtr ? mLayoutWidth : 0, 0, radius, + new RadialGradient(mCircleX, mCircleY, outerRadius, new int[] { Color.argb( (int) (Color.alpha(mDarkColor) * mAlpha / 255f), 0, 0, 0), Color.TRANSPARENT }, - new float[] { Math.max(0f, mLayoutWidth * INNER_EXTENT / radius), 1f }, + new float[] { Math.max(0f, INNER_EXTENT / OUTER_EXTENT), 1f }, Shader.TileMode.CLAMP)); } + + /** + * Sets the center of the radial gradient used as a background + * + * @param x + * @param y + */ + public void setGradientCenter(int x, int y) { + mCircleX = x; + mCircleY = y; + updatePaint(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 1ab7652d4280..6c097bdb08d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -59,6 +59,7 @@ import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.SignalStrengthUtil; import com.android.systemui.R; 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 java.io.PrintWriter; @@ -402,10 +403,12 @@ public class MobileSignalController extends SignalController<MobileState, Mobile showDataIcon |= mCurrentState.roaming; IconState statusIcon = new IconState(showDataIcon && !mCurrentState.airplaneMode, getCurrentIconId(), contentDescription); - callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, + MobileDataIndicators mobileDataIndicators = new MobileDataIndicators( + statusIcon, qsIcon, typeIcon, qsTypeIcon, activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml, description, icons.isWide, mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming, showTriangle); + callback.setMobileDataIndicators(mobileDataIndicators); } else { boolean showDataIcon = mCurrentState.dataConnected || dataDisabled; IconState statusIcon = new IconState( @@ -432,10 +435,12 @@ public class MobileSignalController extends SignalController<MobileState, Mobile showDataIcon &= mCurrentState.isDefault || dataDisabled; int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0; boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode; - callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, + MobileDataIndicators mobileDataIndicators = new MobileDataIndicators( + statusIcon, qsIcon, typeIcon, qsTypeIcon, activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml, description, icons.isWide, mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming, showTriangle); + callback.setMobileDataIndicators(mobileDataIndicators); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 0a9fead9cb64..ef2ca985858d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -46,34 +46,117 @@ public interface NetworkController extends CallbackController<SignalCallback>, D boolean isRadioOn(); + /** + * Wrapper class for all the WiFi signals used for WiFi indicators. + */ + final class WifiIndicators { + public boolean enabled; + public IconState statusIcon; + public IconState qsIcon; + public boolean activityIn; + public boolean activityOut; + public String description; + public boolean isTransient; + public String statusLabel; + + public WifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, + boolean activityIn, boolean activityOut, String description, + boolean isTransient, String statusLabel) { + this.enabled = enabled; + this.statusIcon = statusIcon; + this.qsIcon = qsIcon; + this.activityIn = activityIn; + this.activityOut = activityOut; + this.description = description; + this.isTransient = isTransient; + this.statusLabel = statusLabel; + } + + @Override + public String toString() { + return new StringBuilder("WifiIndicators[") + .append("enabled=").append(enabled) + .append(",statusIcon=").append(statusIcon == null ? "" : statusIcon.toString()) + .append(",qsIcon=").append(qsIcon == null ? "" : qsIcon.toString()) + .append(",activityIn=").append(activityIn) + .append(",activityOut=").append(activityOut) + .append(",description=").append(description) + .append(",isTransient=").append(isTransient) + .append(",statusLabel=").append(statusLabel) + .append(']').toString(); + } + } + + /** + * Wrapper class for all the mobile signals used for mobile data indicators. + */ + final class MobileDataIndicators { + public IconState statusIcon; + public IconState qsIcon; + public int statusType; + public int qsType; + public boolean activityIn; + public boolean activityOut; + public CharSequence typeContentDescription; + public CharSequence typeContentDescriptionHtml; + public CharSequence description; + public boolean isWide; + public int subId; + public boolean roaming; + public boolean showTriangle; + + public MobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, + int qsType, boolean activityIn, boolean activityOut, + CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml, + CharSequence description, boolean isWide, int subId, boolean roaming, + boolean showTriangle) { + this.statusIcon = statusIcon; + this.qsIcon = qsIcon; + this.statusType = statusType; + this.qsType = qsType; + this.activityIn = activityIn; + this.activityOut = activityOut; + this.typeContentDescription = typeContentDescription; + this.typeContentDescriptionHtml = typeContentDescriptionHtml; + this.description = description; + this.isWide = isWide; + this.subId = subId; + this.roaming = roaming; + this.showTriangle = showTriangle; + } + + @Override + public String toString() { + return new StringBuilder("MobileDataIndicators[") + .append("statusIcon=").append(statusIcon == null ? "" : statusIcon.toString()) + .append(",qsIcon=").append(qsIcon == null ? "" : qsIcon.toString()) + .append(",statusType=").append(statusType) + .append(",qsType=").append(qsType) + .append(",activityIn=").append(activityIn) + .append(",activityOut=").append(activityOut) + .append(",typeContentDescription=").append(typeContentDescription) + .append(",typeContentDescriptionHtml=").append(typeContentDescriptionHtml) + .append(",description=").append(description) + .append(",isWide=").append(isWide) + .append(",subId=").append(subId) + .append(",roaming=").append(roaming) + .append(",showTriangle=").append(showTriangle) + .append(']').toString(); + } + } + public interface SignalCallback { - default void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, - boolean activityIn, boolean activityOut, String description, boolean isTransient, - String statusLabel) {} + /** + * Callback for listeners to be able to update the state of any UI tracking connectivity of + * WiFi networks. + */ + default void setWifiIndicators(WifiIndicators wifiIndicators) {} /** * Callback for listeners to be able to update the state of any UI tracking connectivity - * @param statusIcon the icon that should be shown in the status bar - * @param qsIcon the icon to show in Quick Settings - * @param statusType the resId of the data type icon (e.g. LTE) to show in the status bar - * @param qsType similar to above, the resId of the data type icon to show in Quick Settings - * @param activityIn indicates whether there is inbound activity - * @param activityOut indicates outbound activity - * @param typeContentDescription the contentDescription of the data type - * @param typeContentDescriptionHtml the (possibly HTML-styled) contentDescription of the - * data type. Suitable for display - * @param description description of the network (usually just the network name) - * @param isWide //TODO: unused? - * @param subId subscription ID for which to update the UI - * @param roaming indicates roaming - * @param showTriangle whether to show the mobile triangle the in status bar + * of Mobile networks. */ - default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, - int qsType, boolean activityIn, boolean activityOut, - CharSequence typeContentDescription, - CharSequence typeContentDescriptionHtml, CharSequence description, - boolean isWide, int subId, boolean roaming, boolean showTriangle) { - } + default void setMobileDataIndicators(MobileDataIndicators mobileDataIndicators) {} default void setSubs(List<SubscriptionInfo> subs) {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 9f921429f7b8..8eb1e6487046 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -165,7 +165,7 @@ public class NetworkControllerImpl extends BroadcastReceiver private int mCurrentUserId; private OnSubscriptionsChangedListener mSubscriptionListener; - + private NetworkCapabilities mLastDefaultNetworkCapabilities; // Handler that all broadcasts are received on. private final Handler mReceiverHandler; // Handler that all callbacks are made on. @@ -315,6 +315,7 @@ public class NetworkControllerImpl extends BroadcastReceiver public void onLost(Network network) { mLastNetwork = null; mLastNetworkCapabilities = null; + mLastDefaultNetworkCapabilities = null; String callback = new StringBuilder() .append(SSDF.format(System.currentTimeMillis())).append(",") .append("onLost: ") @@ -341,6 +342,7 @@ public class NetworkControllerImpl extends BroadcastReceiver } mLastNetwork = network; mLastNetworkCapabilities = networkCapabilities; + mLastDefaultNetworkCapabilities = networkCapabilities; String callback = new StringBuilder() .append(SSDF.format(System.currentTimeMillis())).append(",") .append("onCapabilitiesChanged: ") @@ -959,18 +961,17 @@ public class NetworkControllerImpl extends BroadcastReceiver private void updateConnectivity() { mConnectedTransports.clear(); mValidatedTransports.clear(); - for (NetworkCapabilities nc : - mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) { - for (int transportType : nc.getTransportTypes()) { + if (mLastDefaultNetworkCapabilities != null) { + for (int transportType : mLastDefaultNetworkCapabilities.getTransportTypes()) { if (transportType == NetworkCapabilities.TRANSPORT_CELLULAR - && Utils.tryGetWifiInfoForVcn(nc) != null) { + && Utils.tryGetWifiInfoForVcn(mLastDefaultNetworkCapabilities) != null) { mConnectedTransports.set(NetworkCapabilities.TRANSPORT_WIFI); - if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) { + if (mLastDefaultNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { mValidatedTransports.set(NetworkCapabilities.TRANSPORT_WIFI); } } else { mConnectedTransports.set(transportType); - if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) { + if (mLastDefaultNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) { mValidatedTransports.set(transportType); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 8e833c05b447..320b00af0fc5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -561,6 +561,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene super.onVisibilityChanged(changedView, visibility); if (changedView == this && mOnVisibilityChangedListener != null) { mOnVisibilityChangedListener.accept(visibility == VISIBLE); + // Hide soft-keyboard when the input view became invisible + // (i.e. The notification shade collapsed by pressing the home key) + if (visibility != VISIBLE && !mEditText.isVisibleToUser()) { + mEditText.hideIme(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt index ea803253ea0f..0bf2d503e5a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt @@ -44,7 +44,7 @@ import com.android.systemui.statusbar.SmartReplyController import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logging.NotificationLogger import com.android.systemui.statusbar.phone.KeyguardDismissUtil -import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions +import com.android.systemui.statusbar.policy.InflatedSmartReplyState.SuppressedActions import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions import com.android.systemui.statusbar.policy.SmartReplyView.SmartButtonType import com.android.systemui.statusbar.policy.SmartReplyView.SmartReplies @@ -53,10 +53,10 @@ import javax.inject.Inject /** Returns whether we should show the smart reply view and its smart suggestions. */ fun shouldShowSmartReplyView( entry: NotificationEntry, - smartRepliesAndActions: SmartRepliesAndActions + smartReplyState: InflatedSmartReplyState ): Boolean { - if (smartRepliesAndActions.smartReplies == null - && smartRepliesAndActions.smartActions == null) { + if (smartReplyState.smartReplies == null && + smartReplyState.smartActions == null) { // There are no smart replies and no smart actions. return false } @@ -71,58 +71,65 @@ fun shouldShowSmartReplyView( .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false) } -/** Determines if two [SmartRepliesAndActions] are visually similar. */ +/** Determines if two [InflatedSmartReplyState] are visually similar. */ fun areSuggestionsSimilar( - left: SmartRepliesAndActions?, - right: SmartRepliesAndActions? + left: InflatedSmartReplyState?, + right: InflatedSmartReplyState? ): Boolean = when { left === right -> true left == null || right == null -> false - left.getSmartReplies() != right.getSmartReplies() -> false - else -> !NotificationUiAdjustment.areDifferent(left.getSmartActions(), right.getSmartActions()) + left.hasPhishingAction != right.hasPhishingAction -> false + left.smartRepliesList != right.smartRepliesList -> false + left.suppressedActionIndices != right.suppressedActionIndices -> false + else -> !NotificationUiAdjustment.areDifferent(left.smartActionsList, right.smartActionsList) } -interface SmartRepliesAndActionsInflater { - fun inflateSmartReplies( +interface SmartReplyStateInflater { + fun inflateSmartReplyState(entry: NotificationEntry): InflatedSmartReplyState + + fun inflateSmartReplyViewHolder( sysuiContext: Context, notifPackageContext: Context, entry: NotificationEntry, - existingRepliesAndAction: SmartRepliesAndActions? - ): InflatedSmartReplies + existingSmartReplyState: InflatedSmartReplyState?, + newSmartReplyState: InflatedSmartReplyState + ): InflatedSmartReplyViewHolder } -/*internal*/ class SmartRepliesAndActionsInflaterImpl @Inject constructor( +/*internal*/ class SmartReplyStateInflaterImpl @Inject constructor( private val constants: SmartReplyConstants, private val activityManagerWrapper: ActivityManagerWrapper, private val packageManagerWrapper: PackageManagerWrapper, private val devicePolicyManagerWrapper: DevicePolicyManagerWrapper, private val smartRepliesInflater: SmartReplyInflater, private val smartActionsInflater: SmartActionInflater -) : SmartRepliesAndActionsInflater { +) : SmartReplyStateInflater { + + override fun inflateSmartReplyState(entry: NotificationEntry): InflatedSmartReplyState = + chooseSmartRepliesAndActions(entry) - override fun inflateSmartReplies( + override fun inflateSmartReplyViewHolder( sysuiContext: Context, notifPackageContext: Context, entry: NotificationEntry, - existingRepliesAndAction: SmartRepliesAndActions? - ): InflatedSmartReplies { - val newRepliesAndActions = chooseSmartRepliesAndActions(entry) - if (!shouldShowSmartReplyView(entry, newRepliesAndActions)) { - return InflatedSmartReplies( + existingSmartReplyState: InflatedSmartReplyState?, + newSmartReplyState: InflatedSmartReplyState + ): InflatedSmartReplyViewHolder { + if (!shouldShowSmartReplyView(entry, newSmartReplyState)) { + return InflatedSmartReplyViewHolder( null /* smartReplyView */, - null /* smartSuggestionButtons */, - newRepliesAndActions) + null /* smartSuggestionButtons */) } // Only block clicks if the smart buttons are different from the previous set - to avoid // scenarios where a user incorrectly cannot click smart buttons because the // notification is updated. val delayOnClickListener = - !areSuggestionsSimilar(existingRepliesAndAction, newRepliesAndActions) + !areSuggestionsSimilar(existingSmartReplyState, newSmartReplyState) val smartReplyView = SmartReplyView.inflate(sysuiContext, constants) - val smartReplies = newRepliesAndActions.smartReplies + val smartReplies = newSmartReplyState.smartReplies smartReplyView.setSmartRepliesGeneratedByAssistant(smartReplies?.fromAssistant ?: false) val smartReplyButtons = smartReplies?.let { smartReplies.choices.asSequence().mapIndexed { index, choice -> @@ -136,7 +143,7 @@ interface SmartRepliesAndActionsInflater { } } ?: emptySequence() - val smartActionButtons = newRepliesAndActions.smartActions?.let { smartActions -> + val smartActionButtons = newSmartReplyState.smartActions?.let { smartActions -> val themedPackageContext = ContextThemeWrapper(notifPackageContext, sysuiContext.theme) smartActions.actions.asSequence() @@ -153,10 +160,9 @@ interface SmartRepliesAndActionsInflater { } } ?: emptySequence() - return InflatedSmartReplies( + return InflatedSmartReplyViewHolder( smartReplyView, - (smartReplyButtons + smartActionButtons).toList(), - newRepliesAndActions) + (smartReplyButtons + smartActionButtons).toList()) } /** @@ -165,23 +171,23 @@ interface SmartRepliesAndActionsInflater { * replies or actions generated by the NotificationAssistantService (NAS), and if the app * provides any smart actions we also don't show any NAS-generated replies or actions. */ - fun chooseSmartRepliesAndActions(entry: NotificationEntry): SmartRepliesAndActions { + fun chooseSmartRepliesAndActions(entry: NotificationEntry): InflatedSmartReplyState { val notification = entry.sbn.notification val remoteInputActionPair = notification.findRemoteInputActionPair(false /* freeform */) val freeformRemoteInputActionPair = notification.findRemoteInputActionPair(true /* freeform */) if (!constants.isEnabled) { if (DEBUG) { - Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for " - + entry.sbn.key) + Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for " + + entry.sbn.key) } - return SmartRepliesAndActions(null, null) + return InflatedSmartReplyState(null, null, null, false) } // Only use smart replies from the app if they target P or above. We have this check because // the smart reply API has been used for other things (Wearables) in the past. The API to // add smart actions is new in Q so it doesn't require a target-sdk check. - val enableAppGeneratedSmartReplies = (!constants.requiresTargetingP() - || entry.targetSdk >= Build.VERSION_CODES.P) + val enableAppGeneratedSmartReplies = (!constants.requiresTargetingP() || + entry.targetSdk >= Build.VERSION_CODES.P) val appGeneratedSmartActions = notification.contextualActions var smartReplies: SmartReplies? = when { @@ -207,18 +213,18 @@ interface SmartRepliesAndActionsInflater { if (smartReplies == null && smartActions == null) { val entryReplies = entry.smartReplies val entryActions = entry.smartActions - if (entryReplies.isNotEmpty() - && freeformRemoteInputActionPair != null - && freeformRemoteInputActionPair.second.allowGeneratedReplies - && freeformRemoteInputActionPair.second.actionIntent != null) { + if (entryReplies.isNotEmpty() && + freeformRemoteInputActionPair != null && + freeformRemoteInputActionPair.second.allowGeneratedReplies && + freeformRemoteInputActionPair.second.actionIntent != null) { smartReplies = SmartReplies( entryReplies, freeformRemoteInputActionPair.first, freeformRemoteInputActionPair.second.actionIntent, true /* fromAssistant */) } - if (entryActions.isNotEmpty() - && notification.allowSystemGeneratedContextualActions) { + if (entryActions.isNotEmpty() && + notification.allowSystemGeneratedContextualActions) { val systemGeneratedActions: List<Notification.Action> = when { activityManagerWrapper.isLockTaskKioskModeActive -> // Filter actions if we're in kiosk-mode - we don't care about screen @@ -229,7 +235,21 @@ interface SmartRepliesAndActionsInflater { smartActions = SmartActions(systemGeneratedActions, true /* fromAssistant */) } } - return SmartRepliesAndActions(smartReplies, smartActions) + val hasPhishingAction = smartActions?.actions?.any { + it.isContextual && it.semanticAction == + Notification.Action.SEMANTIC_ACTION_CONVERSATION_IS_PHISHING + } ?: false + var suppressedActions: SuppressedActions? = null + if (hasPhishingAction) { + // If there is a phishing action, calculate the indices of the actions with RemoteInput + // as those need to be hidden from the view. + val suppressedActionIndices = notification.actions.mapIndexedNotNull { index, action -> + if (action.remoteInputs?.isNotEmpty() == true) index else null + } + suppressedActions = SuppressedActions(suppressedActionIndices) + } + return InflatedSmartReplyState(smartReplies, smartActions, suppressedActions, + hasPhishingAction) } /** @@ -311,8 +331,8 @@ interface SmartActionInflater { actionIndex: Int, action: Notification.Action ) = - if (smartActions.fromAssistant - && SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY == action.semanticAction) { + if (smartActions.fromAssistant && + SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY == action.semanticAction) { entry.row.doSmartActionClick(entry.row.x.toInt() / 2, entry.row.y.toInt() / 2, SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY) smartReplyController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index 34c78813d11a..ad4fa64ac905 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -794,8 +794,8 @@ public class SmartReplyView extends ViewGroup { public final List<CharSequence> choices; public final boolean fromAssistant; - public SmartReplies(List<CharSequence> choices, RemoteInput remoteInput, - PendingIntent pendingIntent, boolean fromAssistant) { + public SmartReplies(@NonNull List<CharSequence> choices, @NonNull RemoteInput remoteInput, + @NonNull PendingIntent pendingIntent, boolean fromAssistant) { this.choices = choices; this.remoteInput = remoteInput; this.pendingIntent = pendingIntent; @@ -812,7 +812,7 @@ public class SmartReplyView extends ViewGroup { public final List<Notification.Action> actions; public final boolean fromAssistant; - public SmartActions(List<Notification.Action> actions, boolean fromAssistant) { + public SmartActions(@NonNull List<Notification.Action> actions, boolean fromAssistant) { this.actions = actions; this.fromAssistant = fromAssistant; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index 16998d7be936..8d72c9c8810e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -38,7 +38,9 @@ import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.wifi.WifiStatusTracker; import com.android.systemui.R; 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 java.io.PrintWriter; import java.util.Objects; @@ -113,18 +115,24 @@ public class WifiSignalController extends mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected : getQsCurrentIconId(), contentDescription); } - callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon, + WifiIndicators wifiIndicators = new WifiIndicators( + mCurrentState.enabled, statusIcon, qsIcon, ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut, - wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel); + wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel + ); + callback.setWifiIndicators(wifiIndicators); } else { IconState qsIcon = new IconState(mCurrentState.connected, mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected : getQsCurrentIconId(), contentDescription); - callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon, + WifiIndicators wifiIndicators = new WifiIndicators( + mCurrentState.enabled, statusIcon, qsIcon, ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut, - wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel); + wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel + ); + callback.setWifiIndicators(wifiIndicators); } } @@ -149,10 +157,13 @@ public class WifiSignalController extends mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(), contentDescription); CharSequence description = mNetworkController.getNetworkNameForCarrierWiFi(mCurrentState.subId); - callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon, + MobileDataIndicators mobileDataIndicators = new MobileDataIndicators( + statusIcon, qsIcon, typeIcon, qsTypeIcon, mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription, dataContentDescriptionHtml, description, icons.isWide, - mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true); + mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true + ); + callback.setMobileDataIndicators(mobileDataIndicators); } private int getCurrentIconIdForCarrierWifi() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt index 803d26ec3286..ce4a9270925b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt @@ -17,10 +17,10 @@ package com.android.systemui.statusbar.policy.dagger import com.android.systemui.statusbar.policy.SmartActionInflater import com.android.systemui.statusbar.policy.SmartActionInflaterImpl -import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflater -import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflaterImpl import com.android.systemui.statusbar.policy.SmartReplyInflater import com.android.systemui.statusbar.policy.SmartReplyInflaterImpl +import com.android.systemui.statusbar.policy.SmartReplyStateInflater +import com.android.systemui.statusbar.policy.SmartReplyStateInflaterImpl import dagger.Binds import dagger.Module @@ -29,6 +29,6 @@ interface SmartRepliesInflationModule { @Binds fun bindSmartActionsInflater(impl: SmartActionInflaterImpl): SmartActionInflater @Binds fun bindSmartReplyInflater(impl: SmartReplyInflaterImpl): SmartReplyInflater @Binds fun bindsInflatedSmartRepliesProvider( - impl: SmartRepliesAndActionsInflaterImpl - ): SmartRepliesAndActionsInflater + impl: SmartReplyStateInflaterImpl + ): SmartReplyStateInflater }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index bdf2b0c24ba5..37a763b740e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -22,12 +22,10 @@ import android.os.RemoteException; import android.os.ServiceManager; import com.android.internal.statusbar.IStatusBarService; -import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.assist.AssistManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar; import javax.inject.Inject; @@ -36,11 +34,6 @@ import dagger.Lazy; /** * Status bar implementation for "large screen" products that mostly present no on-screen nav. * Serves as a collection of UI components, rather than showing its own UI. - * The following is the list of elements that constitute the TV-specific status bar: - * <ul> - * <li> {@link AudioRecordingDisclosureBar} - shown whenever applications are conducting audio - * recording, discloses the responsible applications </li> - * </ul> */ @SysUISingleton public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { @@ -66,11 +59,6 @@ public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { } catch (RemoteException ex) { // If the system process isn't there we're doomed anyway. } - - if (mContext.getResources().getBoolean(R.bool.audio_recording_disclosure_enabled)) { - // Creating AudioRecordingDisclosureBar and just letting it run - new AudioRecordingDisclosureBar(mContext); - } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java deleted file mode 100644 index bbab6253a4d1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java +++ /dev/null @@ -1,48 +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.systemui.statusbar.tv.micdisclosure; - -import android.content.Context; - -import java.util.Set; - -/** - * A base class for implementing observers for different kinds of activities related to audio - * recording. These observers are to be initialized by {@link AudioRecordingDisclosureBar} and to - * report back to it. - */ -abstract class AudioActivityObserver { - - interface OnAudioActivityStateChangeListener { - void onAudioActivityStateChange(boolean active, String packageName); - } - - final Context mContext; - - final OnAudioActivityStateChangeListener mListener; - - AudioActivityObserver(Context context, OnAudioActivityStateChangeListener listener) { - mContext = context; - mListener = listener; - } - - abstract void start(); - - abstract void stop(); - - abstract Set<String> getActivePackages(); -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java deleted file mode 100644 index 8caf95fb48f5..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java +++ /dev/null @@ -1,200 +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.systemui.statusbar.tv.micdisclosure; - -import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; - -import static com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar.DEBUG; - -import android.annotation.UiThread; -import android.app.ActivityManager; -import android.app.IActivityManager; -import android.app.IProcessObserver; -import android.content.Context; -import android.os.RemoteException; -import android.util.ArrayMap; -import android.util.Log; -import android.util.SparseArray; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * The purpose of these class is to detect packages that are running foreground services of type - * 'microphone' and to report back to {@link AudioRecordingDisclosureBar}. - */ -class MicrophoneForegroundServicesObserver extends AudioActivityObserver { - private static final String TAG = "MicrophoneForegroundServicesObserver"; - - private IActivityManager mActivityManager; - /** - * A dictionary that maps PIDs to the package names. We only keep track of the PIDs that are - * "active" (those that are running FGS with FOREGROUND_SERVICE_TYPE_MICROPHONE flag). - */ - private final SparseArray<String[]> mPidToPackages = new SparseArray<>(); - /** - * A dictionary that maps "active" packages to the number of the "active" processes associated - * with those packages. We really only need this in case when one application is running in - * multiple processes, so that we don't lose track of the package when one of its "active" - * processes ceases, while others remain "active". - */ - private final Map<String, Integer> mPackageToProcessCount = new ArrayMap<>(); - - MicrophoneForegroundServicesObserver(Context context, - OnAudioActivityStateChangeListener listener) { - super(context, listener); - } - - @Override - void start() { - mActivityManager = ActivityManager.getService(); - try { - mActivityManager.registerProcessObserver(mProcessObserver); - } catch (RemoteException e) { - Log.e(TAG, "Couldn't register process observer", e); - } - } - - @Override - void stop() { - try { - mActivityManager.unregisterProcessObserver(mProcessObserver); - } catch (RemoteException e) { - Log.e(TAG, "Couldn't unregister process observer", e); - } - mActivityManager = null; - mPackageToProcessCount.clear(); - } - - @Override - Set<String> getActivePackages() { - return mPackageToProcessCount.keySet(); - } - - @UiThread - private void onProcessForegroundServicesChanged(int pid, boolean hasMicFgs) { - final String[] changedPackages; - if (hasMicFgs) { - if (mPidToPackages.contains(pid)) { - // We are already tracking this pid - ignore. - changedPackages = null; - } else { - changedPackages = getPackageNames(pid); - mPidToPackages.append(pid, changedPackages); - } - } else { - changedPackages = mPidToPackages.removeReturnOld(pid); - } - - if (changedPackages == null) { - return; - } - - for (int index = changedPackages.length - 1; index >= 0; index--) { - final String packageName = changedPackages[index]; - int processCount = mPackageToProcessCount.getOrDefault(packageName, 0); - final boolean shouldNotify; - if (hasMicFgs) { - processCount++; - shouldNotify = processCount == 1; - } else { - processCount--; - shouldNotify = processCount == 0; - } - if (processCount > 0) { - mPackageToProcessCount.put(packageName, processCount); - } else { - mPackageToProcessCount.remove(packageName); - } - if (shouldNotify) notifyPackageStateChanged(packageName, hasMicFgs); - } - } - - @UiThread - private void onProcessDied(int pid) { - final String[] packages = mPidToPackages.removeReturnOld(pid); - if (packages == null) { - // This PID was not active - ignore. - return; - } - - for (int index = packages.length - 1; index >= 0; index--) { - final String packageName = packages[index]; - int processCount = mPackageToProcessCount.getOrDefault(packageName, 0); - if (processCount <= 0) { - Log.e(TAG, "Bookkeeping error, process count for " + packageName + " is " - + processCount); - continue; - } - processCount--; - if (processCount > 0) { - mPackageToProcessCount.put(packageName, processCount); - } else { - mPackageToProcessCount.remove(packageName); - notifyPackageStateChanged(packageName, false); - } - } - } - - @UiThread - private void notifyPackageStateChanged(String packageName, boolean active) { - if (DEBUG) { - Log.d(TAG, (active ? "New microphone fgs detected" : "Microphone fgs is gone") - + ", package=" + packageName); - } - - mListener.onAudioActivityStateChange(active, packageName); - } - - @UiThread - private String[] getPackageNames(int pid) { - final List<ActivityManager.RunningAppProcessInfo> runningApps; - try { - runningApps = mActivityManager.getRunningAppProcesses(); - } catch (RemoteException e) { - Log.d(TAG, "Couldn't get package name for pid=" + pid); - return null; - } - if (runningApps == null) { - Log.wtf(TAG, "No running apps reported"); - } - for (ActivityManager.RunningAppProcessInfo app : runningApps) { - if (app.pid == pid) { - return app.pkgList; - } - } - return null; - } - - private final IProcessObserver mProcessObserver = new IProcessObserver.Stub() { - @Override - public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {} - - @Override - public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) { - mContext.getMainExecutor().execute(() -> onProcessForegroundServicesChanged(pid, - (serviceTypes & FOREGROUND_SERVICE_TYPE_MICROPHONE) != 0)); - } - - @Override - public void onProcessDied(int pid, int uid) { - mContext.getMainExecutor().execute( - () -> MicrophoneForegroundServicesObserver.this.onProcessDied(pid)); - } - }; -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java deleted file mode 100644 index 9a2b4a93ac89..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java +++ /dev/null @@ -1,99 +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.systemui.statusbar.tv.micdisclosure; - -import static com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar.DEBUG; - -import android.annotation.UiThread; -import android.app.AppOpsManager; -import android.content.Context; -import android.util.ArraySet; -import android.util.Log; - -import java.util.Set; - -/** - * The purpose of these class is to detect packages that are conducting audio recording (according - * to {@link AppOpsManager}) and report this to {@link AudioRecordingDisclosureBar}. - */ -class RecordAudioAppOpObserver extends AudioActivityObserver implements - AppOpsManager.OnOpActiveChangedListener { - private static final String TAG = "RecordAudioAppOpObserver"; - - /** - * Set of the applications that currently are conducting audio recording according to {@link - * AppOpsManager}. - */ - private final Set<String> mActiveAudioRecordingPackages = new ArraySet<>(); - - RecordAudioAppOpObserver(Context context, OnAudioActivityStateChangeListener listener) { - super(context, listener); - } - - @Override - void start() { - if (DEBUG) { - Log.d(TAG, "Start"); - } - - // Register AppOpsManager callback - mContext.getSystemService(AppOpsManager.class) - .startWatchingActive( - new String[]{AppOpsManager.OPSTR_RECORD_AUDIO}, - mContext.getMainExecutor(), - this); - } - - @Override - void stop() { - if (DEBUG) { - Log.d(TAG, "Stop"); - } - - // Unregister AppOpsManager callback - mContext.getSystemService(AppOpsManager.class).stopWatchingActive(this); - - // Clean up state - mActiveAudioRecordingPackages.clear(); - } - - @UiThread - @Override - Set<String> getActivePackages() { - return mActiveAudioRecordingPackages; - } - - @UiThread - @Override - public void onOpActiveChanged(String op, int uid, String packageName, boolean active) { - if (DEBUG) { - Log.d(TAG, - "OP_RECORD_AUDIO active change, active=" + active + ", package=" - + packageName); - } - - if (active) { - if (mActiveAudioRecordingPackages.add(packageName)) { - mListener.onAudioActivityStateChange(true, packageName); - } - } else { - if (mActiveAudioRecordingPackages.remove(packageName)) { - mListener.onAudioActivityStateChange(false, packageName); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java index 0a3e83326e01..bbb2f1a5259a 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java @@ -94,6 +94,7 @@ public class ThemeOverlayApplier implements Dumpable { */ static final List<String> THEME_CATEGORIES = Lists.newArrayList( OVERLAY_CATEGORY_SYSTEM_PALETTE, + OVERLAY_CATEGORY_NEUTRAL_PALETTE, OVERLAY_CATEGORY_ICON_LAUNCHER, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_FONT, @@ -107,6 +108,7 @@ public class ThemeOverlayApplier implements Dumpable { @VisibleForTesting static final Set<String> SYSTEM_USER_CATEGORIES = Sets.newHashSet( OVERLAY_CATEGORY_SYSTEM_PALETTE, + OVERLAY_CATEGORY_NEUTRAL_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, @@ -129,8 +131,9 @@ public class ThemeOverlayApplier implements Dumpable { mLauncherPackage = launcherPackage; mThemePickerPackage = themePickerPackage; mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet( - OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR, - OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID)); + OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_NEUTRAL_PALETTE, + OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, + OVERLAY_CATEGORY_ICON_ANDROID)); mTargetPackageToCategories.put(SYSUI_PACKAGE, Sets.newHashSet(OVERLAY_CATEGORY_ICON_SYSUI)); mTargetPackageToCategories.put(SETTINGS_PACKAGE, @@ -158,10 +161,10 @@ public class ThemeOverlayApplier implements Dumpable { void applyCurrentUserOverlays( Map<String, OverlayIdentifier> categoryToPackage, FabricatedOverlay[] pendingCreation, - Set<UserHandle> userHandles) { + int currentUser, + Set<UserHandle> managedProfiles) { // Disable all overlays that have not been specified in the user setting. final Set<String> overlayCategoriesToDisable = new HashSet<>(THEME_CATEGORIES); - overlayCategoriesToDisable.removeAll(categoryToPackage.keySet()); final Set<String> targetPackagesToQuery = overlayCategoriesToDisable.stream() .map(category -> mCategoryToTargetPackage.get(category)) .collect(Collectors.toSet()); @@ -172,6 +175,7 @@ public class ThemeOverlayApplier implements Dumpable { .filter(o -> mTargetPackageToCategories.get(o.targetPackageName).contains(o.category)) .filter(o -> overlayCategoriesToDisable.contains(o.category)) + .filter(o -> !categoryToPackage.containsValue(new OverlayIdentifier(o.packageName))) .filter(o -> o.isEnabled()) .map(o -> new Pair<>(o.category, o.packageName)) .collect(Collectors.toList()); @@ -183,17 +187,18 @@ public class ThemeOverlayApplier implements Dumpable { } } - // Toggle overlays in the order of THEME_CATEGORIES. + for (Pair<String, String> packageToDisable : overlaysToDisable) { + OverlayIdentifier overlayInfo = new OverlayIdentifier(packageToDisable.second); + setEnabled(transaction, overlayInfo, packageToDisable.first, currentUser, + managedProfiles, false); + } + for (String category : THEME_CATEGORIES) { if (categoryToPackage.containsKey(category)) { OverlayIdentifier overlayInfo = categoryToPackage.get(category); - setEnabled(transaction, overlayInfo, category, userHandles, true); + setEnabled(transaction, overlayInfo, category, currentUser, managedProfiles, true); } } - for (Pair<String, String> packageToDisable : overlaysToDisable) { - OverlayIdentifier overlayInfo = new OverlayIdentifier(packageToDisable.second); - setEnabled(transaction, overlayInfo, packageToDisable.first, userHandles, false); - } mExecutor.execute(() -> { try { @@ -210,18 +215,30 @@ public class ThemeOverlayApplier implements Dumpable { } private void setEnabled(OverlayManagerTransaction.Builder transaction, - OverlayIdentifier identifier, String category, Set<UserHandle> handles, - boolean enabled) { + OverlayIdentifier identifier, String category, int currentUser, + Set<UserHandle> managedProfiles, boolean enabled) { if (DEBUG) { Log.d(TAG, "setEnabled: " + identifier.getPackageName() + " category: " + category + ": " + enabled); } - for (UserHandle userHandle : handles) { - transaction.setEnabled(identifier, enabled, userHandle.getIdentifier()); - } - if (!handles.contains(UserHandle.SYSTEM) && SYSTEM_USER_CATEGORIES.contains(category)) { + + transaction.setEnabled(identifier, enabled, currentUser); + if (currentUser != UserHandle.SYSTEM.getIdentifier() + && SYSTEM_USER_CATEGORIES.contains(category)) { transaction.setEnabled(identifier, enabled, UserHandle.SYSTEM.getIdentifier()); } + + // Do not apply Launcher or Theme picker overlays to managed users. Apps are not + // installed in there. + OverlayInfo overlayInfo = mOverlayManager.getOverlayInfo(identifier, UserHandle.SYSTEM); + if (overlayInfo == null || overlayInfo.targetPackageName.equals(mLauncherPackage) + || overlayInfo.targetPackageName.equals(mThemePickerPackage)) { + return; + } + + for (UserHandle userHandle : managedProfiles) { + transaction.setEnabled(identifier, enabled, userHandle.getIdentifier()); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 1f222d80f014..5d028454a417 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -55,14 +55,13 @@ import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; -import com.google.android.collect.Sets; - import org.json.JSONException; import org.json.JSONObject; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Collection; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; @@ -86,7 +85,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { protected static final int PRIMARY = 0; protected static final int SECONDARY = 1; - protected static final int NEUTRAL = 1; + protected static final int NEUTRAL = 2; // If lock screen wallpaper colors should also be considered when selecting the theme. // Doing this has performance impact, given that overlays would need to be swapped when @@ -151,7 +150,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added."); - updateThemeOverlays(); + reevaluateSystemTheme(true /* forceReload */); } }, filter, mBgExecutor, UserHandle.ALL); mSecureSettings.registerContentObserverForUser( @@ -163,7 +162,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { int userId) { if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId); if (ActivityManager.getCurrentUser() == userId) { - updateThemeOverlays(); + reevaluateSystemTheme(true /* forceReload */); } } }, @@ -180,7 +179,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { mLockColors = lockColors; } mSystemColors = systemColor; - reevaluateSystemTheme(); + reevaluateSystemTheme(false /* forceReload */); }); }); if (USE_LOCK_SCREEN_WALLPAPER) { @@ -192,7 +191,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { } // It's possible that the user has a lock screen wallpaper. On this case we'll // end up with different colors after unlocking. - reevaluateSystemTheme(); + reevaluateSystemTheme(false /* forceReload */); } }); } @@ -209,11 +208,11 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { Log.d(TAG, "got new lock colors: " + wallpaperColors + " where: " + which); } } - reevaluateSystemTheme(); + reevaluateSystemTheme(false /* forceReload */); }, null, UserHandle.USER_ALL); } - private void reevaluateSystemTheme() { + private void reevaluateSystemTheme(boolean forceReload) { WallpaperColors currentColors = mKeyguardStateController.isShowing() && mLockColors != null ? mLockColors : mSystemColors; @@ -228,7 +227,8 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { accentCandidate = getAccentColor(currentColors); } - if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate) { + if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate + && !forceReload) { return; } @@ -309,6 +309,16 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { } catch (NumberFormatException e) { Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName()); } + } else if (!mIsMonetEnabled && systemPalette != null) { + try { + // It's possible that we flipped the flag off and still have a @ColorInt in the + // setting. We need to sanitize the input, otherwise the overlay transaction will + // fail. + Integer.parseInt(systemPalette.getPackageName().toLowerCase(), 16); + categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE); + } catch (NumberFormatException e) { + // This is a package name. All good, let's continue + } } // Same for accent color. @@ -322,6 +332,13 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { } catch (NumberFormatException e) { Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName()); } + } else if (!mIsMonetEnabled && accentPalette != null) { + try { + Integer.parseInt(accentPalette.getPackageName().toLowerCase(), 16); + categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR); + } catch (NumberFormatException e) { + // This is a package name. All good, let's continue + } } // Compatibility with legacy themes, where full packages were defined, instead of just @@ -337,10 +354,10 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, mSecondaryOverlay.getIdentifier()); } - Set<UserHandle> userHandles = Sets.newHashSet(UserHandle.of(currentUser)); + Set<UserHandle> managedProfiles = new HashSet<>(); for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) { if (userInfo.isManagedProfile()) { - userHandles.add(userInfo.getUserHandle()); + managedProfiles.add(userInfo.getUserHandle()); } } if (DEBUG) { @@ -352,9 +369,10 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { mNeedsOverlayCreation = false; mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] { mPrimaryOverlay, mSecondaryOverlay, mNeutralOverlay - }, userHandles); + }, currentUser, managedProfiles); } else { - mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, userHandles); + mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser, + managedProfiles); } } diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java index 365cd2a5d20b..fab1655b1262 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java @@ -187,10 +187,7 @@ public class SystemUIToast implements ToastPlugin.Toast { mPluginToast.onOrientationChange(orientation); } - mDefaultY = mContext.getResources().getDimensionPixelSize( - mToastStyleEnabled - ? com.android.systemui.R.dimen.toast_y_offset - : R.dimen.toast_y_offset); + mDefaultY = mContext.getResources().getDimensionPixelSize(R.dimen.toast_y_offset); mDefaultGravity = mContext.getResources().getInteger(R.integer.config_toastDefaultGravity); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java index 78341edefbb2..5b66216f41be 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java @@ -43,11 +43,13 @@ public class TunerActivity extends Activity implements private static final String TAG_TUNER = "tuner"; private final DemoModeController mDemoModeController; + private final TunerService mTunerService; @Inject - TunerActivity(DemoModeController demoModeController) { + TunerActivity(DemoModeController demoModeController, TunerService tunerService) { super(); mDemoModeController = demoModeController; + mTunerService = tunerService; } protected void onCreate(Bundle savedInstanceState) { @@ -67,7 +69,7 @@ public class TunerActivity extends Activity implements "com.android.settings.action.DEMO_MODE"); final PreferenceFragment fragment = showDemoMode ? new DemoModeFragment(mDemoModeController) - : new TunerFragment(); + : new TunerFragment(mTunerService); getFragmentManager().beginTransaction().replace(R.id.content_frame, fragment, TAG_TUNER).commit(); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java index 4c724aeea9ae..989462a9fd34 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java @@ -15,7 +15,7 @@ */ package com.android.systemui.tuner; -import android.app.ActivityManager; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; @@ -23,7 +23,6 @@ import android.content.DialogInterface; import android.hardware.display.AmbientDisplayConfiguration; import android.os.Build; import android.os.Bundle; -import android.os.UserHandle; import android.provider.Settings; import android.view.Menu; import android.view.MenuInflater; @@ -56,6 +55,15 @@ public class TunerFragment extends PreferenceFragment { private static final int MENU_REMOVE = Menu.FIRST + 1; + private final TunerService mTunerService; + + // We are the only ones who ever call this constructor, so don't worry about the warning + @SuppressLint("ValidFragment") + public TunerFragment(TunerService tunerService) { + super(); + mTunerService = tunerService; + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -124,13 +132,9 @@ public class TunerFragment extends PreferenceFragment { getActivity().finish(); return true; case MENU_REMOVE: - UserHandle user = new UserHandle(ActivityManager.getCurrentUser()); - TunerService.showResetRequest(getContext(), user, new Runnable() { - @Override - public void run() { - if (getActivity() != null) { - getActivity().finish(); - } + mTunerService.showResetRequest(() -> { + if (getActivity() != null) { + getActivity().finish(); } }); return true; diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java index b67574d1c4de..5d09e064604a 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java @@ -15,19 +15,10 @@ package com.android.systemui.tuner; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.UserHandle; -import android.provider.Settings; import com.android.systemui.Dependency; -import com.android.systemui.R; -import com.android.systemui.statusbar.phone.SystemUIDialog; public abstract class TunerService { @@ -47,6 +38,16 @@ public abstract class TunerService { public abstract void addTunable(Tunable tunable, String... keys); public abstract void removeTunable(Tunable tunable); + /** + * Sets the state of the {@link TunerActivity} component for the current user + */ + public abstract void setTunerEnabled(boolean enabled); + + /** + * Returns true if the tuner is enabled for the current user. + */ + public abstract boolean isTunerEnabled(); + public interface Tunable { void onTuningChanged(String key, String newValue); } @@ -55,38 +56,6 @@ public abstract class TunerService { mContext = context; } - private static Context userContext(Context context, UserHandle user) { - try { - return context.createPackageContextAsUser(context.getPackageName(), 0, user); - } catch (NameNotFoundException e) { - return context; - } - } - - /** Enables or disables the tuner for the supplied user. */ - public void setTunerEnabled(UserHandle user, boolean enabled) { - setTunerEnabled(mContext, user, enabled); - } - - public static final void setTunerEnabled(Context context, UserHandle user, boolean enabled) { - userContext(context, user).getPackageManager().setComponentEnabledSetting( - new ComponentName(context, TunerActivity.class), - enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED - : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, - PackageManager.DONT_KILL_APP); - } - - /** Returns true if the tuner is enabled for the supplied user. */ - public boolean isTunerEnabled(UserHandle user) { - return isTunerEnabled(mContext, user); - } - - public static final boolean isTunerEnabled(Context context, UserHandle user) { - return userContext(context, user).getPackageManager().getComponentEnabledSetting( - new ComponentName(context, TunerActivity.class)) - == PackageManager.COMPONENT_ENABLED_STATE_ENABLED; - } - public static class ClearReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -97,35 +66,7 @@ public abstract class TunerService { } /** */ - public void showResetRequest(UserHandle user, final Runnable onDisabled) { - showResetRequest(mContext, user, onDisabled); - } - - public static final void showResetRequest(final Context context, UserHandle user, - final Runnable onDisabled) { - SystemUIDialog dialog = new SystemUIDialog(context); - dialog.setShowForAllUsers(true); - dialog.setMessage(R.string.remove_from_settings_prompt); - dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.cancel), - (OnClickListener) null); - dialog.setButton(DialogInterface.BUTTON_POSITIVE, - context.getString(R.string.guest_exit_guest_dialog_remove), new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // Tell the tuner (in main SysUI process) to clear all its settings. - context.sendBroadcast(new Intent(TunerService.ACTION_CLEAR)); - // Disable access to tuner. - TunerService.setTunerEnabled(context, user, false); - // Make them sit through the warning dialog again. - Settings.Secure.putInt(context.getContentResolver(), - TunerFragment.SETTING_SEEN_TUNER_WARNING, 0); - if (onDisabled != null) { - onDisabled.run(); - } - } - }); - dialog.show(); - } + public abstract void showResetRequest(Runnable onDisabled); public static boolean parseIntegerSwitch(String value, boolean defaultValue) { try { diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 027c282ba352..e9e4380859b7 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -15,8 +15,12 @@ */ package com.android.systemui.tuner; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.net.Uri; @@ -32,13 +36,14 @@ import android.util.ArraySet; import com.android.internal.util.ArrayUtils; import com.android.systemui.DejankUtils; -import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.qs.QSTileHost; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.util.leak.LeakDetector; import java.util.HashSet; @@ -83,6 +88,7 @@ public class TunerServiceImpl extends TunerService { private int mCurrentUser; private UserTracker.Callback mCurrentUserTracker; private UserTracker mUserTracker; + private final ComponentName mTunerComponent; /** */ @@ -92,7 +98,6 @@ public class TunerServiceImpl extends TunerService { @Main Handler mainHandler, LeakDetector leakDetector, DemoModeController demoModeController, - BroadcastDispatcher broadcastDispatcher, UserTracker userTracker) { super(context); mContext = context; @@ -100,6 +105,7 @@ public class TunerServiceImpl extends TunerService { mLeakDetector = leakDetector; mDemoModeController = demoModeController; mUserTracker = userTracker; + mTunerComponent = new ComponentName(mContext, TunerActivity.class); for (UserInfo user : UserManager.get(mContext).getUsers()) { mCurrentUser = user.getUserHandle().getIdentifier(); @@ -142,7 +148,7 @@ public class TunerServiceImpl extends TunerService { } } if (oldVersion < 2) { - setTunerEnabled(mContext, mUserTracker.getUserHandle(), false); + setTunerEnabled(false); } // 3 Removed because of a revert. if (oldVersion < 4) { @@ -269,6 +275,46 @@ public class TunerServiceImpl extends TunerService { } } + + @Override + public void setTunerEnabled(boolean enabled) { + mUserTracker.getUserContext().getPackageManager().setComponentEnabledSetting( + mTunerComponent, + enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED + : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP + ); + } + + @Override + public boolean isTunerEnabled() { + return mUserTracker.getUserContext().getPackageManager().getComponentEnabledSetting( + mTunerComponent) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + } + + @Override + public void showResetRequest(Runnable onDisabled) { + SystemUIDialog dialog = new SystemUIDialog(mContext); + dialog.setShowForAllUsers(true); + dialog.setMessage(R.string.remove_from_settings_prompt); + dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), + (DialogInterface.OnClickListener) null); + dialog.setButton(DialogInterface.BUTTON_POSITIVE, + mContext.getString(R.string.guest_exit_guest_dialog_remove), (d, which) -> { + // Tell the tuner (in main SysUI process) to clear all its settings. + mContext.sendBroadcast(new Intent(TunerService.ACTION_CLEAR)); + // Disable access to tuner. + setTunerEnabled(false); + // Make them sit through the warning dialog again. + Secure.putInt(mContext.getContentResolver(), + TunerFragment.SETTING_SEEN_TUNER_WARNING, 0); + if (onDisabled != null) { + onDisabled.run(); + } + }); + dialog.show(); + } + private class Observer extends ContentObserver { public Observer() { super(new Handler(Looper.getMainLooper())); diff --git a/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java b/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java index 79a197d9d409..a22793b05070 100644 --- a/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/util/AlphaTintDrawableWrapper.java @@ -16,15 +16,18 @@ package com.android.systemui.util; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.graphics.drawable.DrawableWrapper; +import android.graphics.drawable.InsetDrawable; import android.util.AttributeSet; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.systemui.R; import org.xmlpull.v1.XmlPullParser; @@ -45,13 +48,18 @@ import java.io.IOException; * @attr ref R.styleable#AlphaTintDrawableWrapper_tint * @attr ref R.styleable#AlphaTintDrawableWrapper_alpha */ -public class AlphaTintDrawableWrapper extends DrawableWrapper { +public class AlphaTintDrawableWrapper extends InsetDrawable { private ColorStateList mTint; private int[] mThemeAttrs; /** No-arg constructor used by drawable inflation. */ public AlphaTintDrawableWrapper() { - super(null); + super(null, 0); + } + + AlphaTintDrawableWrapper(Drawable drawable, int[] themeAttrs) { + super(drawable, 0); + mThemeAttrs = themeAttrs; } @Override @@ -74,7 +82,7 @@ public class AlphaTintDrawableWrapper extends DrawableWrapper { public void applyTheme(Theme t) { super.applyTheme(t); - if (mThemeAttrs != null) { + if (mThemeAttrs != null && t != null) { final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.AlphaTintDrawableWrapper); updateStateFromTypedArray(a); @@ -92,9 +100,6 @@ public class AlphaTintDrawableWrapper extends DrawableWrapper { } private void updateStateFromTypedArray(@NonNull TypedArray a) { - if (a.hasValue(R.styleable.AlphaTintDrawableWrapper_android_drawable)) { - setDrawable(a.getDrawable(R.styleable.AlphaTintDrawableWrapper_android_drawable)); - } if (a.hasValue(R.styleable.AlphaTintDrawableWrapper_android_tint)) { mTint = a.getColorStateList(R.styleable.AlphaTintDrawableWrapper_android_tint); } @@ -109,4 +114,57 @@ public class AlphaTintDrawableWrapper extends DrawableWrapper { getDrawable().mutate().setTintList(mTint); } } + + @Nullable + @Override + public ConstantState getConstantState() { + return new AlphaTintState(super.getConstantState(), mThemeAttrs, getAlpha(), mTint); + } + + static class AlphaTintState extends Drawable.ConstantState { + + private ConstantState mWrappedState; + private int[] mThemeAttrs; + private int mAlpha; + private ColorStateList mColorStateList; + + AlphaTintState( + ConstantState wrappedState, + int[] themeAttrs, + int alpha, + ColorStateList colorStateList + ) { + mWrappedState = wrappedState; + mThemeAttrs = themeAttrs; + mAlpha = alpha; + mColorStateList = colorStateList; + } + + @NonNull + @Override + public Drawable newDrawable() { + return newDrawable(null, null); + } + + @NonNull + @Override + public Drawable newDrawable(Resources res, Theme theme) { + DrawableWrapper wrapper = (DrawableWrapper) mWrappedState.newDrawable(res, theme); + AlphaTintDrawableWrapper alphaTintDrawableWrapper = + new AlphaTintDrawableWrapper(wrapper.getDrawable(), mThemeAttrs); + alphaTintDrawableWrapper.setTintList(mColorStateList); + alphaTintDrawableWrapper.setAlpha(mAlpha); + return alphaTintDrawableWrapper; + } + + @Override + public boolean canApplyTheme() { + return true; + } + + @Override + public int getChangingConfigurations() { + return mWrappedState.getChangingConfigurations(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt index 1af2c9f46373..6aadd1020bce 100644 --- a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt @@ -17,15 +17,12 @@ package com.android.systemui.util import android.content.res.Resources -import android.content.res.TypedArray import android.graphics.Canvas import android.graphics.Path import android.graphics.Rect import android.graphics.drawable.Drawable import android.graphics.drawable.DrawableWrapper -import android.util.AttributeSet -import com.android.systemui.R -import org.xmlpull.v1.XmlPullParser +import android.graphics.drawable.InsetDrawable /** * [DrawableWrapper] to use in the progress of a slider. @@ -38,9 +35,9 @@ import org.xmlpull.v1.XmlPullParser * is meant to be smaller than the rounded corner. The background should have rounded corners that * are half of the height. */ -class RoundedCornerProgressDrawable(drawable: Drawable?) : DrawableWrapper(drawable) { - - constructor() : this(null) +class RoundedCornerProgressDrawable @JvmOverloads constructor( + drawable: Drawable? = null +) : InsetDrawable(drawable, 0) { companion object { private const val MAX_LEVEL = 10000 // Taken from Drawable @@ -52,35 +49,11 @@ class RoundedCornerProgressDrawable(drawable: Drawable?) : DrawableWrapper(drawa setClipPath(Rect()) } - override fun inflate( - r: Resources, - parser: XmlPullParser, - attrs: AttributeSet, - theme: Resources.Theme? - ) { - val a = obtainAttributes(r, theme, attrs, R.styleable.RoundedCornerProgressDrawable) - - // Inflation will advance the XmlPullParser and AttributeSet. - super.inflate(r, parser, attrs, theme) - - updateStateFromTypedArray(a) - if (drawable == null) { - throw IllegalStateException("${this::class.java.simpleName} needs a drawable") - } - a.recycle() - } - override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean { onLevelChange(level) return super.onLayoutDirectionChanged(layoutDirection) } - private fun updateStateFromTypedArray(a: TypedArray) { - if (a.hasValue(R.styleable.RoundedCornerProgressDrawable_android_drawable)) { - setDrawable(a.getDrawable(R.styleable.RoundedCornerProgressDrawable_android_drawable)) - } - } - override fun onBoundsChange(bounds: Rect) { setClipPath(bounds) super.onBoundsChange(bounds) @@ -115,4 +88,24 @@ class RoundedCornerProgressDrawable(drawable: Drawable?) : DrawableWrapper(drawa super.draw(canvas) canvas.restore() } + + override fun getConstantState(): ConstantState? { + // This should not be null as it was created with a state in the constructor. + return RoundedCornerState(super.getConstantState()!!) + } + + private class RoundedCornerState(private val wrappedState: ConstantState) : ConstantState() { + override fun newDrawable(): Drawable { + return newDrawable(null, null) + } + + override fun newDrawable(res: Resources?, theme: Resources.Theme?): Drawable { + val wrapper = wrappedState.newDrawable(res, theme) as DrawableWrapper + return RoundedCornerProgressDrawable(wrapper.drawable) + } + + override fun getChangingConfigurations(): Int { + return wrappedState.changingConfigurations + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index 06806d0e6ab6..6a648bdf8cd4 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -347,6 +347,7 @@ public class ProximitySensor implements ThresholdSensor { public void check(long timeoutMs, Consumer<Boolean> callback) { if (!mSensor.isLoaded()) { callback.accept(null); + return; } mCallbacks.add(callback); if (!mRegistered.getAndSet(true)) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index df54eabca8e7..25345d5c4b4c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -33,7 +33,11 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.Dialog; @@ -51,6 +55,9 @@ import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Region; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.RotateDrawable; import android.media.AudioManager; import android.media.AudioSystem; import android.os.Debug; @@ -81,6 +88,8 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; @@ -88,6 +97,7 @@ import android.widget.Toast; import com.android.settingslib.Utils; import com.android.systemui.Dependency; +import com.android.systemui.Interpolators; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.media.dialog.MediaOutputDialogFactory; @@ -99,6 +109,8 @@ import com.android.systemui.plugins.VolumeDialogController.StreamState; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.util.AlphaTintDrawableWrapper; +import com.android.systemui.util.RoundedCornerProgressDrawable; import java.io.PrintWriter; import java.util.ArrayList; @@ -124,8 +136,14 @@ public class VolumeDialogImpl implements VolumeDialog, static final int DIALOG_ODI_CAPTIONS_TOOLTIP_TIMEOUT_MILLIS = 5000; static final int DIALOG_HOVERING_TIMEOUT_MILLIS = 16000; + private static final int DRAWER_ANIMATION_DURATION_SHORT = 175; + private static final int DRAWER_ANIMATION_DURATION = 250; + private final int mDialogShowAnimationDurationMs; private final int mDialogHideAnimationDurationMs; + private final int mRingerDrawerItemSize; + private final boolean mShowVibrate; + private final int mRingerCount; private final boolean mShowLowMediaVolumeIcon; private final boolean mChangeVolumeRowTintWhenInactive; @@ -140,6 +158,30 @@ public class VolumeDialogImpl implements VolumeDialog, private ViewGroup mDialogView; private ViewGroup mDialogRowsView; private ViewGroup mRinger; + + private ViewGroup mSelectedRingerContainer; + private ImageView mSelectedRingerIcon; + + private ViewGroup mRingerDrawerContainer; + private ViewGroup mRingerDrawerMute; + private ViewGroup mRingerDrawerVibrate; + private ViewGroup mRingerDrawerNormal; + private ImageView mRingerDrawerMuteIcon; + private ImageView mRingerDrawerVibrateIcon; + private ImageView mRingerDrawerNormalIcon; + + /** + * View that draws the 'selected' background behind one of the three ringer choices in the + * drawer. + */ + private ViewGroup mRingerDrawerNewSelectionBg; + + private final ValueAnimator mRingerDrawerIconColorAnimator = ValueAnimator.ofFloat(0f, 1f); + private ImageView mRingerDrawerIconAnimatingSelected; + private ImageView mRingerDrawerIconAnimatingDeselected; + + private boolean mIsRingerDrawerOpen = false; + private ImageButton mRingerIcon; private ViewGroup mODICaptionsView; private CaptionsToggleImageButton mODICaptionsIcon; @@ -191,6 +233,12 @@ public class VolumeDialogImpl implements VolumeDialog, mContext.getResources().getInteger(R.integer.config_dialogShowAnimationDurationMs); mDialogHideAnimationDurationMs = mContext.getResources().getInteger(R.integer.config_dialogHideAnimationDurationMs); + mRingerDrawerItemSize = mContext.getResources().getDimensionPixelSize( + R.dimen.volume_ringer_drawer_item_size); + mShowVibrate = mController.hasVibrator(); + + // Normal, mute, and possibly vibrate. + mRingerCount = mShowVibrate ? 3 : 2; } @Override @@ -314,6 +362,20 @@ public class VolumeDialogImpl implements VolumeDialog, mZenIcon = mRinger.findViewById(R.id.dnd_icon); } + mSelectedRingerIcon = mDialog.findViewById(R.id.volume_new_ringer_active_icon); + mSelectedRingerContainer = mDialog.findViewById( + R.id.volume_new_ringer_active_icon_container); + + mRingerDrawerMute = mDialog.findViewById(R.id.volume_drawer_mute); + mRingerDrawerNormal = mDialog.findViewById(R.id.volume_drawer_normal); + mRingerDrawerVibrate = mDialog.findViewById(R.id.volume_drawer_vibrate); + mRingerDrawerMuteIcon = mDialog.findViewById(R.id.volume_drawer_mute_icon); + mRingerDrawerVibrateIcon = mDialog.findViewById(R.id.volume_drawer_vibrate_icon); + mRingerDrawerNormalIcon = mDialog.findViewById(R.id.volume_drawer_normal_icon); + mRingerDrawerNewSelectionBg = mDialog.findViewById(R.id.volume_drawer_selection_background); + + setupRingerDrawer(); + mODICaptionsView = mDialog.findViewById(R.id.odi_captions); if (mODICaptionsView != null) { mODICaptionsIcon = mODICaptionsView.findViewById(R.id.odi_captions_icon); @@ -475,38 +537,273 @@ public class VolumeDialogImpl implements VolumeDialog, row.anim = null; + final LayerDrawable seekbarDrawable = + (LayerDrawable) mContext.getDrawable(R.drawable.volume_row_seekbar); + + final LayerDrawable seekbarBgDrawable = + (LayerDrawable) seekbarDrawable.findDrawableByLayerId(android.R.id.background); + + row.sliderBgSolid = seekbarBgDrawable.findDrawableByLayerId( + R.id.volume_seekbar_background_solid); + + row.sliderBgIcon = (AlphaTintDrawableWrapper) + ((RotateDrawable) seekbarBgDrawable.findDrawableByLayerId( + R.id.volume_seekbar_background_icon)).getDrawable(); + + final LayerDrawable seekbarProgressDrawable = (LayerDrawable) + ((RoundedCornerProgressDrawable) seekbarDrawable.findDrawableByLayerId( + android.R.id.progress)).getDrawable(); + + row.sliderProgressSolid = seekbarProgressDrawable.findDrawableByLayerId( + R.id.volume_seekbar_progress_solid); + + row.sliderProgressIcon = (AlphaTintDrawableWrapper) + ((RotateDrawable) seekbarProgressDrawable.findDrawableByLayerId( + R.id.volume_seekbar_progress_icon)).getDrawable(); + + row.slider.setProgressDrawable(seekbarDrawable); + row.slider.setThumb(null); + row.icon = row.view.findViewById(R.id.volume_row_icon); - row.icon.setImageResource(iconRes); - if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) { - row.icon.setOnClickListener(v -> { - Events.writeEvent(Events.EVENT_ICON_CLICK, row.stream, row.iconState); - mController.setActiveStream(row.stream); - if (row.stream == AudioManager.STREAM_RING) { - final boolean hasVibrator = mController.hasVibrator(); - if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) { - if (hasVibrator) { - mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false); + + row.setIcon(iconRes, mContext.getTheme()); + + if (row.icon != null) { + if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) { + row.icon.setOnClickListener(v -> { + Events.writeEvent(Events.EVENT_ICON_CLICK, row.stream, row.iconState); + mController.setActiveStream(row.stream); + if (row.stream == AudioManager.STREAM_RING) { + final boolean hasVibrator = mController.hasVibrator(); + if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) { + if (hasVibrator) { + mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false); + } else { + final boolean wasZero = row.ss.level == 0; + mController.setStreamVolume(stream, + wasZero ? row.lastAudibleLevel : 0); + } } else { - final boolean wasZero = row.ss.level == 0; - mController.setStreamVolume(stream, - wasZero ? row.lastAudibleLevel : 0); + mController.setRingerMode( + AudioManager.RINGER_MODE_NORMAL, false); + if (row.ss.level == 0) { + mController.setStreamVolume(stream, 1); + } } } else { - mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false); - if (row.ss.level == 0) { - mController.setStreamVolume(stream, 1); - } + final boolean vmute = row.ss.level == row.ss.levelMin; + mController.setStreamVolume(stream, + vmute ? row.lastAudibleLevel : row.ss.levelMin); } - } else { - final boolean vmute = row.ss.level == row.ss.levelMin; - mController.setStreamVolume(stream, - vmute ? row.lastAudibleLevel : row.ss.levelMin); - } - row.userAttempt = 0; // reset the grace period, slider updates immediately - }); + row.userAttempt = 0; // reset the grace period, slider updates immediately + }); + } else { + row.icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + } + } + } + + private void setRingerMode(int newRingerMode) { + Events.writeEvent(Events.EVENT_RINGER_TOGGLE, newRingerMode); + incrementManualToggleCount(); + updateRingerH(); + provideTouchFeedbackH(newRingerMode); + mController.setRingerMode(newRingerMode, false); + maybeShowToastH(newRingerMode); + } + + private void setupRingerDrawer() { + mRingerDrawerContainer = mDialog.findViewById(R.id.volume_drawer_container); + + if (mRingerDrawerContainer == null) { + return; + } + + if (!mShowVibrate) { + mRingerDrawerVibrate.setVisibility(GONE); + } + + // In portrait, add padding to the bottom to account for the height of the open ringer + // drawer. + if (!isLandscape()) { + mDialogView.setPadding( + mDialogView.getPaddingLeft(), + mDialogView.getPaddingTop(), + mDialogView.getPaddingRight(), + mDialogView.getPaddingBottom() + (mRingerCount - 1) * mRingerDrawerItemSize); + } else { + mDialogView.setPadding( + mDialogView.getPaddingLeft() + (mRingerCount - 1) * mRingerDrawerItemSize, + mDialogView.getPaddingTop(), + mDialogView.getPaddingRight(), + mDialogView.getPaddingBottom()); + } + + ((LinearLayout) mRingerDrawerContainer.findViewById(R.id.volume_drawer_options)) + .setOrientation(isLandscape() ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL); + + mSelectedRingerContainer.setOnClickListener(view -> { + if (mIsRingerDrawerOpen) { + hideRingerDrawer(); + } else { + showRingerDrawer(); + } + }); + + mRingerDrawerVibrate.setOnClickListener( + new RingerDrawerItemClickListener(RINGER_MODE_VIBRATE)); + mRingerDrawerMute.setOnClickListener( + new RingerDrawerItemClickListener(RINGER_MODE_SILENT)); + mRingerDrawerNormal.setOnClickListener( + new RingerDrawerItemClickListener(RINGER_MODE_NORMAL)); + + final int unselectedColor = Utils.getColorAccentDefaultColor(mContext); + final int selectedColor = Utils.getColorAttrDefaultColor( + mContext, android.R.attr.colorBackgroundFloating); + + // Add an update listener that animates the deselected icon to the unselected color, and the + // selected icon to the selected color. + mRingerDrawerIconColorAnimator.addUpdateListener( + anim -> { + final float currentValue = (float) anim.getAnimatedValue(); + final int curUnselectedColor = (int) ArgbEvaluator.getInstance().evaluate( + currentValue, selectedColor, unselectedColor); + final int curSelectedColor = (int) ArgbEvaluator.getInstance().evaluate( + currentValue, unselectedColor, selectedColor); + + mRingerDrawerIconAnimatingDeselected.setColorFilter(curUnselectedColor); + mRingerDrawerIconAnimatingSelected.setColorFilter(curSelectedColor); + }); + mRingerDrawerIconColorAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mRingerDrawerIconAnimatingDeselected.clearColorFilter(); + mRingerDrawerIconAnimatingSelected.clearColorFilter(); + } + }); + mRingerDrawerIconColorAnimator.setDuration(DRAWER_ANIMATION_DURATION_SHORT); + } + + private ImageView getDrawerIconViewForMode(int mode) { + if (mode == RINGER_MODE_VIBRATE) { + return mRingerDrawerVibrateIcon; + } else if (mode == RINGER_MODE_SILENT) { + return mRingerDrawerMuteIcon; + } else { + return mRingerDrawerNormalIcon; + } + } + + /** + * Translation to apply form the origin (either top or left) to overlap the selection background + * with the given mode in the drawer. + */ + private float getTranslationInDrawerForRingerMode(int mode) { + return mode == RINGER_MODE_VIBRATE + ? -mRingerDrawerItemSize * 2 + : mode == RINGER_MODE_SILENT + ? -mRingerDrawerItemSize + : 0; + } + + /** Animates in the ringer drawer. */ + private void showRingerDrawer() { + // Show all ringer icons except the currently selected one, since we're going to animate the + // ringer button to that position. + mRingerDrawerVibrateIcon.setVisibility( + mState.ringerModeInternal == RINGER_MODE_VIBRATE ? INVISIBLE : VISIBLE); + mRingerDrawerMuteIcon.setVisibility( + mState.ringerModeInternal == RINGER_MODE_SILENT ? INVISIBLE : VISIBLE); + mRingerDrawerNormalIcon.setVisibility( + mState.ringerModeInternal == RINGER_MODE_NORMAL ? INVISIBLE : VISIBLE); + + // Hide the selection background - we use this to show a selection when one is + // tapped, so it should be invisible until that happens. However, position it below + // the currently selected ringer so that it's ready to animate. + mRingerDrawerNewSelectionBg.setAlpha(0f); + + if (!isLandscape()) { + mRingerDrawerNewSelectionBg.setTranslationY( + getTranslationInDrawerForRingerMode(mState.ringerModeInternal)); + } else { + mRingerDrawerNewSelectionBg.setTranslationX( + getTranslationInDrawerForRingerMode(mState.ringerModeInternal)); + } + + // Move the drawer so that the top/rightmost ringer choice overlaps with the selected ringer + // icon. + if (!isLandscape()) { + mRingerDrawerContainer.setTranslationY(mRingerDrawerItemSize * (mRingerCount - 1)); + } else { + mRingerDrawerContainer.setTranslationX(mRingerDrawerItemSize * (mRingerCount - 1)); + } + mRingerDrawerContainer.setAlpha(0f); + mRingerDrawerContainer.setVisibility(VISIBLE); + + // Animate the drawer up and visible. + mRingerDrawerContainer.animate() + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + // Vibrate is way farther up, so give the selected ringer icon a head start if + // vibrate is selected. + .setDuration(mState.ringerModeInternal == RINGER_MODE_VIBRATE + ? DRAWER_ANIMATION_DURATION_SHORT + : DRAWER_ANIMATION_DURATION) + .setStartDelay(mState.ringerModeInternal == RINGER_MODE_VIBRATE + ? DRAWER_ANIMATION_DURATION - DRAWER_ANIMATION_DURATION_SHORT + : 0) + .alpha(1f) + .translationX(0f) + .translationY(0f) + .start(); + + // Animate the selected ringer view up to that ringer's position in the drawer. + mSelectedRingerContainer.animate() + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .setDuration(DRAWER_ANIMATION_DURATION) + .withEndAction(() -> + getDrawerIconViewForMode(mState.ringerModeInternal).setVisibility(VISIBLE)); + + if (!isLandscape()) { + mSelectedRingerContainer.animate() + .translationY(getTranslationInDrawerForRingerMode(mState.ringerModeInternal)) + .start(); } else { - row.icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + mSelectedRingerContainer.animate() + .translationX(getTranslationInDrawerForRingerMode(mState.ringerModeInternal)) + .start(); } + + mIsRingerDrawerOpen = true; + } + + /** Animates away the ringer drawer. */ + private void hideRingerDrawer() { + // Hide the drawer icon for the selected ringer - it's visible in the ringer button and we + // don't want to be able to see it while it animates away. + getDrawerIconViewForMode(mState.ringerModeInternal).setVisibility(INVISIBLE); + + mRingerDrawerContainer.animate() + .alpha(0f) + .setDuration(DRAWER_ANIMATION_DURATION) + .setStartDelay(0) + .withEndAction(() -> mRingerDrawerContainer.setVisibility(INVISIBLE)); + + if (!isLandscape()) { + mRingerDrawerContainer.animate() + .translationY(mRingerDrawerItemSize * 2) + .start(); + } else { + mRingerDrawerContainer.animate() + .translationX(mRingerDrawerItemSize * 2) + .start(); + } + + mSelectedRingerContainer.animate() + .translationX(0f) + .translationY(0f) + .start(); + + mIsRingerDrawerOpen = false; } public void initSettingsH() { @@ -555,12 +852,8 @@ public class VolumeDialogImpl implements VolumeDialog, mController.setStreamVolume(AudioManager.STREAM_RING, 1); } } - Events.writeEvent(Events.EVENT_RINGER_TOGGLE, newRingerMode); - incrementManualToggleCount(); - updateRingerH(); - provideTouchFeedbackH(newRingerMode); - mController.setRingerMode(newRingerMode, false); - maybeShowToastH(newRingerMode); + + setRingerMode(newRingerMode); }); } updateRingerH(); @@ -809,6 +1102,8 @@ public class VolumeDialogImpl implements VolumeDialog, mDialog.dismiss(); tryToRemoveCaptionsTooltip(); mIsAnimatingDismiss = false; + + hideRingerDrawer(); }, 50)); if (!isLandscape()) animator.translationX(mDialogView.getWidth() / 2.0f); animator.start(); @@ -889,12 +1184,14 @@ public class VolumeDialogImpl implements VolumeDialog, switch (mState.ringerModeInternal) { case AudioManager.RINGER_MODE_VIBRATE: mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate); + mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate); addAccessibilityDescription(mRingerIcon, RINGER_MODE_VIBRATE, mContext.getString(R.string.volume_ringer_hint_mute)); mRingerIcon.setTag(Events.ICON_STATE_VIBRATE); break; case AudioManager.RINGER_MODE_SILENT: mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute); + mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute); mRingerIcon.setTag(Events.ICON_STATE_MUTE); addAccessibilityDescription(mRingerIcon, RINGER_MODE_SILENT, mContext.getString(R.string.volume_ringer_hint_unmute)); @@ -904,11 +1201,13 @@ public class VolumeDialogImpl implements VolumeDialog, boolean muted = (mAutomute && ss.level == 0) || ss.muted; if (!isZenMuted && muted) { mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute); + mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute); addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL, mContext.getString(R.string.volume_ringer_hint_unmute)); mRingerIcon.setTag(Events.ICON_STATE_MUTE); } else { mRingerIcon.setImageResource(R.drawable.ic_volume_ringer); + mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer); if (mController.hasVibrator()) { addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL, mContext.getString(R.string.volume_ringer_hint_vibrate)); @@ -1075,8 +1374,6 @@ public class VolumeDialogImpl implements VolumeDialog, // update icon final boolean iconEnabled = (mAutomute || ss.muteSupported) && !zenMuted; - row.icon.setEnabled(iconEnabled); - row.icon.setAlpha(iconEnabled ? 1 : 0.5f); final int iconRes; if (isRingVibrate) { iconRes = R.drawable.ic_volume_ringer_vibrate; @@ -1092,7 +1389,7 @@ public class VolumeDialogImpl implements VolumeDialog, ? R.drawable.ic_volume_media_low : row.iconRes; } - row.icon.setImageResource(iconRes); + row.setIcon(iconRes, mContext.getTheme()); row.iconState = iconRes == R.drawable.ic_volume_ringer_vibrate ? Events.ICON_STATE_VIBRATE : (iconRes == R.drawable.ic_volume_media_bt_mute || iconRes == row.iconMuteRes) @@ -1101,18 +1398,35 @@ public class VolumeDialogImpl implements VolumeDialog, || iconRes == R.drawable.ic_volume_media_low) ? Events.ICON_STATE_UNMUTE : Events.ICON_STATE_UNKNOWN; - if (iconEnabled) { - if (isRingStream) { - if (isRingVibrate) { - row.icon.setContentDescription(mContext.getString( - R.string.volume_stream_content_description_unmute, - getStreamLabelH(ss))); + + if (row.icon != null) { + if (iconEnabled) { + if (isRingStream) { + if (isRingVibrate) { + row.icon.setContentDescription(mContext.getString( + R.string.volume_stream_content_description_unmute, + getStreamLabelH(ss))); + } else { + if (mController.hasVibrator()) { + row.icon.setContentDescription(mContext.getString( + mShowA11yStream + ? R.string.volume_stream_content_description_vibrate_a11y + : R.string.volume_stream_content_description_vibrate, + getStreamLabelH(ss))); + } else { + row.icon.setContentDescription(mContext.getString( + mShowA11yStream + ? R.string.volume_stream_content_description_mute_a11y + : R.string.volume_stream_content_description_mute, + getStreamLabelH(ss))); + } + } + } else if (isA11yStream) { + row.icon.setContentDescription(getStreamLabelH(ss)); } else { - if (mController.hasVibrator()) { + if (ss.muted || mAutomute && ss.level == 0) { row.icon.setContentDescription(mContext.getString( - mShowA11yStream - ? R.string.volume_stream_content_description_vibrate_a11y - : R.string.volume_stream_content_description_vibrate, + R.string.volume_stream_content_description_unmute, getStreamLabelH(ss))); } else { row.icon.setContentDescription(mContext.getString( @@ -1122,23 +1436,9 @@ public class VolumeDialogImpl implements VolumeDialog, getStreamLabelH(ss))); } } - } else if (isA11yStream) { - row.icon.setContentDescription(getStreamLabelH(ss)); } else { - if (ss.muted || mAutomute && ss.level == 0) { - row.icon.setContentDescription(mContext.getString( - R.string.volume_stream_content_description_unmute, - getStreamLabelH(ss))); - } else { - row.icon.setContentDescription(mContext.getString( - mShowA11yStream - ? R.string.volume_stream_content_description_mute_a11y - : R.string.volume_stream_content_description_mute, - getStreamLabelH(ss))); - } + row.icon.setContentDescription(getStreamLabelH(ss)); } - } else { - row.icon.setContentDescription(getStreamLabelH(ss)); } // ensure tracking is disabled if zenMuted @@ -1167,22 +1467,29 @@ public class VolumeDialogImpl implements VolumeDialog, if (!useActiveColoring && !mChangeVolumeRowTintWhenInactive) { return; } - final ColorStateList tint = useActiveColoring + final ColorStateList colorTint = useActiveColoring ? Utils.getColorAccent(mContext) : Utils.getColorAttr(mContext, android.R.attr.colorForeground); final int alpha = useActiveColoring - ? Color.alpha(tint.getDefaultColor()) + ? Color.alpha(colorTint.getDefaultColor()) : getAlphaAttr(android.R.attr.secondaryContentAlpha); - if (tint == row.cachedTint) return; - row.slider.setProgressTintList(tint); - row.slider.setThumbTintList(tint); - row.slider.setProgressBackgroundTintList(tint); - row.slider.setAlpha(((float) alpha) / 255); - row.icon.setImageTintList(tint); - row.icon.setImageAlpha(alpha); - row.cachedTint = tint; + + final ColorStateList bgTint = Utils.getColorAttr( + mContext, android.R.attr.colorBackgroundFloating); + + row.sliderProgressSolid.setTintList(colorTint); + row.sliderBgIcon.setTintList(colorTint); + + row.sliderBgSolid.setTintList(bgTint); + row.sliderProgressIcon.setTintList(bgTint); + + if (row.icon != null) { + row.icon.setImageTintList(colorTint); + row.icon.setImageAlpha(alpha); + } + if (row.number != null) { - row.number.setTextColor(tint); + row.number.setTextColor(colorTint); row.number.setAlpha(alpha); } } @@ -1538,6 +1845,10 @@ public class VolumeDialogImpl implements VolumeDialog, private View view; private TextView header; private ImageButton icon; + private Drawable sliderBgSolid; + private AlphaTintDrawableWrapper sliderBgIcon; + private Drawable sliderProgressSolid; + private AlphaTintDrawableWrapper sliderProgressIcon; private SeekBar slider; private TextView number; private int stream; @@ -1555,5 +1866,69 @@ public class VolumeDialogImpl implements VolumeDialog, private int animTargetProgress; private int lastAudibleLevel = 1; private FrameLayout dndIcon; + + void setIcon(int iconRes, Resources.Theme theme) { + if (icon != null) { + icon.setImageResource(iconRes); + } + + sliderProgressIcon.setDrawable(view.getResources().getDrawable(iconRes, theme)); + sliderBgIcon.setDrawable(view.getResources().getDrawable(iconRes, theme)); + } + } + + /** + * Click listener added to each ringer option in the drawer. This will initiate the animation to + * select and then close the ringer drawer, and actually change the ringer mode. + */ + private class RingerDrawerItemClickListener implements View.OnClickListener { + private final int mClickedRingerMode; + + RingerDrawerItemClickListener(int clickedRingerMode) { + mClickedRingerMode = clickedRingerMode; + } + + @Override + public void onClick(View view) { + setRingerMode(mClickedRingerMode); + + mRingerDrawerIconAnimatingSelected = getDrawerIconViewForMode(mClickedRingerMode); + mRingerDrawerIconAnimatingDeselected = getDrawerIconViewForMode( + mState.ringerModeInternal); + + // Begin switching the selected icon and deselected icon colors since the background is + // going to animate behind the new selection. + mRingerDrawerIconColorAnimator.start(); + + mSelectedRingerContainer.setVisibility(View.INVISIBLE); + mRingerDrawerNewSelectionBg.setAlpha(1f); + mRingerDrawerNewSelectionBg.animate() + .setInterpolator(Interpolators.ACCELERATE_DECELERATE) + .setDuration(DRAWER_ANIMATION_DURATION_SHORT) + .withEndAction(() -> { + mRingerDrawerNewSelectionBg.setAlpha(0f); + + if (!isLandscape()) { + mSelectedRingerContainer.setTranslationY( + getTranslationInDrawerForRingerMode(mClickedRingerMode)); + } else { + mSelectedRingerContainer.setTranslationX( + getTranslationInDrawerForRingerMode(mClickedRingerMode)); + } + + mSelectedRingerContainer.setVisibility(VISIBLE); + hideRingerDrawer(); + }); + + if (!isLandscape()) { + mRingerDrawerNewSelectionBg.animate() + .translationY(getTranslationInDrawerForRingerMode(mClickedRingerMode)) + .start(); + } else { + mRingerDrawerNewSelectionBg.animate() + .translationX(getTranslationInDrawerForRingerMode(mClickedRingerMode)) + .start(); + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 8505703b9e25..4eb75ebe4553 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -287,6 +287,13 @@ public final class WMShell extends SystemUI @Override public void onKeyguardVisibilityChanged(boolean showing) { + if (showing) { + // When keyguard shown, temperory lock OHM disabled to avoid mis-trigger. + oneHanded.setLockedDisabled(true /* locked */, false /* enabled */); + } else { + // Reset locked. + oneHanded.setLockedDisabled(false /* locked */, false /* enabled */); + } oneHanded.stopOneHanded(); } }; @@ -315,6 +322,13 @@ public final class WMShell extends SystemUI } } }); + + mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() { + @Override + public void onConfigChanged(Configuration newConfig) { + oneHanded.onConfigChanged(newConfig); + } + }); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index fba0b0079012..4611fe03e157 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -31,6 +31,7 @@ import android.view.WindowManager; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; +import com.android.systemui.R; import com.android.systemui.dagger.WMComponent; import com.android.systemui.dagger.WMSingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -97,7 +98,12 @@ import dagger.Provides; @Module public abstract class WMShellBaseModule { - private static final boolean ENABLE_SHELL_MAIN_THREAD = false; + /** + * Returns whether to enable a separate shell thread for the shell features. + */ + private static boolean enableShellMainThread(Context context) { + return context.getResources().getBoolean(R.bool.config_enableShellMainThread); + } // // Shell Concurrency - Components used for managing threading in the Shell and SysUI @@ -120,8 +126,8 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides @ShellMainThread - public static Handler provideShellMainHandler(@Main Handler sysuiMainHandler) { - if (ENABLE_SHELL_MAIN_THREAD) { + public static Handler provideShellMainHandler(Context context, @Main Handler sysuiMainHandler) { + if (enableShellMainThread(context)) { HandlerThread mainThread = new HandlerThread("wmshell.main"); mainThread.start(); return mainThread.getThreadHandler(); @@ -135,9 +141,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides @ShellMainThread - public static ShellExecutor provideShellMainExecutor(@ShellMainThread Handler mainHandler, - @Main ShellExecutor sysuiMainExecutor) { - if (ENABLE_SHELL_MAIN_THREAD) { + public static ShellExecutor provideShellMainExecutor(Context context, + @ShellMainThread Handler mainHandler, @Main ShellExecutor sysuiMainExecutor) { + if (enableShellMainThread(context)) { return new HandlerExecutor(mainHandler); } return sysuiMainExecutor; @@ -380,9 +386,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool, - @ShellMainThread ShellExecutor mainExecutor, + Context context, @ShellMainThread ShellExecutor mainExecutor, @ShellAnimationThread ShellExecutor animExecutor) { - return new Transitions(organizer, pool, mainExecutor, animExecutor); + return new Transitions(organizer, pool, context, mainExecutor, animExecutor); } // @@ -409,10 +415,11 @@ public abstract class WMShellBaseModule { ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Context context, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, - @ShellMainThread ShellExecutor mainExecutor) { + @ShellMainThread ShellExecutor mainExecutor, + DisplayImeController displayImeController) { if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) { return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context, - rootTaskDisplayAreaOrganizer, mainExecutor)); + rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController)); } else { return Optional.empty(); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 997b488a627f..754b6a6435b4 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -104,9 +104,10 @@ public class WMShellModule { @Provides static AppPairsController provideAppPairs(ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, DisplayController displayController, - @ShellMainThread ShellExecutor mainExecutor) { + @ShellMainThread ShellExecutor mainExecutor, + DisplayImeController displayImeController) { return new AppPairsController(shellTaskOrganizer, syncQueue, displayController, - mainExecutor); + mainExecutor, displayImeController); } // diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java index d3f9d641ca9f..aa4122fd190a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java @@ -54,6 +54,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.text.TextUtils; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.WakefulnessLifecycle; @@ -73,7 +74,7 @@ import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class CarrierTextManagerTest extends SysuiTestCase { +public class CarrierTextControllerTest extends SysuiTestCase { private static final CharSequence SEPARATOR = " \u2014 "; private static final CharSequence INVALID_CARD_TEXT = "Invalid card"; @@ -94,9 +95,7 @@ public class CarrierTextManagerTest extends SysuiTestCase { @Mock private WifiManager mWifiManager; @Mock - private WakefulnessLifecycle mWakefulnessLifecycle; - @Mock - private CarrierTextManager.CarrierTextCallback mCarrierTextCallback; + private CarrierTextController.CarrierTextCallback mCarrierTextCallback; @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock @@ -105,14 +104,13 @@ public class CarrierTextManagerTest extends SysuiTestCase { private TelephonyManager mTelephonyManager; @Mock private SubscriptionManager mSubscriptionManager; - private CarrierTextManager.CarrierTextCallbackInfo mCarrierTextCallbackInfo; + private CarrierTextController.CarrierTextCallbackInfo mCarrierTextCallbackInfo; - private CarrierTextManager mCarrierTextManager; + private CarrierTextController mCarrierTextController; private TestableLooper mTestableLooper; - private Handler mMainHandler; private Void checkMainThread(InvocationOnMock inv) { - Looper mainLooper = mMainHandler.getLooper(); + Looper mainLooper = Dependency.get(Dependency.MAIN_HANDLER).getLooper(); if (!mainLooper.isCurrentThread()) { fail("This call should be done from the main thread"); } @@ -124,33 +122,35 @@ public class CarrierTextManagerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); - mMainHandler = new Handler(mTestableLooper.getLooper()); + mContext.addMockSystemService(WifiManager.class, mWifiManager); + mContext.addMockSystemService(ConnectivityManager.class, mConnectivityManager); when(mConnectivityManager.isNetworkSupported(anyInt())).thenReturn(true); + mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager); + mContext.addMockSystemService(SubscriptionManager.class, mSubscriptionManager); mContext.getOrCreateTestableResources().addOverride( R.string.keyguard_sim_error_message_short, INVALID_CARD_TEXT); mContext.getOrCreateTestableResources().addOverride( R.string.airplane_mode, AIRPLANE_MODE_TEXT); + mDependency.injectMockDependency(WakefulnessLifecycle.class); + mDependency.injectTestDependency(Dependency.MAIN_HANDLER, + new Handler(mTestableLooper.getLooper())); + mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); + mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor); doAnswer(this::checkMainThread).when(mKeyguardUpdateMonitor) .registerCallback(any(KeyguardUpdateMonitorCallback.class)); doAnswer(this::checkMainThread).when(mKeyguardUpdateMonitor) .removeCallback(any(KeyguardUpdateMonitorCallback.class)); - mCarrierTextCallbackInfo = new CarrierTextManager.CarrierTextCallbackInfo("", + mCarrierTextCallbackInfo = new CarrierTextController.CarrierTextCallbackInfo("", new CharSequence[]{}, false, new int[]{}); when(mTelephonyManager.getSupportedModemCount()).thenReturn(3); when(mTelephonyManager.getActiveModemCount()).thenReturn(3); - mCarrierTextManager = new CarrierTextManager.Builder( - mContext, mContext.getResources(), mWifiManager, mConnectivityManager, - mTelephonyManager, mWakefulnessLifecycle, new Handler(mTestableLooper.getLooper()), - mMainHandler, mKeyguardUpdateMonitor) - .setShowAirplaneMode(true) - .setShowMissingSim(true) - .build(); + mCarrierTextController = new CarrierTextController(mContext, SEPARATOR, true, true); // This should not start listening on any of the real dependencies but will test that // callbacks in mKeyguardUpdateMonitor are done in the mTestableLooper thread - mCarrierTextManager.setListening(mCarrierTextCallback); + mCarrierTextController.setListening(mCarrierTextCallback); mTestableLooper.processAllMessages(); } @@ -165,8 +165,8 @@ public class CarrierTextManagerTest extends SysuiTestCase { TestableLooper testableLooper = new TestableLooper(thread.getLooper()); Handler h = new Handler(testableLooper.getLooper()); h.post(() -> { - mCarrierTextManager.setListening(null); - mCarrierTextManager.setListening(mCarrierTextCallback); + mCarrierTextController.setListening(null); + mCarrierTextController.setListening(mCarrierTextCallback); }); testableLooper.processAllMessages(); mTestableLooper.processAllMessages(); @@ -183,11 +183,11 @@ public class CarrierTextManagerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.getSimState(0)).thenReturn(TelephonyManager.SIM_STATE_READY); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); - mCarrierTextManager.updateCarrierText(); + mCarrierTextController.updateCarrierText(); - ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( - CarrierTextManager.CarrierTextCallbackInfo.class); + CarrierTextController.CarrierTextCallbackInfo.class); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); @@ -205,12 +205,12 @@ public class CarrierTextManagerTest extends SysuiTestCase { TelephonyManager.SIM_STATE_CARD_IO_ERROR); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); - mCarrierTextManager.mCallback.onSimStateChanged(3, 1, + mCarrierTextController.mCallback.onSimStateChanged(3, 1, TelephonyManager.SIM_STATE_CARD_IO_ERROR); - ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( - CarrierTextManager.CarrierTextCallbackInfo.class); + CarrierTextController.CarrierTextCallbackInfo.class); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); @@ -223,7 +223,7 @@ public class CarrierTextManagerTest extends SysuiTestCase { reset(mCarrierTextCallback); when(mTelephonyManager.getActiveModemCount()).thenReturn(1); // Update carrier text. It should ignore error state of subId 3 in inactive slotId. - mCarrierTextManager.updateCarrierText(); + mCarrierTextController.updateCarrierText(); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); assertEquals("TEST_CARRIER", captor.getValue().carrierText); @@ -237,9 +237,9 @@ public class CarrierTextManagerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn( TelephonyManager.SIM_STATE_CARD_IO_ERROR); // This should not produce an out of bounds error, even though there are no subscriptions - mCarrierTextManager.mCallback.onSimStateChanged(0, -3, + mCarrierTextController.mCallback.onSimStateChanged(0, -3, TelephonyManager.SIM_STATE_CARD_IO_ERROR); - mCarrierTextManager.mCallback.onSimStateChanged(0, 3, TelephonyManager.SIM_STATE_READY); + mCarrierTextController.mCallback.onSimStateChanged(0, 3, TelephonyManager.SIM_STATE_READY); verify(mCarrierTextCallback, never()).updateCarrierInfo(any()); } @@ -257,23 +257,23 @@ public class CarrierTextManagerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn( TelephonyManager.SIM_STATE_CARD_IO_ERROR); // This should not produce an out of bounds error, even though there are no subscriptions - mCarrierTextManager.mCallback.onSimStateChanged(0, 1, + mCarrierTextController.mCallback.onSimStateChanged(0, 1, TelephonyManager.SIM_STATE_CARD_IO_ERROR); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo( - any(CarrierTextManager.CarrierTextCallbackInfo.class)); + any(CarrierTextController.CarrierTextCallbackInfo.class)); } @Test public void testCallback() { reset(mCarrierTextCallback); - mCarrierTextManager.postToCallback(mCarrierTextCallbackInfo); + mCarrierTextController.postToCallback(mCarrierTextCallbackInfo); mTestableLooper.processAllMessages(); - ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( - CarrierTextManager.CarrierTextCallbackInfo.class); + CarrierTextController.CarrierTextCallbackInfo.class); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); assertEquals(mCarrierTextCallbackInfo, captor.getValue()); } @@ -282,8 +282,8 @@ public class CarrierTextManagerTest extends SysuiTestCase { public void testNullingCallback() { reset(mCarrierTextCallback); - mCarrierTextManager.postToCallback(mCarrierTextCallbackInfo); - mCarrierTextManager.setListening(null); + mCarrierTextController.postToCallback(mCarrierTextCallbackInfo); + mCarrierTextController.setListening(null); // This shouldn't produce NPE mTestableLooper.processAllMessages(); @@ -301,15 +301,15 @@ public class CarrierTextManagerTest extends SysuiTestCase { mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); - ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( - CarrierTextManager.CarrierTextCallbackInfo.class); + CarrierTextController.CarrierTextCallbackInfo.class); - mCarrierTextManager.updateCarrierText(); + mCarrierTextController.updateCarrierText(); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); - CarrierTextManager.CarrierTextCallbackInfo info = captor.getValue(); + CarrierTextController.CarrierTextCallbackInfo info = captor.getValue(); assertEquals(1, info.listOfCarriers.length); assertEquals(TEST_CARRIER, info.listOfCarriers[0]); assertEquals(1, info.subscriptionIds.length); @@ -326,15 +326,15 @@ public class CarrierTextManagerTest extends SysuiTestCase { mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); - ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( - CarrierTextManager.CarrierTextCallbackInfo.class); + CarrierTextController.CarrierTextCallbackInfo.class); - mCarrierTextManager.updateCarrierText(); + mCarrierTextController.updateCarrierText(); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); - CarrierTextManager.CarrierTextCallbackInfo info = captor.getValue(); + CarrierTextController.CarrierTextCallbackInfo info = captor.getValue(); assertEquals(1, info.listOfCarriers.length); assertTrue(info.listOfCarriers[0].toString().contains(TEST_CARRIER)); assertEquals(1, info.subscriptionIds.length); @@ -346,16 +346,16 @@ public class CarrierTextManagerTest extends SysuiTestCase { List<SubscriptionInfo> list = new ArrayList<>(); list.add(TEST_SUBSCRIPTION_NULL); when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn( - TelephonyManager.SIM_STATE_READY); + TelephonyManager.SIM_STATE_READY); when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); - ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( - CarrierTextManager.CarrierTextCallbackInfo.class); + CarrierTextController.CarrierTextCallbackInfo.class); - mCarrierTextManager.updateCarrierText(); + mCarrierTextController.updateCarrierText(); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); @@ -380,11 +380,11 @@ public class CarrierTextManagerTest extends SysuiTestCase { when(ss.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE); mKeyguardUpdateMonitor.mServiceStates.put(TEST_SUBSCRIPTION_NULL.getSubscriptionId(), ss); - ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( - CarrierTextManager.CarrierTextCallbackInfo.class); + CarrierTextController.CarrierTextCallbackInfo.class); - mCarrierTextManager.updateCarrierText(); + mCarrierTextController.updateCarrierText(); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); @@ -407,15 +407,15 @@ public class CarrierTextManagerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn( new ArrayList<>()); - ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( - CarrierTextManager.CarrierTextCallbackInfo.class); + CarrierTextController.CarrierTextCallbackInfo.class); - mCarrierTextManager.updateCarrierText(); + mCarrierTextController.updateCarrierText(); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); - CarrierTextManager.CarrierTextCallbackInfo info = captor.getValue(); + CarrierTextController.CarrierTextCallbackInfo info = captor.getValue(); assertEquals(0, info.listOfCarriers.length); assertEquals(0, info.subscriptionIds.length); @@ -428,16 +428,16 @@ public class CarrierTextManagerTest extends SysuiTestCase { list.add(TEST_SUBSCRIPTION); list.add(TEST_SUBSCRIPTION); when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn( - TelephonyManager.SIM_STATE_READY); + TelephonyManager.SIM_STATE_READY); when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); - ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( - CarrierTextManager.CarrierTextCallbackInfo.class); + CarrierTextController.CarrierTextCallbackInfo.class); - mCarrierTextManager.updateCarrierText(); + mCarrierTextController.updateCarrierText(); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); @@ -458,11 +458,11 @@ public class CarrierTextManagerTest extends SysuiTestCase { mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); - ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( - CarrierTextManager.CarrierTextCallbackInfo.class); + CarrierTextController.CarrierTextCallbackInfo.class); - mCarrierTextManager.updateCarrierText(); + mCarrierTextController.updateCarrierText(); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); @@ -483,11 +483,11 @@ public class CarrierTextManagerTest extends SysuiTestCase { mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); - ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( - CarrierTextManager.CarrierTextCallbackInfo.class); + CarrierTextController.CarrierTextCallbackInfo.class); - mCarrierTextManager.updateCarrierText(); + mCarrierTextController.updateCarrierText(); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); @@ -509,11 +509,11 @@ public class CarrierTextManagerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); - ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = ArgumentCaptor.forClass( - CarrierTextManager.CarrierTextCallbackInfo.class); + CarrierTextController.CarrierTextCallbackInfo.class); - mCarrierTextManager.updateCarrierText(); + mCarrierTextController.updateCarrierText(); mTestableLooper.processAllMessages(); verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java index d67fe6dfb0b2..c2ade81a9877 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java @@ -69,8 +69,6 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { private KeyguardMessageAreaController mKeyguardMessageAreaController; @Mock private LatencyTracker mLatencyTracker; - @Mock - private EmergencyButtonController mEmergencyButtonController; private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController; @@ -86,8 +84,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { .thenReturn(mKeyguardMessageArea); mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView, mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, - mKeyguardMessageAreaControllerFactory, mLatencyTracker, - mEmergencyButtonController) { + mKeyguardMessageAreaControllerFactory, mLatencyTracker) { @Override void resetState() { } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java index 4beec574cd2a..826be2ba0d83 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java @@ -54,11 +54,11 @@ import java.util.concurrent.Executor; public class KeyguardDisplayManagerTest extends SysuiTestCase { @Mock - private NavigationBarController mNavigationBarController; - @Mock private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; + @Mock private DisplayManager mDisplayManager; + @Mock private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation; @@ -76,8 +76,9 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); mContext.addMockSystemService(DisplayManager.class, mDisplayManager); - mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController, - mKeyguardStatusViewComponentFactory, mBackgroundExecutor)); + mDependency.injectMockDependency(NavigationBarController.class); + mManager = spy(new KeyguardDisplayManager(mContext, mKeyguardStatusViewComponentFactory, + mBackgroundExecutor)); doReturn(mKeyguardPresentation).when(mManager).createPresentation(any()); mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt index 6d0c64088abc..c69ec1a254c3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt @@ -49,8 +49,6 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() { @Mock private lateinit var mLatencyTracker: LatencyTracker @Mock - private lateinit var mEmergencyButtonController: EmergencyButtonController - @Mock private lateinit var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory @Mock @@ -74,8 +72,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() { .thenReturn(mKeyguardMessageAreaController) mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView, mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, - mLatencyTracker, mEmergencyButtonController, - mKeyguardMessageAreaControllerFactory) + mLatencyTracker, mKeyguardMessageAreaControllerFactory) } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java index 8d1e1a4a4463..31cc7bb7c958 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java @@ -67,8 +67,6 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase { private LatencyTracker mLatencyTracker; @Mock private LiftToActivateListener mLiftToactivateListener; - @Mock - private EmergencyButtonController mEmergencyButtonController; private FalsingCollector mFalsingCollector = new FalsingCollectorFake(); @Mock private View mDeleteButton; @@ -94,7 +92,7 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase { mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView, mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener, - mEmergencyButtonController, mFalsingCollector) { + mFalsingCollector) { @Override public void onResume(int reason) { super.onResume(reason); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java index 9296d3d5ec82..3b7f4b839853 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java @@ -59,10 +59,6 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase { @Mock private KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory; @Mock - private EmergencyButtonController.Factory mEmergencyButtonControllerFactory; - @Mock - private EmergencyButtonController mEmergencyButtonController; - @Mock private KeyguardInputViewController mKeyguardInputViewController; @Mock private KeyguardInputView mInputView; @@ -80,12 +76,9 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase { any(KeyguardSecurityCallback.class))) .thenReturn(mKeyguardInputViewController); when(mView.getWindowInsetsController()).thenReturn(mWindowInsetsController); - when(mEmergencyButtonControllerFactory.create(any(EmergencyButton.class))) - .thenReturn(mEmergencyButtonController); mKeyguardSecurityViewFlipperController = new KeyguardSecurityViewFlipperController(mView, - mLayoutInflater, mKeyguardSecurityViewControllerFactory, - mEmergencyButtonControllerFactory); + mLayoutInflater, mKeyguardSecurityViewControllerFactory); } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 6e2398c033ba..eb95d1653e84 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -56,6 +56,7 @@ import android.hardware.face.FaceSensorProperties; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintManager; import android.media.AudioManager; +import android.nfc.NfcAdapter; import android.os.Bundle; import android.os.Handler; import android.os.IRemoteCallback; @@ -851,6 +852,17 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.shouldListenForUdfps()).isEqualTo(false); } + @Test + public void testRequireUnlockForNfc_Broadcast() { + KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class); + mKeyguardUpdateMonitor.registerCallback(callback); + Intent intent = new Intent(NfcAdapter.ACTION_REQUIRE_UNLOCK_FOR_NFC); + mKeyguardUpdateMonitor.mBroadcastAllReceiver.onReceive(getContext(), intent); + mTestableLooper.processAllMessages(); + + verify(callback, atLeastOnce()).onRequireUnlockForNfc(); + } + private void setKeyguardBouncerVisibility(boolean isVisible) { mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible); mTestableLooper.processAllMessages(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java index 6c3b37edbd15..ba21afdfab0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java @@ -309,17 +309,6 @@ public class ForegroundServiceControllerTest extends SysuiTestCase { } @Test - public void testOverlayPredicate() { - StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1", - 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); - StatusBarNotification sbn_user1_overlay = makeMockSBN(USERID_ONE, "android", - 0, "AlertWindowNotification", Notification.FLAG_NO_CLEAR); - - assertTrue(mFsc.isSystemAlertNotification(sbn_user1_overlay)); - assertFalse(mFsc.isSystemAlertNotification(sbn_user1_app1)); - } - - @Test public void testNoNotifsNorAppOps_noSystemAlertWarningRequired() { // no notifications nor app op signals that this package/userId requires system alert // warning diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java index 0c69ffdef372..e1ddaada44cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java @@ -43,6 +43,7 @@ import android.view.SurfaceHolder; import com.android.systemui.glwallpaper.ImageWallpaperRenderer; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -53,6 +54,7 @@ import java.util.concurrent.CountDownLatch; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper +@Ignore public class ImageWallpaperTest extends SysuiTestCase { private static final int LOW_BMP_WIDTH = 128; private static final int LOW_BMP_HEIGHT = 128; diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index bc322f7f18fc..2526990dfd40 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -16,8 +16,8 @@ package com.android.systemui.appops; -import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA; -import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE; +import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; +import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; import static junit.framework.TestCase.assertFalse; @@ -69,6 +69,7 @@ import java.util.List; @TestableLooper.RunWithLooper public class AppOpsControllerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; + private static final String TEST_ATTRIBUTION_NAME = "attribution"; private static final int TEST_UID = UserHandle.getUid(0, 0); private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0); private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0); @@ -124,9 +125,9 @@ public class AppOpsControllerTest extends SysuiTestCase { when(mAudioManager.getActiveRecordingConfigurations()) .thenReturn(List.of(mPausedMockRecording)); - when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA)) + when(mSensorPrivacyController.isSensorBlocked(CAMERA)) .thenReturn(false); - when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA)) + when(mSensorPrivacyController.isSensorBlocked(CAMERA)) .thenReturn(false); mController = new AppOpsControllerImpl( @@ -164,7 +165,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.onOpActiveChanged( AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); mTestableLooper.processAllMessages(); verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); @@ -218,8 +219,8 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.onOpActiveChanged(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, - TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.OP_FLAG_SELF, - AppOpsManager.MODE_ALLOWED); + TEST_UID, TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, + AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); assertEquals(3, mController.getActiveAppOps().size()); } @@ -230,8 +231,8 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.onOpActiveChanged(AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, - TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.OP_FLAG_SELF, - AppOpsManager.MODE_ALLOWED); + TEST_UID, TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, + AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); assertEquals(2, mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size()); assertEquals(1, @@ -262,7 +263,7 @@ public class AppOpsControllerTest extends SysuiTestCase { public void opNotedScheduledForRemoval() { mController.setBGHandler(mMockHandler); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong()); } @@ -274,7 +275,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.onOpActiveChanged(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); assertFalse(mController.getActiveAppOps().isEmpty()); mController.setListening(false); @@ -288,9 +289,9 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.setBGHandler(mMockHandler); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); // Only one post to notify subscribers verify(mMockHandler, times(1)).post(any()); @@ -304,9 +305,9 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.setBGHandler(mMockHandler); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); // Only one post to notify subscribers verify(mMockHandler, times(2)).scheduleRemoval(any(), anyLong()); @@ -324,7 +325,7 @@ public class AppOpsControllerTest extends SysuiTestCase { AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); // Check that we "scheduled" the removal. Don't actually schedule until we are ready to // process messages at a later time. @@ -353,7 +354,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); mController.onOpActiveChanged( AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); @@ -382,7 +383,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); mController.onOpActiveChanged( AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); @@ -400,7 +401,7 @@ public class AppOpsControllerTest extends SysuiTestCase { AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); mTestableLooper.processAllMessages(); verify(mCallback).onActiveStateChanged( @@ -504,7 +505,7 @@ public class AppOpsControllerTest extends SysuiTestCase { assertFalse(list.get(0).isDisabled()); // Add a camera op, and disable the microphone. The camera op should be the only op returned - mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, true); + mController.onSensorBlockedChanged(MICROPHONE, true); mController.onOpActiveChanged( AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); mTestableLooper.processAllMessages(); @@ -514,7 +515,7 @@ public class AppOpsControllerTest extends SysuiTestCase { // Re enable the microphone, and verify the op returns - mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, false); + mController.onSensorBlockedChanged(MICROPHONE, false); mTestableLooper.processAllMessages(); list = mController.getActiveAppOps(); @@ -537,7 +538,7 @@ public class AppOpsControllerTest extends SysuiTestCase { assertFalse(list.get(0).isDisabled()); // Add an audio op, and disable the camera. The audio op should be the only op returned - mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, true); + mController.onSensorBlockedChanged(CAMERA, true); mController.onOpActiveChanged( AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); mTestableLooper.processAllMessages(); @@ -546,7 +547,7 @@ public class AppOpsControllerTest extends SysuiTestCase { assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode()); // Re enable the camera, and verify the op returns - mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, false); + mController.onSensorBlockedChanged(CAMERA, false); mTestableLooper.processAllMessages(); list = mController.getActiveAppOps(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 3e873d1985fb..700f101e44b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -150,7 +150,10 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void dozeTimeTick() { + public void dozeTimeTick() throws RemoteException { + mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD); + mFgExecutor.runAllReady(); mUdfpsController.dozeTimeTick(); verify(mUdfpsView).dozeTimeTick(); } @@ -240,7 +243,8 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void registersViewForCallbacks() throws RemoteException { - verify(mStatusBarStateController).addCallback(mUdfpsView); - verify(mStatusBar).addExpansionChangedListener(mUdfpsView); + verify(mStatusBarStateController).addCallback(mUdfpsController.mStatusBarStateListener); + verify(mStatusBar).addExpansionChangedListener( + mUdfpsController.mStatusBarExpansionListener); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java index 6d8c372a061b..afe5c0b2edbd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java @@ -41,7 +41,9 @@ import androidx.test.runner.AndroidJUnit4; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.tuner.TunerService; import com.android.systemui.util.wakelock.WakeLockFake; import org.junit.After; @@ -67,10 +69,14 @@ public class DozeUiTest extends SysuiTestCase { private DozeHost mHost; @Mock private DozeLog mDozeLog; + @Mock + private TunerService mTunerService; private WakeLockFake mWakeLock; private Handler mHandler; private HandlerThread mHandlerThread; private DozeUi mDozeUi; + @Mock + private StatusBarStateController mStatusBarStateController; @Before public void setUp() throws Exception { @@ -82,7 +88,8 @@ public class DozeUiTest extends SysuiTestCase { mHandler = mHandlerThread.getThreadHandler(); mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler, - mDozeParameters, mKeyguardUpdateMonitor, mDozeLog); + mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService, + () -> mStatusBarStateController); mDozeUi.setDozeMachine(mMachine); } @@ -138,7 +145,8 @@ public class DozeUiTest extends SysuiTestCase { reset(mHost); when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true); mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler, - mDozeParameters, mKeyguardUpdateMonitor, mDozeLog); + mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService, + () -> mStatusBarStateController); mDozeUi.setDozeMachine(mMachine); // Never animate if display doesn't support it. diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java index c79037b761aa..223714cfda30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java @@ -18,7 +18,6 @@ package com.android.systemui.flags; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -27,59 +26,44 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.res.Resources; -import android.provider.DeviceConfig; import androidx.annotation.BoolRes; import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.assist.DeviceConfigHelper; import com.android.systemui.util.wrapper.BuildInfo; import org.junit.Before; import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.Executor; - @SmallTest public class FeatureFlagReaderTest extends SysuiTestCase { @Mock private Resources mResources; @Mock private BuildInfo mBuildInfo; - @Mock private DeviceConfigHelper mDeviceConfig; - @Mock private Executor mBackgroundExecutor; + @Mock private SystemPropertiesHelper mSystemPropertiesHelper; private FeatureFlagReader mReader; - @Captor private ArgumentCaptor<DeviceConfig.OnPropertiesChangedListener> mListenerCaptor; - private DeviceConfig.OnPropertiesChangedListener mPropChangeListener; - @Before public void setUp() { MockitoAnnotations.initMocks(this); - when(mDeviceConfig.getBoolean(anyString(), anyBoolean())) + when(mSystemPropertiesHelper.getBoolean(anyString(), anyBoolean())) .thenAnswer(invocation -> invocation.getArgument(1)); defineFlag(FLAG_RESID_0, false); defineFlag(FLAG_RESID_1, true); initialize(true, true); - - verify(mDeviceConfig).addOnPropertiesChangedListener(any(), mListenerCaptor.capture()); - mPropChangeListener = mListenerCaptor.getValue(); } private void initialize(boolean isDebuggable, boolean isOverrideable) { when(mBuildInfo.isDebuggable()).thenReturn(isDebuggable); when(mResources.getBoolean(R.bool.are_flags_overrideable)).thenReturn(isOverrideable); - mReader = new FeatureFlagReader(mResources, mBuildInfo, mDeviceConfig, mBackgroundExecutor); + mReader = new FeatureFlagReader(mResources, mBuildInfo, mSystemPropertiesHelper); } @Test @@ -136,24 +120,8 @@ public class FeatureFlagReaderTest extends SysuiTestCase { // THEN the underlying resource and override are only queried once verify(mResources, times(1)).getBoolean(FLAG_RESID_0); - verify(mDeviceConfig, times(1)).getBoolean(fakeStorageKey(FLAG_RESID_0), false); - } - - @Test - public void testCachesAreClearedAfterPropsChange() { - // GIVEN a flag whose value has already been queried - assertFalse(mReader.isEnabled(FLAG_RESID_0)); - - // WHEN the value of the flag changes - overrideFlag(FLAG_RESID_0, true); - Map<String, String> changedMap = new HashMap<>(); - changedMap.put(fakeStorageKey(FLAG_RESID_0), "true"); - DeviceConfig.Properties properties = - new DeviceConfig.Properties("systemui", changedMap); - mPropChangeListener.onPropertiesChanged(properties); - - // THEN the new value is provided - assertTrue(mReader.isEnabled(FLAG_RESID_0)); + verify(mSystemPropertiesHelper, times(1)) + .getBoolean(fakeStorageKey(FLAG_RESID_0), false); } private void defineFlag(int resId, boolean value) { @@ -162,12 +130,12 @@ public class FeatureFlagReaderTest extends SysuiTestCase { } private void overrideFlag(int resId, boolean value) { - when(mDeviceConfig.getBoolean(eq(fakeStorageKey(resId)), anyBoolean())) + when(mSystemPropertiesHelper.getBoolean(eq(fakeStorageKey(resId)), anyBoolean())) .thenReturn(value); } private String fakeStorageKey(@BoolRes int resId) { - return "flag_testname_" + resId; + return "persist.systemui.flag_testname_" + resId; } private static final int FLAG_RESID_0 = 47; diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java index e23507b0a895..a3221b58e937 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java @@ -50,6 +50,7 @@ import org.mockito.Spy; @SmallTest @RunWith(AndroidTestingRunner.class) +@Ignore public class EglHelperTest extends SysuiTestCase { @Spy diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java index 24f3eb27579a..510b90722650 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java @@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,6 +44,7 @@ import java.util.Set; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) +@Ignore public class ImageWallpaperRendererTest extends SysuiTestCase { private WallpaperManager mWpmSpy; diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java index 0320103ceaa8..c8f223bdd72f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java @@ -16,6 +16,7 @@ package com.android.systemui.navigationbar.buttons; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; @@ -25,6 +26,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.res.Configuration; +import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import android.view.MotionEvent; @@ -39,6 +41,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Map; + @RunWith(AndroidTestingRunner.class) @RunWithLooper @SmallTest @@ -51,6 +55,7 @@ public class NearestTouchFrameTest extends SysuiTestCase { Configuration c = new Configuration(mContext.getResources().getConfiguration()); c.smallestScreenWidthDp = 500; mNearestTouchFrame = new NearestTouchFrame(mContext, null, c); + mNearestTouchFrame.layout(0, 0, 100, 100); } @Test @@ -64,7 +69,7 @@ public class NearestTouchFrameTest extends SysuiTestCase { mNearestTouchFrame.addView(left); mNearestTouchFrame.addView(right); - mNearestTouchFrame.onMeasure(0, 0); + mNearestTouchFrame.layout(0, 0, 30, 30); MotionEvent ev = MotionEvent.obtain(0, 0, 0, 12 /* x */, 5 /* y */, 0); @@ -81,7 +86,7 @@ public class NearestTouchFrameTest extends SysuiTestCase { mNearestTouchFrame.addView(left); mNearestTouchFrame.addView(right); - mNearestTouchFrame.onMeasure(0, 0); + mNearestTouchFrame.layout(0, 0, 30, 30); MotionEvent ev = MotionEvent.obtain(0, 0, 0, 12 /* x */, 5 /* y */, 0); @@ -100,7 +105,7 @@ public class NearestTouchFrameTest extends SysuiTestCase { mNearestTouchFrame.addView(left); mNearestTouchFrame.addView(right); - mNearestTouchFrame.onMeasure(0, 0); + mNearestTouchFrame.layout(0, 0, 30, 30); // Would go to left view if attached, but goes to right instead as left should be detached. MotionEvent ev = MotionEvent.obtain(0, 0, 0, @@ -117,7 +122,7 @@ public class NearestTouchFrameTest extends SysuiTestCase { mNearestTouchFrame.addView(left); mNearestTouchFrame.addView(right); - mNearestTouchFrame.onMeasure(0, 0); + mNearestTouchFrame.layout(0, 0, 30, 30); MotionEvent ev = MotionEvent.obtain(0, 0, 0, 12 /* x */, 5 /* y */, 0); @@ -133,7 +138,7 @@ public class NearestTouchFrameTest extends SysuiTestCase { mNearestTouchFrame.addView(left); mNearestTouchFrame.addView(right); - mNearestTouchFrame.onMeasure(0, 0); + mNearestTouchFrame.layout(0, 0, 30, 30); MotionEvent ev = MotionEvent.obtain(0, 0, 0, 18 /* x */, 5 /* y */, 0); @@ -146,10 +151,10 @@ public class NearestTouchFrameTest extends SysuiTestCase { public void testVerticalSelection_Top() { View top = mockViewAt(0, 0, 10, 10); View bottom = mockViewAt(0, 20, 10, 10); - + mNearestTouchFrame.setIsVertical(true); mNearestTouchFrame.addView(top); mNearestTouchFrame.addView(bottom); - mNearestTouchFrame.onMeasure(0, 0); + mNearestTouchFrame.layout(0, 0, 30, 30); MotionEvent ev = MotionEvent.obtain(0, 0, 0, 5 /* x */, 12 /* y */, 0); @@ -162,10 +167,10 @@ public class NearestTouchFrameTest extends SysuiTestCase { public void testVerticalSelection_Bottom() { View top = mockViewAt(0, 0, 10, 10); View bottom = mockViewAt(0, 20, 10, 10); - + mNearestTouchFrame.setIsVertical(true); mNearestTouchFrame.addView(top); mNearestTouchFrame.addView(bottom); - mNearestTouchFrame.onMeasure(0, 0); + mNearestTouchFrame.layout(0, 0, 30, 30); MotionEvent ev = MotionEvent.obtain(0, 0, 0, 5 /* x */, 18 /* y */, 0); @@ -179,7 +184,7 @@ public class NearestTouchFrameTest extends SysuiTestCase { View view = mockViewAt(0, 20, 10, 10); when(view.isAttachedToWindow()).thenReturn(false); mNearestTouchFrame.addView(view); - mNearestTouchFrame.onMeasure(0, 0); + mNearestTouchFrame.layout(0, 0, 30, 30); MotionEvent ev = MotionEvent.obtain(0, 0, 0, 5 /* x */, 18 /* y */, 0); mNearestTouchFrame.onTouchEvent(ev); @@ -187,6 +192,56 @@ public class NearestTouchFrameTest extends SysuiTestCase { ev.recycle(); } + @Test + public void testViewMiddleChildNotAttachedCrash() { + View view1 = mockViewAt(0, 20, 10, 10); + View view2 = mockViewAt(11, 20, 10, 10); + View view3 = mockViewAt(21, 20, 10, 10); + when(view2.isAttachedToWindow()).thenReturn(false); + mNearestTouchFrame.addView(view1); + mNearestTouchFrame.addView(view2); + mNearestTouchFrame.addView(view3); + mNearestTouchFrame.layout(0, 0, 30, 30); + + MotionEvent ev = MotionEvent.obtain(0, 0, 0, 5 /* x */, 18 /* y */, 0); + mNearestTouchFrame.onTouchEvent(ev); + verify(view2, never()).onTouchEvent(eq(ev)); + ev.recycle(); + } + + @Test + public void testCachedRegionsSplit_horizontal() { + View left = mockViewAt(0, 0, 5, 20); + View right = mockViewAt(15, 0, 5, 20); + mNearestTouchFrame.addView(left); + mNearestTouchFrame.addView(right); + mNearestTouchFrame.layout(0, 0, 20, 20); + + Map<View, Rect> childRegions = mNearestTouchFrame.getFullTouchableChildRegions(); + assertEquals(2, childRegions.size()); + Rect leftRegion = childRegions.get(left); + Rect rightRegion = childRegions.get(right); + assertEquals(new Rect(0, 0, 9, 20), leftRegion); + assertEquals(new Rect(10, 0, 20, 20), rightRegion); + } + + @Test + public void testCachedRegionsSplit_vertical() { + View top = mockViewAt(0, 0, 20, 5); + View bottom = mockViewAt(0, 15, 20, 5); + mNearestTouchFrame.addView(top); + mNearestTouchFrame.addView(bottom); + mNearestTouchFrame.setIsVertical(true); + mNearestTouchFrame.layout(0, 0, 20, 20); + + Map<View, Rect> childRegions = mNearestTouchFrame.getFullTouchableChildRegions(); + assertEquals(2, childRegions.size()); + Rect topRegion = childRegions.get(top); + Rect bottomRegion = childRegions.get(bottom); + assertEquals(new Rect(0, 0, 20, 9), topRegion); + assertEquals(new Rect(0, 10, 20, 20), bottomRegion); + } + private View mockViewAt(int x, int y, int width, int height) { View v = spy(new View(mContext)); doAnswer(invocation -> { @@ -197,6 +252,8 @@ public class NearestTouchFrameTest extends SysuiTestCase { }).when(v).getLocationInWindow(any()); when(v.isClickable()).thenReturn(true); when(v.isAttachedToWindow()).thenReturn(true); + when(v.getWidth()).thenReturn(width); + when(v.getHeight()).thenReturn(height); // Stupid final methods. v.setLeft(0); diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java index d79155cbb2fc..2a4b41cbfe32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -16,6 +16,7 @@ package com.android.systemui.people; +import static android.app.Notification.CATEGORY_MISSED_CALL; import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY; import static android.app.people.ConversationStatus.ACTIVITY_GAME; import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY; @@ -113,6 +114,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { private static final Uri URI = Uri.parse("fake_uri"); private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android); private static final String GAME_DESCRIPTION = "Playing a game!"; + private static final CharSequence MISSED_CALL = "Custom missed call message"; private static final String NAME = "username"; private static final Person PERSON = new Person.Builder() .setName("name") @@ -346,7 +348,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .build(); Notification.MessagingStyle.Message lastMessage = - PeopleSpaceUtils.getLastMessagingStyleMessage(sbn); + PeopleSpaceUtils.getLastMessagingStyleMessage(sbn.getNotification()); assertThat(lastMessage).isNull(); } @@ -447,7 +449,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .build(); Notification.MessagingStyle.Message lastMessage = - PeopleSpaceUtils.getLastMessagingStyleMessage(sbn); + PeopleSpaceUtils.getLastMessagingStyleMessage(sbn.getNotification()); assertThat(lastMessage.getText().toString()).isEqualTo(NOTIFICATION_TEXT_2); } @@ -462,10 +464,10 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(0) + .setUserHandle(new UserHandle(0)) .build(); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromNotification(tile, sbn); + .augmentTileFromNotification(mContext, tile, sbn); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); } @@ -480,12 +482,11 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID_3, "userName", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(0) + .setUserHandle(new UserHandle(0)) .build(); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromNotification(tile, sbn); + .augmentTileFromNotification(mContext, tile, sbn); - assertThat(actual.getNotificationKey()).isEqualTo(null); assertThat(actual.getNotificationContent()).isEqualTo(null); } @@ -495,10 +496,10 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(0) + .setUserHandle(new UserHandle(0)) .build(); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromVisibleNotifications(tile, + .augmentTileFromVisibleNotifications(mContext, tile, Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1)); assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2); @@ -510,10 +511,10 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID_4, "userName", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(0) + .setUserHandle(new UserHandle(0)) .build(); PeopleSpaceTile actual = PeopleSpaceUtils - .augmentTileFromVisibleNotifications(tile, + .augmentTileFromVisibleNotifications(mContext, tile, Map.of(PeopleSpaceUtils.getKey(mNotificationEntry1), mNotificationEntry1)); assertThat(actual.getNotificationContent()).isEqualTo(null); @@ -525,10 +526,11 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(0) + .setUserHandle(new UserHandle(0)) .build(); List<PeopleSpaceTile> actualList = PeopleSpaceUtils - .augmentTilesFromVisibleNotifications(List.of(tile), mNotificationEntryManager); + .augmentTilesFromVisibleNotifications( + mContext, List.of(tile), mNotificationEntryManager); assertThat(actualList.size()).isEqualTo(1); assertThat(actualList.get(0).getNotificationContent().toString()) @@ -543,16 +545,16 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(1) + .setUserHandle(new UserHandle(0)) .build(); PeopleSpaceTile tile2 = new PeopleSpaceTile .Builder(SHORTCUT_ID_2, "userName2", ICON, new Intent()) .setPackageName(PACKAGE_NAME) - .setUid(0) + .setUserHandle(new UserHandle(0)) .build(); List<PeopleSpaceTile> actualList = PeopleSpaceUtils - .augmentTilesFromVisibleNotifications(List.of(tile1, tile2), + .augmentTilesFromVisibleNotifications(mContext, List.of(tile1, tile2), mNotificationEntryManager); assertThat(actualList.size()).isEqualTo(2); @@ -763,6 +765,33 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { } @Test + public void testCreateRemoteViewsWithMissedCallNotification() { + PeopleSpaceTile tileWithMissedCallNotification = PERSON_TILE.toBuilder() + .setNotificationDataUri(null) + .setNotificationCategory(CATEGORY_MISSED_CALL) + .setNotificationContent(MISSED_CALL) + .build(); + RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext, + tileWithMissedCallNotification, 0); + View result = views.apply(mContext, null); + + TextView name = (TextView) result.findViewById(R.id.name); + assertEquals(name.getText(), NAME); + // Has availability. + View availability = result.findViewById(R.id.availability); + assertEquals(View.GONE, availability.getVisibility()); + // Has new story. + View personIcon = result.findViewById(R.id.person_icon_only); + View personIconWithStory = result.findViewById(R.id.person_icon_with_story); + assertEquals(View.VISIBLE, personIcon.getVisibility()); + assertEquals(View.GONE, personIconWithStory.getVisibility()); + // Has status. + TextView statusContent = (TextView) result.findViewById(R.id.status); + assertEquals(statusContent.getText(), MISSED_CALL); + } + + + @Test public void testCreateRemoteViewsWithNotificationTemplate() { PeopleSpaceTile tileWithStatusAndNotification = PERSON_TILE.toBuilder() .setNotificationDataUri(null) diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index 9470141178dd..800d8593035d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.people.widget; +import static android.app.Notification.CATEGORY_MISSED_CALL; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; @@ -108,7 +109,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { new PeopleSpaceTile .Builder(SHORTCUT_ID, "username", ICON, new Intent()) .setPackageName(TEST_PACKAGE_A) - .setUid(0) + .setUserHandle(new UserHandle(1)) .setNotificationKey(NOTIFICATION_KEY + "1") .setNotificationContent(NOTIFICATION_CONTENT) .setNotificationDataUri(URI) @@ -167,7 +168,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { int[] widgetIdsArray = {}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID); + StatusBarNotification sbn = createNotification( + OTHER_SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) .setId(1)); @@ -207,7 +209,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbnWithoutPackageName = new SbnBuilder() - .setNotification(createMessagingStyleNotification(SHORTCUT_ID)) + .setNotification(createMessagingStyleNotification( + SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false)) .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbnWithoutPackageName) @@ -256,7 +259,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID); + StatusBarNotification sbn = createNotification( + OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) .setId(1)); @@ -276,7 +280,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder() - .setNotification(createMessagingStyleNotification(SHORTCUT_ID)) + .setNotification(createMessagingStyleNotification( + SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ false)) .setPkg(TEST_PACKAGE_B) .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() @@ -295,7 +300,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID); + StatusBarNotification sbn = createNotification( + OTHER_SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) .setId(1)); @@ -315,7 +321,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder() - .setNotification(createMessagingStyleNotification(SHORTCUT_ID)) + .setNotification(createMessagingStyleNotification( + SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false)) .setPkg(TEST_PACKAGE_B) .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() @@ -337,7 +344,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setSbn(createConversationNotification(SHORTCUT_ID)) + .setSbn(createNotification( + SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false)) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); @@ -367,7 +375,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setSbn(createConversationNotification(SHORTCUT_ID)) + .setSbn(createNotification( + SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false)) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); @@ -400,7 +409,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setSbn(createConversationNotification(SHORTCUT_ID)) + .setSbn(createNotification( + SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false)) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); @@ -417,33 +427,52 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile() + public void testUpdateMissedCallNotificationWithoutContentPostedIfExistingTile() throws Exception { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); - Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext) - .setContentTitle("TEST_TITLE") - .setContentText("TEST_TEXT") - .setShortcutId(SHORTCUT_ID) - .build(); - StatusBarNotification sbn = new SbnBuilder() - .setNotification(notificationWithoutMessagingStyle) - .setPkg(TEST_PACKAGE_A) - .setUid(0) - .build(); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() - .setSbn(sbn) + .setSbn(createNotification( + SHORTCUT_ID, /* isMessagingStyle = */ false, /* isMissedCall = */ true)) .setId(1)); mClock.advanceTime(MIN_LINGER_DURATION); verify(mAppWidgetManager, times(1)) .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), mBundleArgumentCaptor.capture()); - Bundle options = requireNonNull(mBundleArgumentCaptor.getValue()); - assertThat((PeopleSpaceTile) options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE)) - .isEqualTo(PERSON_TILE); + Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue()); + + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY); + assertThat(tile.getNotificationContent()) + .isEqualTo(mContext.getString(R.string.missed_call)); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test + public void testUpdateMissedCallNotificationWithContentPostedIfExistingTile() + throws Exception { + int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; + when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT); + + NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() + .setSbn(createNotification( + SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ true)) + .setId(1)); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, times(1)) + .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), + mBundleArgumentCaptor.capture()); + Bundle bundle = requireNonNull(mBundleArgumentCaptor.getValue()); + + PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE); + assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY); + assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT); verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), any()); } @@ -453,7 +482,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT}; when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); - StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID); + StatusBarNotification sbn = createNotification( + SHORTCUT_ID, /* isMessagingStyle = */ true, /* isMissedCall = */ false); NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder() .setSbn(sbn) .setId(1)); @@ -483,21 +513,29 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { return convo; } - private Notification createMessagingStyleNotification(String shortcutId) { - return new Notification.Builder(mContext) + private Notification createMessagingStyleNotification(String shortcutId, + boolean isMessagingStyle, boolean isMissedCall) { + Notification.Builder builder = new Notification.Builder(mContext) .setContentTitle("TEST_TITLE") .setContentText("TEST_TEXT") - .setShortcutId(shortcutId) - .setStyle(new Notification.MessagingStyle(PERSON) - .addMessage( - new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10, - PERSON)) - ) - .build(); + .setShortcutId(shortcutId); + if (isMessagingStyle) { + builder.setStyle(new Notification.MessagingStyle(PERSON) + .addMessage( + new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10, + PERSON)) + ); + } + if (isMissedCall) { + builder.setCategory(CATEGORY_MISSED_CALL); + } + return builder.build(); } - private StatusBarNotification createConversationNotification(String shortcutId) { - Notification notification = createMessagingStyleNotification(shortcutId); + private StatusBarNotification createNotification(String shortcutId, + boolean isMessagingStyle, boolean isMissedCall) { + Notification notification = createMessagingStyleNotification( + shortcutId, isMessagingStyle, isMissedCall); return new SbnBuilder() .setNotification(notification) .setPkg(TEST_PACKAGE_A) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java index 2dfd38832997..d40e6a4586b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java @@ -89,6 +89,8 @@ public class QSFooterViewControllerTest extends LeakCheckedTest { private View mEdit; @Mock private MultiUserSwitch mMultiUserSwitch; + @Mock + private View mPowerMenuLiteView; private QSFooterViewController mController; @@ -111,11 +113,12 @@ public class QSFooterViewControllerTest extends LeakCheckedTest { when(mView.findViewById(R.id.build)).thenReturn(mBuildText); when(mView.findViewById(android.R.id.edit)).thenReturn(mEdit); when(mView.findViewById(R.id.multi_user_switch)).thenReturn(mMultiUserSwitch); + when(mView.findViewById(R.id.pm_lite)).thenReturn(mPowerMenuLiteView); mController = new QSFooterViewController(mView, mUserManager, mUserInfoController, mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController, new QSDetailDisplayer(), mQuickQSPanelController, mFakeTunerService, - mMetricsLogger); + mMetricsLogger, false); mController.init(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java index 0dfebab59feb..a2a179ea29df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java @@ -121,7 +121,6 @@ public class QSPanelControllerTest extends SysuiTestCase { mQSTileHost, mQSCustomizerController, true, mMediaHost, mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger, mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory, - /* labelsFlag */ false, mFeatureFlags ); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt index 587020090433..cb380d51bd79 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt @@ -87,7 +87,6 @@ class QuickQSPanelControllerTest : SysuiTestCase() { uiEventLogger, qsLogger, dumpManager, - false, featureFlags ) 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 e855834dcad4..5a1bd5f72a02 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 @@ -33,9 +33,10 @@ import android.widget.TextView; import androidx.test.filters.SmallTest; -import com.android.keyguard.CarrierTextManager; +import com.android.keyguard.CarrierTextController; 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.utils.leaks.LeakCheckedTest; import com.android.systemui.utils.os.FakeHandler; @@ -54,7 +55,7 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { private QSCarrierGroupController mQSCarrierGroupController; private NetworkController.SignalCallback mSignalCallback; - private CarrierTextManager.CarrierTextCallback mCallback; + private CarrierTextController.CarrierTextCallback mCallback; @Mock private QSCarrierGroup mQSCarrierGroup; @Mock @@ -62,9 +63,9 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { @Mock private NetworkController mNetworkController; @Mock - private CarrierTextManager.Builder mCarrierTextControllerBuilder; + private CarrierTextController.Builder mCarrierTextControllerBuilder; @Mock - private CarrierTextManager mCarrierTextManager; + private CarrierTextController mCarrierTextController; private TestableLooper mTestableLooper; @Before @@ -83,11 +84,11 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { .thenReturn(mCarrierTextControllerBuilder); when(mCarrierTextControllerBuilder.setShowMissingSim(anyBoolean())) .thenReturn(mCarrierTextControllerBuilder); - when(mCarrierTextControllerBuilder.build()).thenReturn(mCarrierTextManager); + when(mCarrierTextControllerBuilder.build()).thenReturn(mCarrierTextController); doAnswer(invocation -> mCallback = invocation.getArgument(0)) - .when(mCarrierTextManager) - .setListening(any(CarrierTextManager.CarrierTextCallback.class)); + .when(mCarrierTextController) + .setListening(any(CarrierTextController.CarrierTextCallback.class)); when(mQSCarrierGroup.getNoSimTextView()).thenReturn(new TextView(mContext)); when(mQSCarrierGroup.getCarrier1View()).thenReturn(mock(QSCarrier.class)); @@ -113,8 +114,8 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { (Answer<Integer>) invocationOnMock -> invocationOnMock.getArgument(0)); // listOfCarriers length 1, subscriptionIds length 1, anySims false - CarrierTextManager.CarrierTextCallbackInfo - c1 = new CarrierTextManager.CarrierTextCallbackInfo( + CarrierTextController.CarrierTextCallbackInfo + c1 = new CarrierTextController.CarrierTextCallbackInfo( "", new CharSequence[]{""}, false, @@ -122,8 +123,8 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { mCallback.updateCarrierInfo(c1); // listOfCarriers length 1, subscriptionIds length 1, anySims true - CarrierTextManager.CarrierTextCallbackInfo - c2 = new CarrierTextManager.CarrierTextCallbackInfo( + CarrierTextController.CarrierTextCallbackInfo + c2 = new CarrierTextController.CarrierTextCallbackInfo( "", new CharSequence[]{""}, true, @@ -131,8 +132,8 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { mCallback.updateCarrierInfo(c2); // listOfCarriers length 2, subscriptionIds length 2, anySims false - CarrierTextManager.CarrierTextCallbackInfo - c3 = new CarrierTextManager.CarrierTextCallbackInfo( + CarrierTextController.CarrierTextCallbackInfo + c3 = new CarrierTextController.CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, false, @@ -140,8 +141,8 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { mCallback.updateCarrierInfo(c3); // listOfCarriers length 2, subscriptionIds length 2, anySims true - CarrierTextManager.CarrierTextCallbackInfo - c4 = new CarrierTextManager.CarrierTextCallbackInfo( + CarrierTextController.CarrierTextCallbackInfo + c4 = new CarrierTextController.CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, true, @@ -159,8 +160,8 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { (Answer<Integer>) invocationOnMock -> invocationOnMock.getArgument(0)); // listOfCarriers length 2, subscriptionIds length 1, anySims false - CarrierTextManager.CarrierTextCallbackInfo - c1 = new CarrierTextManager.CarrierTextCallbackInfo( + CarrierTextController.CarrierTextCallbackInfo + c1 = new CarrierTextController.CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, false, @@ -168,8 +169,8 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { mCallback.updateCarrierInfo(c1); // listOfCarriers length 2, subscriptionIds length 1, anySims true - CarrierTextManager.CarrierTextCallbackInfo - c2 = new CarrierTextManager.CarrierTextCallbackInfo( + CarrierTextController.CarrierTextCallbackInfo + c2 = new CarrierTextController.CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, true, @@ -177,8 +178,8 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { mCallback.updateCarrierInfo(c2); // listOfCarriers length 1, subscriptionIds length 2, anySims false - CarrierTextManager.CarrierTextCallbackInfo - c3 = new CarrierTextManager.CarrierTextCallbackInfo( + CarrierTextController.CarrierTextCallbackInfo + c3 = new CarrierTextController.CarrierTextCallbackInfo( "", new CharSequence[]{""}, false, @@ -186,8 +187,8 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { mCallback.updateCarrierInfo(c3); // listOfCarriers length 1, subscriptionIds length 2, anySims true - CarrierTextManager.CarrierTextCallbackInfo - c4 = new CarrierTextManager.CarrierTextCallbackInfo( + CarrierTextController.CarrierTextCallbackInfo + c4 = new CarrierTextController.CarrierTextCallbackInfo( "", new CharSequence[]{""}, true, @@ -203,8 +204,8 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { when(spiedCarrierGroupController.getSlotIndex(anyInt())).thenReturn( SubscriptionManager.INVALID_SIM_SLOT_INDEX); - CarrierTextManager.CarrierTextCallbackInfo - c4 = new CarrierTextManager.CarrierTextCallbackInfo( + CarrierTextController.CarrierTextCallbackInfo + c4 = new CarrierTextController.CarrierTextCallbackInfo( "", new CharSequence[]{"", ""}, true, @@ -215,16 +216,17 @@ public class QSCarrierGroupControllerTest extends LeakCheckedTest { @Test // throws no Exception public void testSetMobileDataIndicators_invalidSim() { - mSignalCallback.setMobileDataIndicators( + MobileDataIndicators indicators = new MobileDataIndicators( mock(NetworkController.IconState.class), mock(NetworkController.IconState.class), 0, 0, true, true, "", "", "", true, 0, true, true); + mSignalCallback.setMobileDataIndicators(indicators); } @Test public void testNoEmptyVisibleView_airplaneMode() { - CarrierTextManager.CarrierTextCallbackInfo - info = new CarrierTextManager.CarrierTextCallbackInfo( + CarrierTextController.CarrierTextCallbackInfo + info = new CarrierTextController.CarrierTextCallbackInfo( "", new CharSequence[]{""}, true, 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 faf43a21356e..1c29a8174359 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 @@ -46,6 +46,7 @@ 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; @@ -134,9 +135,11 @@ public class CastTileTest extends SysuiTestCase { public void testStateUnavailable_wifiDisabled() { NetworkController.IconState qsIcon = new NetworkController.IconState(false, 0, ""); - mSignalCallback.setWifiIndicators(false, mock(NetworkController.IconState.class), + WifiIndicators indicators = new WifiIndicators( + false, mock(NetworkController.IconState.class), qsIcon, false,false, "", false, ""); + mSignalCallback.setWifiIndicators(indicators); mTestableLooper.processAllMessages(); assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state); @@ -146,9 +149,11 @@ public class CastTileTest extends SysuiTestCase { public void testStateUnavailable_wifiNotConnected() { NetworkController.IconState qsIcon = new NetworkController.IconState(false, 0, ""); - mSignalCallback.setWifiIndicators(true, mock(NetworkController.IconState.class), + WifiIndicators indicators = new WifiIndicators( + true, mock(NetworkController.IconState.class), qsIcon, false,false, "", false, ""); + mSignalCallback.setWifiIndicators(indicators); mTestableLooper.processAllMessages(); assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state); @@ -157,9 +162,11 @@ public class CastTileTest extends SysuiTestCase { private void enableWifiAndProcessMessages() { NetworkController.IconState qsIcon = new NetworkController.IconState(true, 0, ""); - mSignalCallback.setWifiIndicators(true, mock(NetworkController.IconState.class), + WifiIndicators indicators = new WifiIndicators( + true, mock(NetworkController.IconState.class), qsIcon, false,false, "", false, ""); + mSignalCallback.setWifiIndicators(indicators); mTestableLooper.processAllMessages(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt index d3dbe2bf7d2b..ccd9548b269f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt @@ -25,7 +25,6 @@ import androidx.lifecycle.LifecycleOwner import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger import com.android.internal.logging.UiEventLogger -import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.controller.ControlsController @@ -37,9 +36,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.logging.QSLogger -import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.FeatureFlags -import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture import com.android.systemui.util.settings.FakeSettings @@ -49,7 +46,6 @@ import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Answers import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock @@ -57,6 +53,7 @@ import org.mockito.Mockito.`when` import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import java.util.Optional @SmallTest @RunWith(AndroidTestingRunner::class) @@ -73,6 +70,7 @@ class DeviceControlsTileTest : SysuiTestCase() { private lateinit var activityStarter: ActivityStarter @Mock private lateinit var qsLogger: QSLogger + @Mock private lateinit var controlsComponent: ControlsComponent @Mock private lateinit var controlsUiController: ControlsUiController @@ -96,54 +94,64 @@ class DeviceControlsTileTest : SysuiTestCase() { private lateinit var testableLooper: TestableLooper private lateinit var tile: DeviceControlsTile - @Mock - private lateinit var keyguardStateController: KeyguardStateController - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private lateinit var userTracker: UserTracker - @Mock - private lateinit var lockPatternUtils: LockPatternUtils - @Mock private lateinit var secureSettings: SecureSettings + private var featureEnabled = true @Before fun setUp() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) + secureSettings = FakeSettings() `when`(qsHost.context).thenReturn(mContext) `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger) + `when`(controlsController.available).thenReturn(true) + `when`(controlsComponent.isEnabled()).thenReturn(true) + secureSettings.putInt(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 1) - controlsComponent = ControlsComponent( - true, - mContext, - { controlsController }, - { controlsUiController }, - { controlsListingController }, - lockPatternUtils, - keyguardStateController, - userTracker, - secureSettings - ) + setupControlsComponent() globalSettings = FakeSettings() globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 1) `when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(true) - `when`(userTracker.userHandle.identifier).thenReturn(0) - tile = createTile() } + private fun setupControlsComponent() { + `when`(controlsComponent.getControlsController()).thenAnswer { + if (featureEnabled) { + Optional.of(controlsController) + } else { + Optional.empty() + } + } + + `when`(controlsComponent.getControlsListingController()).thenAnswer { + if (featureEnabled) { + Optional.of(controlsListingController) + } else { + Optional.empty() + } + } + + `when`(controlsComponent.getControlsUiController()).thenAnswer { + if (featureEnabled) { + Optional.of(controlsUiController) + } else { + Optional.empty() + } + } + } + @Test fun testAvailable() { - `when`(controlsController.available).thenReturn(true) assertThat(tile.isAvailable).isTrue() } @Test fun testNotAvailableFeature() { - `when`(controlsController.available).thenReturn(true) `when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(false) assertThat(tile.isAvailable).isFalse() @@ -151,24 +159,22 @@ class DeviceControlsTileTest : SysuiTestCase() { @Test fun testNotAvailableControls() { - controlsComponent = ControlsComponent( - false, - mContext, - { controlsController }, - { controlsUiController }, - { controlsListingController }, - lockPatternUtils, - keyguardStateController, - userTracker, - secureSettings - ) + featureEnabled = false tile = createTile() assertThat(tile.isAvailable).isFalse() } @Test - fun testNotAvailableFlag() { + fun testAvailableControlsSettingOff() { + `when`(controlsController.available).thenReturn(false) + + tile = createTile() + assertThat(tile.isAvailable).isTrue() + } + + @Test + fun testNotAvailableControlsLockscreenFlag() { globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 0) tile = createTile() @@ -207,11 +213,26 @@ class DeviceControlsTileTest : SysuiTestCase() { } @Test + fun testStateUnavailableIfNotEnabled() { + verify(controlsListingController).observe( + any(LifecycleOwner::class.java), + capture(listingCallbackCaptor) + ) + `when`(controlsComponent.isEnabled()).thenReturn(false) + + listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo)) + testableLooper.processAllMessages() + + assertThat(tile.state.state).isEqualTo(Tile.STATE_UNAVAILABLE) + } + + @Test fun testStateAvailableIfListings() { verify(controlsListingController).observe( any(LifecycleOwner::class.java), capture(listingCallbackCaptor) ) + `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE) listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo)) testableLooper.processAllMessages() @@ -220,6 +241,21 @@ class DeviceControlsTileTest : SysuiTestCase() { } @Test + fun testStateInactiveIfLocked() { + verify(controlsListingController).observe( + any(LifecycleOwner::class.java), + capture(listingCallbackCaptor) + ) + `when`(controlsComponent.getVisibility()) + .thenReturn(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK) + + listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo)) + testableLooper.processAllMessages() + + assertThat(tile.state.state).isEqualTo(Tile.STATE_INACTIVE) + } + + @Test fun testMoveBetweenStates() { verify(controlsListingController).observe( any(LifecycleOwner::class.java), @@ -249,6 +285,7 @@ class DeviceControlsTileTest : SysuiTestCase() { any(LifecycleOwner::class.java), capture(listingCallbackCaptor) ) + `when`(controlsComponent.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE) listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo)) testableLooper.processAllMessages() @@ -260,6 +297,24 @@ class DeviceControlsTileTest : SysuiTestCase() { } @Test + fun testNoDialogWhenInactive() { + verify(controlsListingController).observe( + any(LifecycleOwner::class.java), + capture(listingCallbackCaptor) + ) + `when`(controlsComponent.getVisibility()) + .thenReturn(ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK) + + listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo)) + testableLooper.processAllMessages() + + tile.click() + testableLooper.processAllMessages() + + verify(controlsDialog, never()).show(any(ControlsUiController::class.java)) + } + + @Test fun testDialogDismissedOnDestroy() { verify(controlsListingController).observe( any(LifecycleOwner::class.java), diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java index a75c39c33f14..9e62a6263a43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java @@ -24,6 +24,7 @@ import android.graphics.Paint; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; +import android.os.ICancellationSignal; import android.os.RemoteException; import android.view.IScrollCaptureCallbacks; import android.view.IScrollCaptureConnection; @@ -46,7 +47,7 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { } @Override - public void startCapture(Surface surface) { + public ICancellationSignal startCapture(Surface surface) { mSurface = surface; mHwuiContext = new HwuiContext(false, surface); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -56,27 +57,28 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { } catch (RemoteException e) { e.rethrowAsRuntimeException(); } + return null; } @Override - public void requestImage(Rect rect) { + public ICancellationSignal requestImage(Rect rect) { Canvas canvas = mHwuiContext.lockCanvas(rect.width(), rect.height()); mPaint.setColor(mColors[mNextColor]); canvas.drawRect(rect, mPaint); mNextColor = (mNextColor++) % mColors.length; - long frameNumber = mSurface.getNextFrameNumber(); mHwuiContext.unlockAndPost(canvas); try { - mCallbacks.onCaptureBufferSent(frameNumber, rect); + mCallbacks.onImageRequestCompleted(0, rect); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } + return null; } @Override - public void endCapture() { + public ICancellationSignal endCapture() { try { - mCallbacks.onConnectionClosed(); + mCallbacks.onCaptureEnded(); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } finally { @@ -84,6 +86,12 @@ class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { mSurface = null; mCallbacks = null; } + return null; + } + + @Override + public void close() throws RemoteException { + } // From android.view.Surface, but issues render requests synchronously with waitForPresent(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java index 580f800fbc44..802b462ec10e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java @@ -29,7 +29,6 @@ import static org.mockito.Mockito.verify; import static java.util.Objects.requireNonNull; import android.content.Context; -import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.RemoteException; @@ -37,6 +36,7 @@ import android.testing.AndroidTestingRunner; import android.view.Display; import android.view.IScrollCaptureCallbacks; import android.view.IWindowManager; +import android.view.ScrollCaptureResponse; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; @@ -82,10 +82,11 @@ public class ScrollCaptureClientTest extends SysuiTestCase { public void testBasicClientFlow() throws RemoteException { doAnswer((Answer<Void>) invocation -> { IScrollCaptureCallbacks cb = invocation.getArgument(3); - cb.onConnected( - new FakeScrollCaptureConnection(cb), - /* scrollBounds */ new Rect(0, 0, 100, 100), - /* positionInWindow */ new Point(0, 0)); + cb.onScrollCaptureResponse(new ScrollCaptureResponse.Builder() + .setBoundsInWindow(new Rect(0, 0, 100, 100)) + .setWindowBounds(new Rect(0, 0, 100, 100)) + .setConnection(new FakeScrollCaptureConnection(cb)) + .build()); return null; }).when(mWm).requestScrollCapture(/* displayId */ anyInt(), /* token */ isNull(), /* taskId */ anyInt(), any(IScrollCaptureCallbacks.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java index 2b3ca7c00a49..6564d588f4ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java @@ -19,15 +19,14 @@ package com.android.systemui.screenshot; import static org.junit.Assert.fail; import android.content.Intent; -import android.graphics.Point; import android.graphics.Rect; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.util.Log; import android.view.Display; import android.view.IScrollCaptureCallbacks; -import android.view.IScrollCaptureConnection; import android.view.IWindowManager; +import android.view.ScrollCaptureResponse; import android.view.WindowManagerGlobal; import androidx.test.filters.SmallTest; @@ -67,31 +66,27 @@ public class ScrollCaptureTest extends SysuiTestCase { wms.requestScrollCapture(Display.DEFAULT_DISPLAY, null, -1, new IScrollCaptureCallbacks.Stub() { @Override - public void onConnected( - IScrollCaptureConnection connection, Rect scrollBounds, - Point positionInWindow) { - Log.d(TAG, - "client connected: " + connection + "[scrollBounds= " - + scrollBounds + ", " - + "positionInWindow=" + positionInWindow + "]"); + public void onScrollCaptureResponse(ScrollCaptureResponse response) + throws RemoteException { + Log.d(TAG, "onScrollCaptureResponse: " + response); latch.countDown(); } @Override - public void onUnavailable() { - } - - @Override public void onCaptureStarted() { } @Override - public void onCaptureBufferSent(long frameNumber, Rect capturedArea) { + public void onImageRequestCompleted(int i, Rect rect) + throws RemoteException { + } @Override - public void onConnectionClosed() { + public void onCaptureEnded() throws RemoteException { + } + }); } catch (RemoteException e) { Log.e(TAG, "request failed", e); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 0cae67427e3b..7ff056ead653 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE; @@ -40,6 +42,7 @@ import android.app.Instrumentation; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.UserInfo; @@ -94,8 +97,12 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private static final String ORGANIZATION_NAME = "organization"; + private static final ComponentName DEVICE_OWNER_COMPONENT = new ComponentName("com.android.foo", + "bar"); + private String mDisclosureWithOrganization; private String mDisclosureGeneric; + private String mFinancedDisclosureWithOrganization; @Mock private DevicePolicyManager mDevicePolicyManager; @@ -156,6 +163,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name, ORGANIZATION_NAME); mDisclosureGeneric = mContext.getString(R.string.do_disclosure_generic); + mFinancedDisclosureWithOrganization = mContext.getString( + R.string.do_financed_disclosure_with_name, ORGANIZATION_NAME); when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true); @@ -165,6 +174,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { .thenReturn(mIndicationAreaBottom); when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView); + when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()) + .thenReturn(DEVICE_OWNER_COMPONENT); + when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_DEFAULT); + mWakeLock = new WakeLockFake(); mWakeLockBuilder = new WakeLockFake.Builder(mContext); mWakeLockBuilder.setWakeLock(mWakeLock); @@ -372,6 +386,22 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test + public void disclosure_deviceOwner_financedDeviceWithOrganizationName() { + createController(); + + when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true); + when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME); + when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_FINANCED); + sendUpdateDisclosureBroadcast(); + + verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE), + mKeyguardIndicationCaptor.capture(), eq(false)); + assertThat(mKeyguardIndicationCaptor.getValue().getMessage()) + .isEqualTo(mFinancedDisclosureWithOrganization); + } + + @Test public void transientIndication_holdsWakeLock_whenDozing() { createController(); @@ -609,6 +639,16 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { assertThat(mTextView.getText()).isEqualTo(percentage); } + @Test + public void onRequireUnlockForNfc_showsRequireUnlockForNfcIndication() { + createController(); + String message = mContext.getString(R.string.require_unlock_for_nfc); + mController.getKeyguardCallback().onRequireUnlockForNfc(); + mController.setVisible(true); + + assertThat(mTextView.getText()).isEqualTo(message); + } + private void sendUpdateDisclosureBroadcast() { mBroadcastReceiver.onReceive(mContext, new Intent()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java index 05cf33a3729c..b493b9a6bd65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java @@ -171,62 +171,10 @@ public class NotificationFilterTest extends SysuiTestCase { } @Test - public void testSuppressSystemAlertNotification() { - when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false); - when(mFsc.isSystemAlertNotification(any())).thenReturn(true); - StatusBarNotification sbn = mRow.getEntry().getSbn(); - Bundle bundle = new Bundle(); - bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"}); - sbn.getNotification().extras = bundle; - - assertTrue(mNotificationFilter.shouldFilterOut(mRow.getEntry())); - } - - @Test - public void testDoNotSuppressSystemAlertNotification() { - StatusBarNotification sbn = mRow.getEntry().getSbn(); - Bundle bundle = new Bundle(); - bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"}); - sbn.getNotification().extras = bundle; - - when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true); - when(mFsc.isSystemAlertNotification(any())).thenReturn(true); - - assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry())); - - when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true); - when(mFsc.isSystemAlertNotification(any())).thenReturn(false); - - assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry())); - - when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false); - when(mFsc.isSystemAlertNotification(any())).thenReturn(false); - - assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry())); - } - - @Test - public void testDoNotSuppressMalformedSystemAlertNotification() { - when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true); - - // missing extra - assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry())); - - StatusBarNotification sbn = mRow.getEntry().getSbn(); - Bundle bundle = new Bundle(); - bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{}); - sbn.getNotification().extras = bundle; - - // extra missing values - assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry())); - } - - @Test public void testShouldFilterHiddenNotifications() { initStatusBarNotification(false); // setup when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false); - when(mFsc.isSystemAlertNotification(any())).thenReturn(false); // test should filter out hidden notifications: // hidden diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java index ce0f1220fc88..9a5482c33501 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java @@ -377,8 +377,6 @@ public class NotifCollectionTest extends SysuiTestCase { // THEN we send the dismissal to system server verify(mStatusBarService).onNotificationClear( notif2.sbn.getPackageName(), - notif2.sbn.getTag(), - 88, notif2.sbn.getUser().getIdentifier(), notif2.sbn.getKey(), stats.dismissalSurface, @@ -528,8 +526,6 @@ public class NotifCollectionTest extends SysuiTestCase { // THEN we never send the dismissal to system server verify(mStatusBarService, never()).onNotificationClear( notif.sbn.getPackageName(), - notif.sbn.getTag(), - 47, notif.sbn.getUser().getIdentifier(), notif.sbn.getKey(), stats.dismissalSurface, @@ -566,8 +562,6 @@ public class NotifCollectionTest extends SysuiTestCase { // THEN the notification is never sent to system server to dismiss verify(mStatusBarService, never()).onNotificationClear( eq(notif.sbn.getPackageName()), - eq(notif.sbn.getTag()), - eq(47), eq(notif.sbn.getUser().getIdentifier()), eq(notif.sbn.getKey()), anyInt(), @@ -596,8 +590,6 @@ public class NotifCollectionTest extends SysuiTestCase { // THEN we send the dismissal to system server verify(mStatusBarService).onNotificationClear( eq(notif.sbn.getPackageName()), - eq(notif.sbn.getTag()), - eq(47), eq(notif.sbn.getUser().getIdentifier()), eq(notif.sbn.getKey()), anyInt(), @@ -1125,8 +1117,6 @@ public class NotifCollectionTest extends SysuiTestCase { // THEN we send the dismissals to system server verify(mStatusBarService).onNotificationClear( notif1.sbn.getPackageName(), - notif1.sbn.getTag(), - 47, notif1.sbn.getUser().getIdentifier(), notif1.sbn.getKey(), stats1.dismissalSurface, @@ -1135,8 +1125,6 @@ public class NotifCollectionTest extends SysuiTestCase { verify(mStatusBarService).onNotificationClear( notif2.sbn.getPackageName(), - notif2.sbn.getTag(), - 88, notif2.sbn.getUser().getIdentifier(), notif2.sbn.getKey(), stats2.dismissalSurface, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java index 09546216183f..278456859735 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java @@ -115,41 +115,6 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { } @Test - public void filterTest_systemAlertNotificationUnnecessary() { - // GIVEN the alert notification isn't needed for this user - final Bundle extras = new Bundle(); - extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS, - new String[]{TEST_PKG}); - mEntryBuilder.modifyNotification(mContext) - .setExtras(extras); - NotificationEntry entry = mEntryBuilder.build(); - StatusBarNotification sbn = entry.getSbn(); - when(mForegroundServiceController.isSystemAlertWarningNeeded(sbn.getUserId(), TEST_PKG)) - .thenReturn(false); - - // GIVEN the notification is a system alert notification + not a disclosure notification - when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(true); - when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false); - - - // THEN filter out the notification - assertTrue(mForegroundFilter.shouldFilterOut(entry, 0)); - } - - @Test - public void filterTest_doNotFilter() { - NotificationEntry entry = mEntryBuilder.build(); - StatusBarNotification sbn = entry.getSbn(); - - // GIVEN the notification isn't a system alert notification nor a disclosure notification - when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(false); - when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false); - - // THEN don't filter out the notification - assertFalse(mForegroundFilter.shouldFilterOut(entry, 0)); - } - - @Test public void testIncludeFGSInSection_importanceDefault() { // GIVEN the notification represents a colorized foreground service with > min importance mEntryBuilder diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java index 2e2945e28161..8375e7cceb28 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java @@ -58,8 +58,9 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; -import com.android.systemui.statusbar.policy.InflatedSmartReplies; -import com.android.systemui.statusbar.policy.SmartRepliesAndActionsInflater; +import com.android.systemui.statusbar.policy.InflatedSmartReplyState; +import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; +import com.android.systemui.statusbar.policy.SmartReplyStateInflater; import com.android.systemui.tests.R; import org.junit.Assert; @@ -87,11 +88,24 @@ public class NotificationContentInflaterTest extends SysuiTestCase { @Mock private NotifRemoteViewCache mCache; @Mock private ConversationNotificationProcessor mConversationNotificationProcessor; - @Mock private InflatedSmartReplies mInflatedSmartReplies; + @Mock private InflatedSmartReplyState mInflatedSmartReplyState; + @Mock private InflatedSmartReplyViewHolder mInflatedSmartReplies; + + private final SmartReplyStateInflater mSmartReplyStateInflater = + new SmartReplyStateInflater() { + @Override + public InflatedSmartReplyViewHolder inflateSmartReplyViewHolder( + Context sysuiContext, Context notifPackageContext, NotificationEntry entry, + InflatedSmartReplyState existingSmartReplyState, + InflatedSmartReplyState newSmartReplyState) { + return mInflatedSmartReplies; + } - private final SmartRepliesAndActionsInflater mSmartRepliesAndActionsInflater = - (sysuiContext, notifPackageContext, entry, existingRepliesAndAction) -> - mInflatedSmartReplies; + @Override + public InflatedSmartReplyState inflateSmartReplyState(NotificationEntry entry) { + return mInflatedSmartReplyState; + } + }; @Before public void setUp() throws Exception { @@ -114,7 +128,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { mConversationNotificationProcessor, mock(MediaFeatureFlag.class), mock(Executor.class), - mSmartRepliesAndActionsInflater); + mSmartReplyStateInflater); } @Test @@ -130,7 +144,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { FLAG_CONTENT_VIEW_ALL, builder, mContext, - mSmartRepliesAndActionsInflater); + mSmartReplyStateInflater); verify(builder).createHeadsUpContentView(true); } @@ -147,7 +161,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { FLAG_CONTENT_VIEW_ALL, builder, mContext, - mSmartRepliesAndActionsInflater); + mSmartReplyStateInflater); verify(builder).createContentView(true); } @@ -386,7 +400,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { @Override public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor, - OnViewAppliedListener listener, OnClickHandler handler) { + OnViewAppliedListener listener, InteractionHandler handler) { mHandler.post(() -> listener.onError(new RuntimeException("Failed to inflate async"))); return new CancellationSignal(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index ababebdd807c..97313bafb8a8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Notification; +import android.content.Context; import android.content.pm.LauncherApps; import android.os.Handler; import android.service.notification.NotificationListenerService; @@ -80,7 +81,9 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationRowCom import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.InflatedSmartReplies; +import com.android.systemui.statusbar.policy.InflatedSmartReplyState; +import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; +import com.android.systemui.statusbar.policy.SmartReplyStateInflater; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.time.FakeSystemClock; @@ -140,7 +143,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { @Mock private ActivatableNotificationViewController mActivatableNotificationViewController; @Mock private NotificationRowComponent.Builder mNotificationRowComponentBuilder; @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier; - @Mock private InflatedSmartReplies mInflatedSmartReplies; + @Mock private InflatedSmartReplyState mInflatedSmartReplyState; + @Mock private InflatedSmartReplyViewHolder mInflatedSmartReplies; private StatusBarNotification mSbn; private NotificationListenerService.RankingMap mRankingMap; @@ -206,8 +210,21 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mock(ConversationNotificationProcessor.class), mock(MediaFeatureFlag.class), mBgExecutor, - (sysuiContext, notifPackageContext, entry, existingRepliesAndAction) -> - mInflatedSmartReplies); + new SmartReplyStateInflater() { + @Override + public InflatedSmartReplyState inflateSmartReplyState(NotificationEntry entry) { + return mInflatedSmartReplyState; + } + + @Override + public InflatedSmartReplyViewHolder inflateSmartReplyViewHolder( + Context sysuiContext, Context notifPackageContext, + NotificationEntry entry, + InflatedSmartReplyState existingSmartReplyState, + InflatedSmartReplyState newSmartReplyState) { + return mInflatedSmartReplies; + } + }); mRowContentBindStage = new RowContentBindStage( binder, mock(NotifInflationErrorManager.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 64a7bee3c8dc..f3813fcaf6fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -66,7 +66,9 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.policy.InflatedSmartReplies; +import com.android.systemui.statusbar.policy.InflatedSmartReplyState; +import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; +import com.android.systemui.statusbar.policy.SmartReplyStateInflater; import com.android.systemui.tests.R; import com.android.systemui.wmshell.BubblesManager; import com.android.systemui.wmshell.BubblesTestActivity; @@ -139,8 +141,7 @@ public class NotificationTestHelper { mock(ConversationNotificationProcessor.class), mock(MediaFeatureFlag.class), mock(Executor.class), - (sysuiContext, notifPackageContext, entry, existingRepliesAndAction) -> - mock(InflatedSmartReplies.class)); + new MockSmartReplyInflater()); contentBinder.setInflateSynchronously(true); mBindStage = new RowContentBindStage(contentBinder, mock(NotifInflationErrorManager.class), @@ -464,4 +465,19 @@ public class NotificationTestHelper { .setDesiredHeight(314) .build(); } + + private static class MockSmartReplyInflater implements SmartReplyStateInflater { + @Override + public InflatedSmartReplyState inflateSmartReplyState(NotificationEntry entry) { + return mock(InflatedSmartReplyState.class); + } + + @Override + public InflatedSmartReplyViewHolder inflateSmartReplyViewHolder(Context sysuiContext, + Context notifPackageContext, NotificationEntry entry, + InflatedSmartReplyState existingSmartReplyState, + InflatedSmartReplyState newSmartReplyState) { + return mock(InflatedSmartReplyViewHolder.class); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 461f64eda6e5..84fb3689b0b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -21,6 +21,8 @@ import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; +import static com.google.common.truth.Truth.assertWithMessage; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; @@ -49,6 +51,7 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.EmptyShadeView; +import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelfController; @@ -101,6 +104,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private SysuiStatusBarStateController mStatusBarStateController; @Mock private NotificationSwipeHelper mNotificationSwipeHelper; @Mock private NotificationStackScrollLayoutController mStackScrollLayoutController; + @Mock private FeatureFlags mFeatureFlags; @Before @UiThreadTest @@ -139,8 +143,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mGroupMembershipManger, mGroupExpansionManager, mStatusBarStateController, - mAmbientState - ); + mAmbientState, + mFeatureFlags); mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider, mNotificationSwipeHelper); mStackScroller = spy(mStackScrollerInternal); @@ -205,8 +209,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test @UiThreadTest public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() { - final float expectedHeight[] = {0f}; - final float expectedAppear[] = {0f}; + final float[] expectedHeight = {0f}; + final float[] expectedAppear = {0f}; mStackScroller.addOnExpandedHeightChangedListener((height, appear) -> { Assert.assertEquals(expectedHeight[0], height, 0); @@ -222,6 +226,29 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test + @UiThreadTest + public void testSetExpandedHeight_withSplitShade_doesntInterpolateStackHeight() { + when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); + final int[] expectedStackHeight = {0}; + + mStackScroller.addOnExpandedHeightChangedListener((expandedHeight, appear) -> { + assertWithMessage("Given shade enabled: %s", + mFeatureFlags.isTwoColumnNotificationShadeEnabled()) + .that(mStackScroller.getHeight()) + .isEqualTo(expectedStackHeight[0]); + }); + + when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false); + expectedStackHeight[0] = 0; + mStackScroller.setExpandedHeight(100f); + + when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); + expectedStackHeight[0] = 100; + mStackScroller.setExpandedHeight(100f); + } + + + @Test public void manageNotifications_visible() { FooterView view = mock(FooterView.class); mStackScroller.setFooterView(view); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index 8dea84c4d0b6..6e0cbd9ecfa3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -197,9 +198,9 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mHeadsUpAppearanceController.destroy(); verify(mHeadsUpManager).removeListener(any()); verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any()); - verify(mPanelView).removeVerticalTranslationListener(any()); + verify(mPanelView).setVerticalTranslationListener(isNull()); verify(mPanelView).removeTrackingHeadsUpListener(any()); - verify(mPanelView).setHeadsUpAppearanceController(any()); + verify(mPanelView).setHeadsUpAppearanceController(isNull()); verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any()); verify(mStackScrollerController).removeOnLayoutChangeListener(any()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index d69d2dbdd841..dd31f522b94d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -18,19 +18,22 @@ package com.android.systemui.statusbar.phone; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import static com.android.systemui.statusbar.StatusBarState.SHADE; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.IdRes; import android.app.ActivityManager; import android.app.StatusBarManager; import android.content.res.Configuration; @@ -45,6 +48,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; @@ -53,6 +57,7 @@ import androidx.constraintlayout.widget.ConstraintSet; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardClockSwitch; @@ -61,7 +66,6 @@ import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardStatusViewController; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; -import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.systemui.R; @@ -101,7 +105,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; @@ -117,8 +120,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private StatusBar mStatusBar; @Mock - private SysuiStatusBarStateController mStatusBarStateController; - @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; @Mock private KeyguardBottomAreaView mKeyguardBottomArea; @@ -207,16 +208,10 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private KeyguardStatusViewComponent mKeyguardStatusViewComponent; @Mock - private KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; - @Mock - private KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent; - @Mock private KeyguardClockSwitchController mKeyguardClockSwitchController; @Mock private KeyguardStatusViewController mKeyguardStatusViewController; @Mock - private KeyguardStatusBarViewController mKeyguardStatusBarViewController; - @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; @Mock private AuthController mAuthController; @@ -234,7 +229,10 @@ public class NotificationPanelViewTest extends SysuiTestCase { private AmbientState mAmbientState; @Mock private UserManager mUserManager; + @Mock + private UiEventLogger mUiEventLogger; + private SysuiStatusBarStateController mStatusBarStateController; private NotificationPanelViewController mNotificationPanelViewController; private View.AccessibilityDelegate mAccessibiltyDelegate; private NotificationsQuickSettingsContainer mNotificationContainerParent; @@ -242,6 +240,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Before public void setup() { MockitoAnnotations.initMocks(this); + mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger); + when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false); when(mHeadsUpCallback.getContext()).thenReturn(mContext); when(mView.getResources()).thenReturn(mResources); @@ -265,12 +265,14 @@ public class NotificationPanelViewTest extends SysuiTestCase { when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); 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)); - when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar); mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null); + mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame)); + mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller)); when(mView.findViewById(R.id.notification_container_parent)) .thenReturn(mNotificationContainerParent); FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder( @@ -300,14 +302,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { .thenReturn(mKeyguardClockSwitchController); when(mKeyguardStatusViewComponent.getKeyguardStatusViewController()) .thenReturn(mKeyguardStatusViewController); - when(mQsFrame.getLayoutParams()).thenReturn( - new ViewGroup.LayoutParams(600, 400)); - when(mNotificationStackScrollLayoutController.getLayoutParams()).thenReturn( - new ViewGroup.LayoutParams(600, 400)); - when(mKeyguardStatusBarViewComponentFactory.build(any())) - .thenReturn(mKeyguardStatusBarViewComponent); - when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController()) - .thenReturn(mKeyguardStatusBarViewController); mNotificationPanelViewController = new NotificationPanelViewController(mView, mResources, @@ -326,7 +320,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { mKeyguardStatusViewComponentFactory, mKeyguardQsUserSwitchComponentFactory, mKeyguardUserSwitcherComponentFactory, - mKeyguardStatusBarViewComponentFactory, mQSDetailDisplayer, mGroupManager, mNotificationAreaController, @@ -348,17 +341,19 @@ public class NotificationPanelViewTest extends SysuiTestCase { ArgumentCaptor.forClass(View.AccessibilityDelegate.class); verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture()); mAccessibiltyDelegate = accessibilityDelegateArgumentCaptor.getValue(); + mNotificationPanelViewController.mStatusBarStateController + .addCallback(mNotificationPanelViewController.mStatusBarStateListener); + mNotificationPanelViewController + .setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class)); } @Test public void testSetDozing_notifiesNsslAndStateController() { - mNotificationPanelViewController.setDozing(true /* dozing */, true /* animate */, + mNotificationPanelViewController.setDozing(true /* dozing */, false /* animate */, null /* touch */); - InOrder inOrder = inOrder( - mNotificationStackScrollLayoutController, mStatusBarStateController); - inOrder.verify(mNotificationStackScrollLayoutController) - .setDozing(eq(true), eq(true), eq(null)); - inOrder.verify(mStatusBarStateController).setDozeAmount(eq(1f), eq(true)); + verify(mNotificationStackScrollLayoutController) + .setDozing(eq(true), eq(false), eq(null)); + assertThat(mStatusBarStateController.getDozeAmount()).isEqualTo(1f); } @Test @@ -454,9 +449,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Test public void testAllChildrenOfNotificationContainer_haveIds() { - when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); - when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); - + enableSplitShade(); + mNotificationContainerParent.removeAllViews(); mNotificationContainerParent.addView(newViewWithId(1)); mNotificationContainerParent.addView(newViewWithId(View.NO_ID)); @@ -469,36 +463,92 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Test public void testSinglePaneShadeLayout_isAlignedToParent() { when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false); - mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame)); - mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller)); mNotificationPanelViewController.updateResources(); - ConstraintSet constraintSet = new ConstraintSet(); - constraintSet.clone(mNotificationContainerParent); - ConstraintSet.Layout qsFrameLayout = constraintSet.getConstraint(R.id.qs_frame).layout; - ConstraintSet.Layout stackScrollerLayout = constraintSet.getConstraint( - R.id.notification_stack_scroller).layout; - assertThat(qsFrameLayout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID); - assertThat(stackScrollerLayout.startToStart).isEqualTo(ConstraintSet.PARENT_ID); + assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd) + .isEqualTo(ConstraintSet.PARENT_ID); + assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startToStart) + .isEqualTo(ConstraintSet.PARENT_ID); } @Test public void testSplitShadeLayout_isAlignedToGuideline() { - when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); - when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); - mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame)); - mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller)); + enableSplitShade(); mNotificationPanelViewController.updateResources(); - ConstraintSet constraintSet = new ConstraintSet(); - constraintSet.clone(mNotificationContainerParent); - ConstraintSet.Layout qsFrameLayout = constraintSet.getConstraint(R.id.qs_frame).layout; - ConstraintSet.Layout stackScrollerLayout = constraintSet.getConstraint( - R.id.notification_stack_scroller).layout; - assertThat(qsFrameLayout.endToEnd).isEqualTo(R.id.qs_edge_guideline); - assertThat(stackScrollerLayout.startToStart).isEqualTo(R.id.qs_edge_guideline); + assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd) + .isEqualTo(R.id.qs_edge_guideline); + assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startToStart) + .isEqualTo(R.id.qs_edge_guideline); + } + + @Test + public void testSinglePaneShadeLayout_childrenHaveConstantWidth() { + when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false); + + mNotificationPanelViewController.updateResources(); + + assertThat(getConstraintSetLayout(R.id.qs_frame).mWidth) + .isEqualTo(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)); + assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).mWidth) + .isEqualTo(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)); + } + + @Test + public void testSplitShadeLayout_childrenHaveZeroWidth() { + enableSplitShade(); + + mNotificationPanelViewController.updateResources(); + + assertThat(getConstraintSetLayout(R.id.qs_frame).mWidth).isEqualTo(0); + assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).mWidth).isEqualTo(0); + } + + @Test + public void testOnDragDownEvent_horizontalTranslationIsZeroForSplitShade() { + when(mNotificationStackScrollLayoutController.getWidth()).thenReturn(350f); + when(mView.getWidth()).thenReturn(800); + enableSplitShade(); + + onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, + 200f /* x position */, 0f, 0)); + + verify(mQsFrame).setTranslationX(0); + } + + @Test + public void testCanCollapsePanelOnTouch_trueForKeyGuard() { + mStatusBarStateController.setState(KEYGUARD); + + assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue(); + } + + @Test + public void testCanCollapsePanelOnTouch_trueWhenScrolledToBottom() { + mStatusBarStateController.setState(SHADE); + when(mNotificationStackScrollLayoutController.isScrolledToBottom()).thenReturn(true); + + assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue(); + } + + @Test + public void testCanCollapsePanelOnTouch_trueWhenInSettings() { + mStatusBarStateController.setState(SHADE); + mNotificationPanelViewController.setQsExpanded(true); + + assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isTrue(); + } + + @Test + public void testCanCollapsePanelOnTouch_falseInDualPaneShade() { + mStatusBarStateController.setState(SHADE); + when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); + when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); + mNotificationPanelViewController.setQsExpanded(true); + + assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isFalse(); } private View newViewWithId(int id) { @@ -511,6 +561,17 @@ public class NotificationPanelViewTest extends SysuiTestCase { return view; } + private ConstraintSet.Layout getConstraintSetLayout(@IdRes int id) { + ConstraintSet constraintSet = new ConstraintSet(); + constraintSet.clone(mNotificationContainerParent); + return constraintSet.getConstraint(id).layout; + } + + private void enableSplitShade() { + when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); + when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); + } + private void onTouchEvent(MotionEvent ev) { mTouchHandler.onTouch(mView, ev); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java index 67c1a086bb33..24182434f1ba 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java @@ -30,7 +30,9 @@ 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.tests.R; import org.junit.Before; @@ -86,28 +88,24 @@ public class CallbackHandlerTest extends SysuiTestCase { boolean out = true; String description = "Test"; String secondaryLabel = "Secondary label"; - mHandler.setWifiIndicators(enabled, status, qs, in, out, description, true, secondaryLabel); + WifiIndicators indicators = new WifiIndicators( + enabled, status, qs, in, out, description, true, secondaryLabel); + mHandler.setWifiIndicators(indicators); waitForCallbacks(); - ArgumentCaptor<Boolean> enableArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class); - ArgumentCaptor<IconState> qsArg = ArgumentCaptor.forClass(IconState.class); - ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class); - ArgumentCaptor<Boolean> isTransient = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<String> secondary = ArgumentCaptor.forClass(String.class); - Mockito.verify(mSignalCallback).setWifiIndicators(enableArg.capture(), - statusArg.capture(), qsArg.capture(), inArg.capture(), outArg.capture(), - descArg.capture(), isTransient.capture(), secondary.capture()); - assertEquals(enabled, (boolean) enableArg.getValue()); - assertEquals(status, statusArg.getValue()); - assertEquals(qs, qsArg.getValue()); - assertEquals(in, (boolean) inArg.getValue()); - assertEquals(out, (boolean) outArg.getValue()); - assertEquals(description, descArg.getValue()); - assertTrue(isTransient.getValue()); - assertEquals(secondaryLabel, secondary.getValue()); + ArgumentCaptor<WifiIndicators> indicatorArg = + ArgumentCaptor.forClass(WifiIndicators.class); + Mockito.verify(mSignalCallback).setWifiIndicators(indicatorArg.capture()); + WifiIndicators expected = indicatorArg.getValue(); + + assertEquals(enabled, expected.enabled); + assertEquals(status, expected.statusIcon); + assertEquals(qs, expected.qsIcon); + assertEquals(in, expected.activityIn); + assertEquals(out, expected.activityOut); + assertEquals(description, expected.description); + assertTrue(expected.isTransient); + assertEquals(secondaryLabel, expected.statusLabel); } @Test @@ -124,37 +122,30 @@ public class CallbackHandlerTest extends SysuiTestCase { boolean wide = true; int subId = 5; boolean roaming = true; - mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription, + MobileDataIndicators indicators = new MobileDataIndicators( + status, qs, type, qsType, in, out, typeDescription, typeDescriptionHtml, description, wide, subId, roaming, true); + mHandler.setMobileDataIndicators(indicators); waitForCallbacks(); - ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class); - ArgumentCaptor<IconState> qsArg = ArgumentCaptor.forClass(IconState.class); - ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor<Integer> qsTypeIconArg = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<CharSequence> typeContentArg = ArgumentCaptor.forClass(CharSequence.class); - ArgumentCaptor<CharSequence> typeContentHtmlArg = - ArgumentCaptor.forClass(CharSequence.class); - ArgumentCaptor<CharSequence> descArg = ArgumentCaptor.forClass(CharSequence.class); - ArgumentCaptor<Boolean> wideArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<Integer> subIdArg = ArgumentCaptor.forClass(Integer.class); - Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(), - qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(), - outArg.capture(), typeContentArg.capture(), typeContentHtmlArg.capture(), - descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming), eq(true)); - assertEquals(status, statusArg.getValue()); - assertEquals(qs, qsArg.getValue()); - assertEquals(type, (int) typeIconArg.getValue()); - assertEquals(qsType, (int) qsTypeIconArg.getValue()); - assertEquals(in, (boolean) inArg.getValue()); - assertEquals(out, (boolean) outArg.getValue()); - assertEquals(typeDescription, typeContentArg.getValue()); - assertEquals(typeDescriptionHtml, typeContentHtmlArg.getValue()); - assertEquals(description, descArg.getValue()); - assertEquals(wide, (boolean) wideArg.getValue()); - assertEquals(subId, (int) subIdArg.getValue()); + ArgumentCaptor<MobileDataIndicators> indicatorArg = + ArgumentCaptor.forClass(MobileDataIndicators.class); + Mockito.verify(mSignalCallback).setMobileDataIndicators(indicatorArg.capture()); + MobileDataIndicators expected = indicatorArg.getValue(); + + assertEquals(status, expected.statusIcon); + assertEquals(qs, expected.qsIcon); + assertEquals(type, expected.statusType); + assertEquals(qsType, expected.qsType); + assertEquals(in, expected.activityIn); + assertEquals(out, expected.activityOut); + assertEquals(typeDescription, expected.typeContentDescription); + assertEquals(typeDescriptionHtml, expected.typeContentDescriptionHtml); + assertEquals(description, expected.description); + assertEquals(wide, expected.isWide); + assertEquals(subId, expected.subId); + assertTrue(expected.roaming); + assertTrue(expected.showTriangle); } @SuppressWarnings("unchecked") diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java index 32675c92d3f9..5e2fa98178f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java @@ -45,7 +45,7 @@ import com.android.systemui.shared.system.PackageManagerWrapper; import com.android.systemui.statusbar.NotificationEntryHelper; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; -import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions; +import com.android.systemui.statusbar.policy.InflatedSmartReplyState.SuppressedActions; import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions; import com.android.systemui.statusbar.policy.SmartReplyView.SmartReplies; @@ -74,13 +74,12 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { @Mock private ActivityManagerWrapper mActivityManagerWrapper; @Mock private PackageManagerWrapper mPackageManagerWrapper; @Mock private DevicePolicyManagerWrapper mDevicePolicyManagerWrapper; - @Mock private SmartRepliesAndActions mSmartRepliesAndActions; @Mock private SmartReplyInflater mSmartReplyInflater; @Mock private SmartActionInflater mSmartActionInflater; private Icon mActionIcon; private NotificationEntry mEntry; - private SmartRepliesAndActionsInflaterImpl mSmartRepliesInflater; + private SmartReplyStateInflaterImpl mSmartReplyStateInflater; @Before @UiThreadTest @@ -101,7 +100,7 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { when(mActivityManagerWrapper.isLockTaskKioskModeActive()).thenReturn(false); - mSmartRepliesInflater = new SmartRepliesAndActionsInflaterImpl( + mSmartReplyStateInflater = new SmartReplyStateInflaterImpl( mSmartReplyConstants, mActivityManagerWrapper, mPackageManagerWrapper, @@ -118,11 +117,13 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { setupAppGeneratedSuggestions(smartReplies, smartActions); when(mSmartReplyConstants.isEnabled()).thenReturn(false); - SmartRepliesAndActions repliesAndActions = - mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); - assertThat(repliesAndActions.smartReplies).isNull(); - assertThat(repliesAndActions.smartActions).isNull(); + assertThat(smartReplyState.getSmartReplies()).isNull(); + assertThat(smartReplyState.getSmartActions()).isNull(); + assertThat(smartReplyState.getSuppressedActions()).isNull(); + assertThat(smartReplyState.getHasPhishingAction()).isFalse(); } @Test @@ -134,11 +135,13 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { when(mSmartReplyConstants.isEnabled()).thenReturn(false); - SmartRepliesAndActions repliesAndActions = - mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); - assertThat(repliesAndActions.smartReplies).isNull(); - assertThat(repliesAndActions.smartActions).isNull(); + assertThat(smartReplyState.getSmartReplies()).isNull(); + assertThat(smartReplyState.getSmartActions()).isNull(); + assertThat(smartReplyState.getSuppressedActions()).isNull(); + assertThat(smartReplyState.getHasPhishingAction()).isFalse(); } @Test @@ -146,12 +149,15 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"}; setupAppGeneratedReplies(smartReplies); - SmartRepliesAndActions repliesAndActions = - mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); - assertThat(repliesAndActions.smartReplies.choices).isEqualTo(Arrays.asList(smartReplies)); - assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse(); - assertThat(repliesAndActions.smartActions).isNull(); + assertThat(smartReplyState.getSmartReplies().choices) + .containsExactlyElementsIn(smartReplies).inOrder(); + assertThat(smartReplyState.getSmartReplies().fromAssistant).isFalse(); + assertThat(smartReplyState.getSmartActions()).isNull(); + assertThat(smartReplyState.getSuppressedActions()).isNull(); + assertThat(smartReplyState.getHasPhishingAction()).isFalse(); } @Test @@ -161,13 +167,17 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { createActions("Test Action 1", "Test Action 2"); setupAppGeneratedSuggestions(smartReplies, smartActions); - SmartRepliesAndActions repliesAndActions = - mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry); - - assertThat(repliesAndActions.smartReplies.choices).isEqualTo(Arrays.asList(smartReplies)); - assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse(); - assertThat(repliesAndActions.smartActions.actions).isEqualTo(smartActions); - assertThat(repliesAndActions.smartActions.fromAssistant).isFalse(); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); + + assertThat(smartReplyState.getSmartReplies().choices) + .containsExactlyElementsIn(smartReplies).inOrder(); + assertThat(smartReplyState.getSmartReplies().fromAssistant).isFalse(); + assertThat(smartReplyState.getSmartActions().actions) + .containsExactlyElementsIn(smartActions).inOrder(); + assertThat(smartReplyState.getSmartActions().fromAssistant).isFalse(); + assertThat(smartReplyState.getSuppressedActions()).isNull(); + assertThat(smartReplyState.getHasPhishingAction()).isFalse(); } @Test @@ -180,12 +190,15 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2")) .build(); - SmartRepliesAndActions repliesAndActions = - mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); - assertThat(repliesAndActions.smartReplies.choices).isEqualTo(mEntry.getSmartReplies()); - assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue(); - assertThat(repliesAndActions.smartActions).isNull(); + assertThat(smartReplyState.getSmartReplies().choices) + .containsExactlyElementsIn(mEntry.getSmartReplies()).inOrder(); + assertThat(smartReplyState.getSmartReplies().fromAssistant).isTrue(); + assertThat(smartReplyState.getSmartActions()).isNull(); + assertThat(smartReplyState.getSuppressedActions()).isNull(); + assertThat(smartReplyState.getHasPhishingAction()).isFalse(); } @Test @@ -197,11 +210,13 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { NotificationEntryHelper.modifyRanking(mEntry) .setSmartReplies(createReplies("Sys Smart Reply 1", "Sys Smart Reply 2")) .build(); - SmartRepliesAndActions repliesAndActions = - mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); - assertThat(repliesAndActions.smartReplies).isNull(); - assertThat(repliesAndActions.smartActions).isNull(); + assertThat(smartReplyState.getSmartReplies()).isNull(); + assertThat(smartReplyState.getSmartActions()).isNull(); + assertThat(smartReplyState.getSuppressedActions()).isNull(); + assertThat(smartReplyState.getHasPhishingAction()).isFalse(); } @Test @@ -214,13 +229,50 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2")) .build(); - SmartRepliesAndActions repliesAndActions = - mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); + + assertThat(smartReplyState.getSmartReplies()).isNull(); + assertThat(smartReplyState.getSmartActions().actions) + .containsExactlyElementsIn(mEntry.getSmartActions()).inOrder(); + assertThat(smartReplyState.getSmartActions().fromAssistant).isTrue(); + assertThat(smartReplyState.getSuppressedActions()).isNull(); + assertThat(smartReplyState.getHasPhishingAction()).isFalse(); + } + + @Test + public void chooseSmartRepliesAndActions_sysGeneratedPhishingSmartAction() { + // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart + // actions. + setupAppGeneratedReplies(null /* smartReplies */); + + mNotification.actions = new Notification.Action[]{ + createAction("Details"), + createActionBuilder("Reply").addRemoteInput( + new RemoteInput.Builder("key").build()).build() + }; + + modifyRanking(mEntry) + .setSmartActions( + createAction("Sys Smart Action 1"), + createActionBuilder("Sys Smart Action 2") + .setContextual(true) + .setSemanticAction(Notification.Action + .SEMANTIC_ACTION_CONVERSATION_IS_PHISHING) + .build()) + .build(); - assertThat(repliesAndActions.smartReplies).isNull(); - assertThat(repliesAndActions.smartActions.actions) - .isEqualTo(mEntry.getSmartActions()); - assertThat(repliesAndActions.smartActions.fromAssistant).isTrue(); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); + + assertThat(smartReplyState.getSmartReplies()).isNull(); + assertThat(smartReplyState.getSmartActions().actions) + .containsExactlyElementsIn(mEntry.getSmartActions()).inOrder(); + assertThat(smartReplyState.getSmartActions().fromAssistant).isTrue(); + assertThat(smartReplyState.getSuppressedActions()).isNotNull(); + assertThat(smartReplyState.getSuppressedActions().getSuppressedActionIndices()) + .containsExactly(1); + assertThat(smartReplyState.getHasPhishingAction()).isTrue(); } @Test @@ -237,14 +289,17 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2")) .build(); - SmartRepliesAndActions repliesAndActions = - mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry); - - assertThat(repliesAndActions.smartReplies.choices) - .isEqualTo(Arrays.asList(appGenSmartReplies)); - assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse(); - assertThat(repliesAndActions.smartActions.actions).isEqualTo(appGenSmartActions); - assertThat(repliesAndActions.smartActions.fromAssistant).isFalse(); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); + + assertThat(smartReplyState.getSmartReplies().choices) + .containsExactlyElementsIn(Arrays.asList(appGenSmartReplies)).inOrder(); + assertThat(smartReplyState.getSmartReplies().fromAssistant).isFalse(); + assertThat(smartReplyState.getSmartActions().actions) + .containsExactlyElementsIn(appGenSmartActions).inOrder(); + assertThat(smartReplyState.getSmartActions().fromAssistant).isFalse(); + assertThat(smartReplyState.getSuppressedActions()).isNull(); + assertThat(smartReplyState.getHasPhishingAction()).isFalse(); } @Test @@ -259,11 +314,13 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2")) .build(); - SmartRepliesAndActions repliesAndActions = - mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); - assertThat(repliesAndActions.smartActions).isNull(); - assertThat(repliesAndActions.smartReplies).isNull(); + assertThat(smartReplyState.getSmartActions()).isNull(); + assertThat(smartReplyState.getSmartReplies()).isNull(); + assertThat(smartReplyState.getSuppressedActions()).isNull(); + assertThat(smartReplyState.getHasPhishingAction()).isFalse(); } @Test @@ -281,13 +338,15 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2")) .build(); - SmartRepliesAndActions repliesAndActions = - mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); - assertThat(repliesAndActions.smartReplies.choices).isEqualTo( - mEntry.getSmartReplies()); + assertThat(smartReplyState.getSmartReplies().choices) + .containsExactlyElementsIn(mEntry.getSmartReplies()).inOrder(); // Since no apps are whitelisted no actions should be shown. - assertThat(repliesAndActions.smartActions.actions).isEmpty(); + assertThat(smartReplyState.getSmartActions().actions).isEmpty(); + assertThat(smartReplyState.getSuppressedActions()).isNull(); + assertThat(smartReplyState.getHasPhishingAction()).isFalse(); } @Test @@ -317,13 +376,14 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { .setSmartActions(actions) .build(); - SmartRepliesAndActions repliesAndActions = - mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); // Only the action for the whitelisted package should be allowed. - assertThat(repliesAndActions.smartActions.actions.size()).isEqualTo(1); - assertThat(repliesAndActions.smartActions.actions.get(0)).isEqualTo( - mEntry.getSmartActions().get(0)); + assertThat(smartReplyState.getSmartActions().actions) + .containsExactly(mEntry.getSmartActions().get(0)); + assertThat(smartReplyState.getSuppressedActions()).isNull(); + assertThat(smartReplyState.getHasPhishingAction()).isFalse(); } @Test @@ -340,14 +400,16 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { .setSmartActions(createActions("Sys Smart Action 1", "Sys Smart Action 2")) .build(); - SmartRepliesAndActions repliesAndActions = - mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry); + InflatedSmartReplyState smartReplyState = + mSmartReplyStateInflater.chooseSmartRepliesAndActions(mEntry); // We don't restrict replies or actions in screen pinning mode. - assertThat(repliesAndActions.smartReplies.choices).isEqualTo( - mEntry.getSmartReplies()); - assertThat(repliesAndActions.smartActions.actions).isEqualTo( - mEntry.getSmartActions()); + assertThat(smartReplyState.getSmartReplies().choices) + .containsExactlyElementsIn(mEntry.getSmartReplies()).inOrder(); + assertThat(smartReplyState.getSmartActions().actions) + .containsExactlyElementsIn(mEntry.getSmartActions()).inOrder(); + assertThat(smartReplyState.getSuppressedActions()).isNull(); + assertThat(smartReplyState.getHasPhishingAction()).isFalse(); } @Test @@ -360,17 +422,24 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { List<Notification.Action> rightActions = Arrays.asList( createAction("firstAction"), createAction("secondAction")); + List<Integer> leftSuppressed = Arrays.asList(1, 2); + List<Integer> rightSuppressed = Arrays.asList(1, 2); + boolean leftPhishing = true; + boolean rightPhishing = true; - SmartRepliesAndActions leftRepliesAndActions = new SmartRepliesAndActions( + InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState( new SmartReplies(leftReplies, null, null, false /* fromAssistant */), - new SmartActions(leftActions, false /* fromAssistant */)); - SmartRepliesAndActions rightRepliesAndActions = new SmartRepliesAndActions( + new SmartActions(leftActions, false /* fromAssistant */), + new SuppressedActions(leftSuppressed), + leftPhishing); + InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState( new SmartReplies(rightReplies, null, null, false /* fromAssistant */), - new SmartActions(rightActions, false /* fromAssistant */)); + new SmartActions(rightActions, false /* fromAssistant */), + new SuppressedActions(rightSuppressed), + rightPhishing); - assertThat( - SmartRepliesAndActionsInflaterKt - .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions)) + assertThat(SmartReplyStateInflaterKt + .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions)) .isTrue(); } @@ -384,16 +453,25 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { List<Notification.Action> rightActions = Arrays.asList( createAction("firstAction"), createAction("secondAction")); + List<Integer> leftSuppressed = Arrays.asList(1, 2); + List<Integer> rightSuppressed = Arrays.asList(1, 2); + boolean leftPhishing = true; + boolean rightPhishing = true; - SmartRepliesAndActions leftRepliesAndActions = new SmartRepliesAndActions( + InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState( new SmartReplies(leftReplies, null, null, false /* fromAssistant */), - new SmartActions(leftActions, false /* fromAssistant */)); - SmartRepliesAndActions rightRepliesAndActions = new SmartRepliesAndActions( + new SmartActions(leftActions, false /* fromAssistant */), + new SuppressedActions(leftSuppressed), + leftPhishing); + InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState( new SmartReplies(rightReplies, null, null, false /* fromAssistant */), - new SmartActions(rightActions, false /* fromAssistant */)); + new SmartActions(rightActions, false /* fromAssistant */), + new SuppressedActions(rightSuppressed), + rightPhishing); - assertThat(SmartRepliesAndActionsInflaterKt.areSuggestionsSimilar( - leftRepliesAndActions, rightRepliesAndActions)).isFalse(); + assertThat(SmartReplyStateInflaterKt + .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions)) + .isFalse(); } @Test @@ -406,16 +484,87 @@ public class InflatedSmartRepliesTest extends SysuiTestCase { List<Notification.Action> rightActions = Arrays.asList( createAction("firstAction"), createAction("not secondAction")); + List<Integer> leftSuppressed = Arrays.asList(1, 2); + List<Integer> rightSuppressed = Arrays.asList(1, 2); + boolean leftPhishing = true; + boolean rightPhishing = true; + + InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState( + new SmartReplies(leftReplies, null, null, false /* fromAssistant */), + new SmartActions(leftActions, false /* fromAssistant */), + new SuppressedActions(leftSuppressed), + leftPhishing); + InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState( + new SmartReplies(rightReplies, null, null, false /* fromAssistant */), + new SmartActions(rightActions, false /* fromAssistant */), + new SuppressedActions(rightSuppressed), + rightPhishing); + + assertThat(SmartReplyStateInflaterKt + .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions)) + .isFalse(); + } + + @Test + public void areSuggestionsSimilar_falseForDifferentSuppressedActions() { + List<CharSequence> leftReplies = createReplies("first reply", "second reply"); + List<CharSequence> rightReplies = createReplies("first reply", "second reply"); + List<Notification.Action> leftActions = Arrays.asList( + createAction("firstAction"), + createAction("secondAction")); + List<Notification.Action> rightActions = Arrays.asList( + createAction("firstAction"), + createAction("secondAction")); + List<Integer> leftSuppressed = Arrays.asList(1, 2); + List<Integer> rightSuppressed = Arrays.asList(1, 3); + boolean leftPhishing = true; + boolean rightPhishing = true; + + InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState( + new SmartReplies(leftReplies, null, null, false /* fromAssistant */), + new SmartActions(leftActions, false /* fromAssistant */), + new SuppressedActions(leftSuppressed), + leftPhishing); + InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState( + new SmartReplies(rightReplies, null, null, false /* fromAssistant */), + new SmartActions(rightActions, false /* fromAssistant */), + new SuppressedActions(rightSuppressed), + rightPhishing); + + assertThat(SmartReplyStateInflaterKt + .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions)) + .isFalse(); + } + + @Test + public void areSuggestionsSimilar_falseForDifferentPhishing() { + List<CharSequence> leftReplies = createReplies("first reply", "second reply"); + List<CharSequence> rightReplies = createReplies("first reply", "second reply"); + List<Notification.Action> leftActions = Arrays.asList( + createAction("firstAction"), + createAction("secondAction")); + List<Notification.Action> rightActions = Arrays.asList( + createAction("firstAction"), + createAction("secondAction")); + List<Integer> leftSuppressed = Arrays.asList(1, 2); + List<Integer> rightSuppressed = Arrays.asList(1, 2); + boolean leftPhishing = true; + boolean rightPhishing = false; - SmartRepliesAndActions leftRepliesAndActions = new SmartRepliesAndActions( + InflatedSmartReplyState leftRepliesAndActions = new InflatedSmartReplyState( new SmartReplies(leftReplies, null, null, false /* fromAssistant */), - new SmartActions(leftActions, false /* fromAssistant */)); - SmartRepliesAndActions rightRepliesAndActions = new SmartRepliesAndActions( + new SmartActions(leftActions, false /* fromAssistant */), + new SuppressedActions(leftSuppressed), + leftPhishing); + InflatedSmartReplyState rightRepliesAndActions = new InflatedSmartReplyState( new SmartReplies(rightReplies, null, null, false /* fromAssistant */), - new SmartActions(rightActions, false /* fromAssistant */)); + new SmartActions(rightActions, false /* fromAssistant */), + new SuppressedActions(rightSuppressed), + rightPhishing); - assertThat(SmartRepliesAndActionsInflaterKt.areSuggestionsSimilar( - leftRepliesAndActions, rightRepliesAndActions)).isFalse(); + assertThat(SmartReplyStateInflaterKt + .areSuggestionsSimilar(leftRepliesAndActions, rightRepliesAndActions)) + .isFalse(); } private void setupAppGeneratedReplies(CharSequence[] smartReplies) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index 89cc2b574398..e52b92648670 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -21,9 +21,9 @@ import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; @@ -76,6 +76,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.demomode.DemoModeController; 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 org.junit.After; @@ -173,8 +174,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase { mMockSubDefaults = mock(SubscriptionDefaults.class); mNetCapabilities = new NetworkCapabilities(); when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true); - when(mMockCm.getDefaultNetworkCapabilitiesForUser(0)).thenReturn( - new NetworkCapabilities[] { mNetCapabilities }); when(mMockTm.createForSubscriptionId(anyInt())).thenReturn(mMockTm); doAnswer(invocation -> { int rssi = invocation.getArgument(0); @@ -257,8 +256,11 @@ public class NetworkControllerBaseTest extends SysuiTestCase { ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); verify(mMockCm, atLeastOnce()) .registerDefaultNetworkCallback(callbackArg.capture(), isA(Handler.class)); - mDefaultCallbackInWifiTracker = callbackArg.getAllValues().get(0); - mDefaultCallbackInNetworkController = callbackArg.getAllValues().get(1); + int captureSize = callbackArg.getAllValues().size(); + assertTrue(captureSize > 1); + assertEquals(captureSize % 2, 0); + mDefaultCallbackInWifiTracker = callbackArg.getAllValues().get(captureSize - 2); + mDefaultCallbackInNetworkController = callbackArg.getAllValues().get(captureSize - 1); assertNotNull(mDefaultCallbackInWifiTracker); assertNotNull(mDefaultCallbackInNetworkController); verify(mMockCm, atLeastOnce()).registerNetworkCallback( @@ -307,14 +309,18 @@ public class NetworkControllerBaseTest extends SysuiTestCase { setLevel(DEFAULT_LEVEL); updateDataConnectionState(TelephonyManager.DATA_CONNECTED, TelephonyManager.NETWORK_TYPE_UMTS); + setConnectivityViaCallbackInNetworkController( + NetworkCapabilities.TRANSPORT_CELLULAR, true, true, null); setConnectivityViaBroadcast( - NetworkCapabilities.TRANSPORT_CELLULAR, true, true); + NetworkCapabilities.TRANSPORT_CELLULAR, true, true); } public void setConnectivityViaBroadcastForVcn( int networkType, boolean validated, boolean isConnected, VcnTransportInfo info) { mNetCapabilities.setTransportInfo(info); setConnectivityCommon(networkType, validated, isConnected); + mDefaultCallbackInNetworkController.onCapabilitiesChanged( + mock(Network.class), new NetworkCapabilities(mNetCapabilities)); Intent i = new Intent(ConnectivityManager.INET_CONDITION_ACTION); mNetworkController.onReceive(mContext, i); } @@ -322,6 +328,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase { public void setConnectivityViaBroadcast( int networkType, boolean validated, boolean isConnected) { setConnectivityCommon(networkType, validated, isConnected); + mDefaultCallbackInNetworkController.onCapabilitiesChanged( + mock(Network.class), new NetworkCapabilities(mNetCapabilities)); Intent i = new Intent(ConnectivityManager.INET_CONDITION_ACTION); mNetworkController.onReceive(mContext, i); } @@ -487,28 +495,21 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected void verifyLastQsMobileDataIndicators(boolean visible, int icon, int typeIcon, boolean dataIn, boolean dataOut) { - ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); - ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor<Boolean> dataInArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<MobileDataIndicators> indicatorsArg = + ArgumentCaptor.forClass(MobileDataIndicators.class); verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators( - any(), - iconArg.capture(), - anyInt(), - typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(), - any(CharSequence.class), any(CharSequence.class), any(CharSequence.class), - anyBoolean(), anyInt(), anyBoolean(), anyBoolean()); - IconState iconState = iconArg.getValue(); + indicatorsArg.capture()); + MobileDataIndicators expected = indicatorsArg.getValue(); int state = SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(), false); - assertEquals("Visibility in, quick settings", visible, iconState.visible); - assertEquals("Signal icon in, quick settings", state, iconState.icon); - assertEquals("Data icon in, quick settings", typeIcon, (int) typeIconArg.getValue()); + assertEquals("Visibility in, quick settings", visible, expected.qsIcon.visible); + assertEquals("Signal icon in, quick settings", state, expected.qsIcon.icon); + assertEquals("Data icon in, quick settings", typeIcon, expected.qsType); assertEquals("Data direction in, in quick settings", dataIn, - (boolean) dataInArg.getValue()); + expected.activityIn); assertEquals("Data direction out, in quick settings", dataOut, - (boolean) dataOutArg.getValue()); + expected.activityOut); } protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon) { @@ -522,44 +523,35 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon, boolean roaming, boolean inet) { - ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); - ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<MobileDataIndicators> indicatorsArg = + ArgumentCaptor.forClass(MobileDataIndicators.class); // TODO: Verify all fields. verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators( - iconArg.capture(), - any(), - typeIconArg.capture(), - anyInt(), anyBoolean(), anyBoolean(), - any(CharSequence.class), any(CharSequence.class), any(), - anyBoolean(), anyInt(), eq(roaming), anyBoolean()); - IconState iconState = iconArg.getValue(); + indicatorsArg.capture()); + MobileDataIndicators expected = indicatorsArg.getValue(); int state = icon == -1 ? 0 : SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(), !inet); - assertEquals("Signal icon in status bar", state, iconState.icon); - assertEquals("Data icon in status bar", typeIcon, (int) typeIconArg.getValue()); - assertEquals("Visibility in status bar", visible, iconState.visible); + assertEquals("Signal icon in status bar", state, expected.statusIcon.icon); + assertEquals("Data icon in status bar", typeIcon, expected.statusType); + assertEquals("Visibility in status bar", visible, expected.statusIcon.visible); } protected void verifyLastMobileDataIndicatorsForVcn(boolean visible, int level, int typeIcon, boolean inet) { - ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); - ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<MobileDataIndicators> indicatorsArg = + ArgumentCaptor.forClass(MobileDataIndicators.class); verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators( - iconArg.capture(), - any(), - typeIconArg.capture(), - anyInt(), anyBoolean(), anyBoolean(), - any(CharSequence.class), any(CharSequence.class), any(), - anyBoolean(), anyInt(), anyBoolean(), anyBoolean()); - IconState iconState = iconArg.getValue(); + indicatorsArg.capture()); + + MobileDataIndicators expected = indicatorsArg.getValue(); int state = SignalDrawable.getState( level, CellSignalStrength.getNumSignalStrengthLevels(), !inet); - assertEquals("Signal icon in status bar", state, iconState.icon); - assertEquals("Data icon in status bar", typeIcon, (int) typeIconArg.getValue()); - assertEquals("Visibility in status bar", visible, iconState.visible); + assertEquals("Signal icon in status bar", state, expected.statusIcon.icon); + assertEquals("Data icon in status bar", typeIcon, expected.statusType); + assertEquals("Visibility in status bar", visible, expected.statusIcon.visible); } protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon, @@ -580,6 +572,8 @@ public class NetworkControllerBaseTest extends SysuiTestCase { boolean qsVisible, int qsIcon, int qsTypeIcon, boolean dataIn, boolean dataOut, boolean cutOut, CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml) { + ArgumentCaptor<MobileDataIndicators> indicatorsArg = + ArgumentCaptor.forClass(MobileDataIndicators.class); ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor<IconState> qsIconArg = ArgumentCaptor.forClass(IconState.class); @@ -592,17 +586,9 @@ public class NetworkControllerBaseTest extends SysuiTestCase { ArgumentCaptor.forClass(CharSequence.class); verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators( - iconArg.capture(), - qsIconArg.capture(), - typeIconArg.capture(), - qsTypeIconArg.capture(), - dataInArg.capture(), - dataOutArg.capture(), - typeContentDescriptionArg.capture(), - typeContentDescriptionHtmlArg.capture(), - any(), anyBoolean(), anyInt(), anyBoolean(), anyBoolean()); + indicatorsArg.capture()); - IconState iconState = iconArg.getValue(); + MobileDataIndicators expected = indicatorsArg.getValue(); int numSignalStrengthBins = CellSignalStrength.getNumSignalStrengthLevels(); if (mMobileSignalController.mInflateSignalStrengths) { @@ -610,29 +596,28 @@ public class NetworkControllerBaseTest extends SysuiTestCase { icon++; } int state = SignalDrawable.getState(icon, numSignalStrengthBins, cutOut); - assertEquals("Data icon in status bar", typeIcon, (int) typeIconArg.getValue()); - assertEquals("Signal icon in status bar", state, iconState.icon); - assertEquals("Visibility in status bar", visible, iconState.visible); + assertEquals("Data icon in status bar", typeIcon, expected.statusType); + assertEquals("Signal icon in status bar", state, expected.statusIcon.icon); + assertEquals("Visibility in status bar", visible, expected.statusIcon.visible); - iconState = qsIconArg.getValue(); if (visible) { - assertEquals("Visibility in quick settings", qsVisible, iconState.visible); - assertEquals("Signal icon in quick settings", state, iconState.icon); + assertEquals("Visibility in quick settings", qsVisible, expected.qsIcon.visible); + assertEquals("Signal icon in quick settings", state, expected.qsIcon.icon); } else { - assertEquals("Cellular is not default", null, iconState); + assertEquals("Cellular is not default", null, expected.qsIcon); } - assertEquals("Data icon in quick settings", qsTypeIcon, (int) qsTypeIconArg.getValue()); + assertEquals("Data icon in quick settings", qsTypeIcon, expected.qsType); assertEquals("Data direction in in quick settings", dataIn, - (boolean) dataInArg.getValue()); + expected.activityIn); assertEquals("Data direction out in quick settings", dataOut, - (boolean) dataOutArg.getValue()); + expected.activityOut); if (typeContentDescription != null) { // Only check if it was provided assertEquals("Type content description", typeContentDescription, - typeContentDescriptionArg.getValue()); + expected.typeContentDescription); } if (typeContentDescriptionHtml != null) { // Only check if it was provided assertEquals("Type content description (html)", typeContentDescriptionHtml, - typeContentDescriptionHtmlArg.getValue()); + expected.typeContentDescriptionHtml); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java index fc1a08ac3874..c6812a26c20b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java @@ -3,7 +3,6 @@ package com.android.systemui.statusbar.policy; import static junit.framework.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -21,7 +20,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import com.android.settingslib.mobile.TelephonyIcons; -import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; import org.junit.Before; import org.junit.Test; @@ -316,44 +315,42 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { } protected void verifyLastQsDataDirection(boolean in, boolean out) { - ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<WifiIndicators> indicatorsArg = + ArgumentCaptor.forClass(WifiIndicators.class); Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators( - anyBoolean(), any(), any(), inArg.capture(), outArg.capture(), any(), anyBoolean(), - any()); - assertEquals("WiFi data in, in quick settings", in, (boolean) inArg.getValue()); - assertEquals("WiFi data out, in quick settings", out, (boolean) outArg.getValue()); + indicatorsArg.capture()); + WifiIndicators expected = indicatorsArg.getValue(); + assertEquals("WiFi data in, in quick settings", in, expected.activityIn); + assertEquals("WiFi data out, in quick settings", out, expected.activityOut); } protected void verifyLastQsWifiIcon(boolean enabled, boolean connected, int icon, String description) { - ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); - ArgumentCaptor<Boolean> enabledArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class); + ArgumentCaptor<WifiIndicators> indicatorsArg = + ArgumentCaptor.forClass(WifiIndicators.class); Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators( - enabledArg.capture(), any(), iconArg.capture(), anyBoolean(), - anyBoolean(), descArg.capture(), anyBoolean(), any()); - IconState iconState = iconArg.getValue(); - assertEquals("WiFi enabled, in quick settings", enabled, (boolean) enabledArg.getValue()); - assertEquals("WiFI desc (ssid), in quick settings", description, descArg.getValue()); + indicatorsArg.capture()); + WifiIndicators expected = indicatorsArg.getValue(); + assertEquals("WiFi enabled, in quick settings", enabled, expected.enabled); + assertEquals("WiFI desc (ssid), in quick settings", description, expected.description); if (enabled && connected) { - assertEquals("WiFi connected, in quick settings", connected, iconState.visible); - assertEquals("WiFi signal, in quick settings", icon, iconState.icon); + assertEquals("WiFi connected, in quick settings", connected, expected.qsIcon.visible); + assertEquals("WiFi signal, in quick settings", icon, expected.qsIcon.icon); } else { - assertEquals("WiFi is not default", null, iconState); + assertEquals("WiFi is not default", null, expected.qsIcon); } } protected void verifyLastWifiIcon(boolean visible, int icon) { - ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); + ArgumentCaptor<WifiIndicators> indicatorsArg = + ArgumentCaptor.forClass(WifiIndicators.class); Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators( - anyBoolean(), iconArg.capture(), any(), anyBoolean(), anyBoolean(), - any(), anyBoolean(), any()); - IconState iconState = iconArg.getValue(); - assertEquals("WiFi visible, in status bar", visible, iconState.visible); - assertEquals("WiFi signal, in status bar", icon, iconState.icon); + indicatorsArg.capture()); + WifiIndicators expected = indicatorsArg.getValue(); + assertEquals("WiFi visible, in status bar", visible, expected.statusIcon.visible); + assertEquals("WiFi signal, in status bar", icon, expected.statusIcon.icon); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java index 45828c3f73ad..6067b42e0ef8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java @@ -23,6 +23,7 @@ import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_IC import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SETTINGS; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SYSUI; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_THEME_PICKER; +import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_NEUTRAL_PALETTE; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SHAPE; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE; import static com.android.systemui.theme.ThemeOverlayApplier.SETTINGS_PACKAGE; @@ -31,7 +32,6 @@ import static com.android.systemui.theme.ThemeOverlayApplier.SYSUI_PACKAGE; import static com.android.systemui.theme.ThemeOverlayApplier.THEME_CATEGORIES; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; @@ -87,7 +87,9 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { private static final String THEMEPICKER_PACKAGE = "com.android.wallpaper"; private static final String LAUNCHER_PACKAGE = "com.android.launcher3"; private static final UserHandle TEST_USER = UserHandle.of(5); - private static final Set<UserHandle> TEST_USER_HANDLES = Sets.newHashSet(TEST_USER); + private static final UserHandle TEST_USER_MANAGED_PROFILE = UserHandle.of(6); + private static final Set<UserHandle> TEST_USER_HANDLES = + Sets.newHashSet(TEST_USER_MANAGED_PROFILE); @Mock OverlayManager mOverlayManager; @@ -114,6 +116,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, false), createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE, ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, false), + createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_NEUTRAL_PALETTE, + ANDROID_PACKAGE, OVERLAY_CATEGORY_NEUTRAL_PALETTE, false), createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, false), createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SHAPE, @@ -124,6 +128,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, true), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE, ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, true), + createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_NEUTRAL_PALETTE, + ANDROID_PACKAGE, OVERLAY_CATEGORY_NEUTRAL_PALETTE, true), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, true), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SHAPE, @@ -154,13 +160,19 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, false), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER, THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, true))); + + OverlayInfo launcherTargetInfo = new OverlayInfo("packageName", LAUNCHER_PACKAGE, + null, null, "/", 0, 0, 0, false); + when(mOverlayManager.getOverlayInfo(any(OverlayIdentifier.class), any())) + .thenReturn(launcherTargetInfo); clearInvocations(mOverlayManager); verify(mDumpManager).registerDumpable(any(), any()); } @Test public void allCategoriesSpecified_allEnabledExclusively() { - mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES); + mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), + TEST_USER_HANDLES); verify(mOverlayManager).commit(any()); for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { @@ -171,7 +183,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { @Test public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() { - mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES); + mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), + TEST_USER_HANDLES); for (Map.Entry<String, OverlayIdentifier> entry : ALL_CATEGORIES_MAP.entrySet()) { if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) { @@ -187,27 +200,25 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { @Test public void allCategoriesSpecified_enabledForAllUserHandles() { Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES); - UserHandle newUserHandle = UserHandle.of(10); - userHandles.add(newUserHandle); - mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, userHandles); + mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), + userHandles); for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), eq(TEST_USER.getIdentifier())); - verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), - eq(newUserHandle.getIdentifier())); + // Not enabled for work profile because the target package is LAUNCHER_PACKAGE + verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true), + eq(TEST_USER_MANAGED_PROFILE.getIdentifier())); } } @Test public void applyCurrentUserOverlays_createsPendingOverlays() { - Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES); - UserHandle newUserHandle = UserHandle.of(10); - userHandles.add(newUserHandle); - FabricatedOverlay[] pendingCreation = new FabricatedOverlay[] { + FabricatedOverlay[] pendingCreation = new FabricatedOverlay[]{ mock(FabricatedOverlay.class) }; - mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation, userHandles); + mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation, + TEST_USER.getIdentifier(), TEST_USER_HANDLES); for (FabricatedOverlay overlay : pendingCreation) { verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay)); @@ -215,20 +226,13 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { } @Test - public void allCategoriesSpecified_overlayManagerNotQueried() { - mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER_HANDLES); - - verify(mOverlayManager, never()) - .getOverlayInfosForTarget(anyString(), any(UserHandle.class)); - } - - @Test public void someCategoriesSpecified_specifiedEnabled_unspecifiedDisabled() { Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP); categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS); categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID); - mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES); + mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(), + TEST_USER_HANDLES); for (OverlayIdentifier overlayPackage : categoryToPackage.values()) { verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), @@ -244,7 +248,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { @Test public void zeroCategoriesSpecified_allDisabled() { - mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER_HANDLES); + mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER.getIdentifier(), + TEST_USER_HANDLES); for (String category : THEME_CATEGORIES) { verify(mTransactionBuilder).setEnabled( @@ -258,7 +263,8 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP); categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category")); - mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES); + mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(), + TEST_USER_HANDLES); verify(mTransactionBuilder, never()).setEnabled( eq(new OverlayIdentifier("com.example.blah.category")), eq(false), @@ -268,23 +274,6 @@ public class ThemeOverlayApplierTest extends SysuiTestCase { eq(TEST_USER.getIdentifier())); } - @Test - public void overlayManagerOnlyQueriedForUnspecifiedPackages() { - Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP); - categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS); - - mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER_HANDLES); - - verify(mOverlayManager).getOverlayInfosForTarget(SETTINGS_PACKAGE, UserHandle.SYSTEM); - verify(mOverlayManager, never()).getOverlayInfosForTarget(ANDROID_PACKAGE, - UserHandle.SYSTEM); - verify(mOverlayManager, never()).getOverlayInfosForTarget(SYSUI_PACKAGE, UserHandle.SYSTEM); - verify(mOverlayManager, never()).getOverlayInfosForTarget(LAUNCHER_PACKAGE, - UserHandle.SYSTEM); - verify(mOverlayManager, never()).getOverlayInfosForTarget(THEMEPICKER_PACKAGE, - UserHandle.SYSTEM); - } - private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName, String category, boolean enabled) { return new OverlayInfo(packageName, null, targetPackageName, null, category, "", diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index aa385effa931..d80c40fcd07b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -143,7 +143,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(Map.class); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(themeOverlays.capture(), any(), any()); + .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any()); // Assert that we received the colors that we were expecting assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) @@ -175,7 +175,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(Map.class); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(themeOverlays.capture(), any(), any()); + .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any()); // Assert that we received the colors that we were expecting assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) @@ -198,7 +198,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(Map.class); verify(mThemeOverlayApplier) - .applyCurrentUserOverlays(themeOverlays.capture(), any(), any()); + .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any()); // Assert that we received the colors that we were expecting assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java index c5a197eef2d4..242fe9f5fffe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java @@ -16,6 +16,8 @@ package com.android.systemui.util.sensors; +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.assertNull; @@ -86,6 +88,29 @@ public class ProximityCheckTest extends SysuiTestCase { } @Test + public void testNotLoaded() { + mFakeProximitySensor.setSensorAvailable(false); + + assertThat(mTestableCallback.mLastResult).isNull(); + assertThat(mTestableCallback.mNumCalls).isEqualTo(0); + + mProximityCheck.check(100, mTestableCallback); + + assertThat(mTestableCallback.mLastResult).isNull(); + assertThat(mTestableCallback.mNumCalls).isEqualTo(1); + + mFakeProximitySensor.setSensorAvailable(true); + + mProximityCheck.check(100, mTestableCallback); + + mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0)); + mFakeProximitySensor.alertListeners(); + + assertThat(mTestableCallback.mLastResult).isNotNull(); + assertThat(mTestableCallback.mNumCalls).isEqualTo(2); + } + + @Test public void testProxDoesntCancelOthers() { assertFalse(mFakeProximitySensor.isRegistered()); // We don't need our "other" listener to do anything. Just ensure our sensor is registered. diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java index 97d4aa7e4d19..7d8a28812fde 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java @@ -14,7 +14,6 @@ package com.android.systemui.utils.leaks; -import android.os.UserHandle; import android.testing.LeakCheck; import com.android.systemui.tuner.TunerService; @@ -78,12 +77,15 @@ public class FakeTunerService extends TunerService { } @Override - public void setTunerEnabled(UserHandle user, boolean enabled) { + public void setTunerEnabled(boolean enabled) { mEnabled = enabled; } @Override - public boolean isTunerEnabled(UserHandle user) { + public boolean isTunerEnabled() { return mEnabled; } + + @Override + public void showResetRequest(Runnable onDisabled) {} } diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index c0856892dc44..3cea17567173 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -22,6 +22,7 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.KeyguardManager; @@ -37,6 +38,8 @@ import android.view.accessibility.AccessibilityManager; import androidx.test.filters.SmallTest; +import com.android.systemui.Prefs; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.plugins.VolumeDialogController.State; @@ -58,6 +61,11 @@ import java.util.function.Predicate; public class VolumeDialogImplTest extends SysuiTestCase { VolumeDialogImpl mDialog; + View mActiveRinger; + View mDrawerContainer; + View mDrawerVibrate; + View mDrawerMute; + View mDrawerNormal; @Mock VolumeDialogController mController; @@ -80,6 +88,17 @@ public class VolumeDialogImplTest extends SysuiTestCase { mDialog.init(0, null); State state = createShellState(); mDialog.onStateChangedH(state); + + mActiveRinger = mDialog.getDialogView().findViewById( + R.id.volume_new_ringer_active_icon_container); + mDrawerContainer = mDialog.getDialogView().findViewById(R.id.volume_drawer_container); + mDrawerVibrate = mDrawerContainer.findViewById(R.id.volume_drawer_vibrate); + mDrawerMute = mDrawerContainer.findViewById(R.id.volume_drawer_mute); + mDrawerNormal = mDrawerContainer.findViewById(R.id.volume_drawer_normal); + + Prefs.putInt(mContext, + Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, + VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1); } private State createShellState() { @@ -207,6 +226,48 @@ public class VolumeDialogImplTest extends SysuiTestCase { verify(mController, never()).vibrate(any()); } + @Test + public void testSelectVibrateFromDrawer() { + final State initialUnsetState = new State(); + initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; + mDialog.onStateChangedH(initialUnsetState); + + mActiveRinger.performClick(); + mDrawerVibrate.performClick(); + + // Make sure we've actually changed the ringer mode. + verify(mController, times(1)).setRingerMode( + AudioManager.RINGER_MODE_VIBRATE, false); + } + + @Test + public void testSelectMuteFromDrawer() { + final State initialUnsetState = new State(); + initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; + mDialog.onStateChangedH(initialUnsetState); + + mActiveRinger.performClick(); + mDrawerMute.performClick(); + + // Make sure we've actually changed the ringer mode. + verify(mController, times(1)).setRingerMode( + AudioManager.RINGER_MODE_SILENT, false); + } + + @Test + public void testSelectNormalFromDrawer() { + final State initialUnsetState = new State(); + initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE; + mDialog.onStateChangedH(initialUnsetState); + + mActiveRinger.performClick(); + mDrawerNormal.performClick(); + + // Make sure we've actually changed the ringer mode. + verify(mController, times(1)).setRingerMode( + AudioManager.RINGER_MODE_NORMAL, false); + } + /* @Test public void testContentDescriptions() { diff --git a/packages/SystemUI/tools/lint/baseline.xml b/packages/SystemUI/tools/lint/baseline.xml index 3e494032b8e2..9a2e3207a6f4 100644 --- a/packages/SystemUI/tools/lint/baseline.xml +++ b/packages/SystemUI/tools/lint/baseline.xml @@ -1262,17 +1262,6 @@ <issue id="MergeRootFrame" message="This `<FrameLayout>` can be replaced with a `<merge>` tag" - errorLine1="<FrameLayout" - errorLine2="^"> - <location - file="res/layout/pip_menu_activity.xml" - line="16" - column="1"/> - </issue> - - <issue - id="MergeRootFrame" - message="This `<FrameLayout>` can be replaced with a `<merge>` tag" errorLine1="<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"" errorLine2="^"> <location diff --git a/packages/VpnDialogs/Android.bp b/packages/VpnDialogs/Android.bp index 6f2f50c0ddd4..05135b2bebf9 100644 --- a/packages/VpnDialogs/Android.bp +++ b/packages/VpnDialogs/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_app { name: "VpnDialogs", certificate: "platform", diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java index 6dcad255eee4..3502baa99b6d 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java @@ -23,7 +23,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; -import android.net.ConnectivityManager; +import android.net.VpnManager; import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; @@ -42,7 +42,7 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity private static final String TAG = "VpnDisconnected"; - private ConnectivityManager mService; + private VpnManager mService; private int mUserId; private String mVpnPackage; @@ -51,8 +51,8 @@ public class AlwaysOnDisconnectedDialog extends AlertActivity super.onCreate(savedInstanceState); mUserId = UserHandle.myUserId(); - final ConnectivityManager cm = getSystemService(ConnectivityManager.class); - mVpnPackage = cm.getAlwaysOnVpnPackageForUser(mUserId); + final VpnManager vm = getSystemService(VpnManager.class); + mVpnPackage = vm.getAlwaysOnVpnPackageForUser(mUserId); if (mVpnPackage == null) { finish(); return; diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java index aab01d03b96d..fb2367843fc1 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java @@ -21,7 +21,6 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE import android.content.DialogInterface; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; -import android.net.ConnectivityManager; import android.net.VpnManager; import android.os.Bundle; import android.os.UserHandle; @@ -45,7 +44,6 @@ public class ConfirmDialog extends AlertActivity private String mPackage; - private ConnectivityManager mCm; // TODO: switch entirely to VpnManager once VPN code moves private VpnManager mVm; public ConfirmDialog() { @@ -60,7 +58,6 @@ public class ConfirmDialog extends AlertActivity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPackage = getCallingPackage(); - mCm = getSystemService(ConnectivityManager.class); mVm = getSystemService(VpnManager.class); if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) { @@ -72,7 +69,7 @@ public class ConfirmDialog extends AlertActivity finish(); return; } - final String alwaysOnVpnPackage = mCm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId()); + final String alwaysOnVpnPackage = mVm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId()); // Can't prepare new vpn app when another vpn is always-on if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) { finish(); diff --git a/packages/WAPPushManager/Android.bp b/packages/WAPPushManager/Android.bp index 0b62c72c749f..71ec81909579 100644 --- a/packages/WAPPushManager/Android.bp +++ b/packages/WAPPushManager/Android.bp @@ -1,5 +1,24 @@ // Copyright 2007-2008 The Android Open Source Project +package { + default_applicable_licenses: [ + "frameworks_base_packages_WAPPushManager_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_packages_WAPPushManager_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + android_app { name: "WAPPushManager", defaults: ["platform_app_defaults"], diff --git a/packages/WAPPushManager/tests/Android.bp b/packages/WAPPushManager/tests/Android.bp index 25c6121324ea..0a179383fab6 100644 --- a/packages/WAPPushManager/tests/Android.bp +++ b/packages/WAPPushManager/tests/Android.bp @@ -12,6 +12,17 @@ // 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_packages_WAPPushManager_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: [ + "frameworks_base_packages_WAPPushManager_license", + ], +} + android_test { name: "WAPPushManagerTests", libs: [ diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp index e52d53eb4082..d142f25c6a62 100644 --- a/packages/WallpaperBackup/Android.bp +++ b/packages/WallpaperBackup/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_app { name: "WallpaperBackup", defaults: ["platform_app_defaults"], diff --git a/packages/WallpaperCropper/Android.bp b/packages/WallpaperCropper/Android.bp index df97a3c08156..23ec23c9c51a 100644 --- a/packages/WallpaperCropper/Android.bp +++ b/packages/WallpaperCropper/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "WallpaperCropper", defaults: ["platform_app_defaults"], diff --git a/packages/overlays/AccentColorAmethystOverlay/Android.bp b/packages/overlays/AccentColorAmethystOverlay/Android.bp index 7519b12dd2d9..186d770c09a5 100644 --- a/packages/overlays/AccentColorAmethystOverlay/Android.bp +++ b/packages/overlays/AccentColorAmethystOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorAmethystOverlay", theme: "AccentColorAmethyst", diff --git a/packages/overlays/AccentColorAquamarineOverlay/Android.bp b/packages/overlays/AccentColorAquamarineOverlay/Android.bp index 4469b36cfbc5..7fd64f374522 100644 --- a/packages/overlays/AccentColorAquamarineOverlay/Android.bp +++ b/packages/overlays/AccentColorAquamarineOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorAquamarineOverlay", theme: "AccentColorAquamarine", diff --git a/packages/overlays/AccentColorBlackOverlay/Android.bp b/packages/overlays/AccentColorBlackOverlay/Android.bp index bfee26ea52dd..ac923ebd7cd9 100644 --- a/packages/overlays/AccentColorBlackOverlay/Android.bp +++ b/packages/overlays/AccentColorBlackOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorBlackOverlay", theme: "AccentColorBlack", diff --git a/packages/overlays/AccentColorCarbonOverlay/Android.bp b/packages/overlays/AccentColorCarbonOverlay/Android.bp index 47f66dd9ba62..f4f1b8b50a1e 100644 --- a/packages/overlays/AccentColorCarbonOverlay/Android.bp +++ b/packages/overlays/AccentColorCarbonOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorCarbonOverlay", theme: "AccentColorCarbon", diff --git a/packages/overlays/AccentColorCinnamonOverlay/Android.bp b/packages/overlays/AccentColorCinnamonOverlay/Android.bp index 8250315256b8..53899bfefd98 100644 --- a/packages/overlays/AccentColorCinnamonOverlay/Android.bp +++ b/packages/overlays/AccentColorCinnamonOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorCinnamonOverlay", theme: "AccentColorCinnamon", diff --git a/packages/overlays/AccentColorGreenOverlay/Android.bp b/packages/overlays/AccentColorGreenOverlay/Android.bp index 15b50c76aa38..5b1f7447a7ca 100644 --- a/packages/overlays/AccentColorGreenOverlay/Android.bp +++ b/packages/overlays/AccentColorGreenOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorGreenOverlay", theme: "AccentColorGreen", diff --git a/packages/overlays/AccentColorOceanOverlay/Android.bp b/packages/overlays/AccentColorOceanOverlay/Android.bp index 6ad63bc92816..a85883044dc2 100644 --- a/packages/overlays/AccentColorOceanOverlay/Android.bp +++ b/packages/overlays/AccentColorOceanOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorOceanOverlay", theme: "AccentColorOcean", diff --git a/packages/overlays/AccentColorOrchidOverlay/Android.bp b/packages/overlays/AccentColorOrchidOverlay/Android.bp index b66933397e12..31ed30921664 100644 --- a/packages/overlays/AccentColorOrchidOverlay/Android.bp +++ b/packages/overlays/AccentColorOrchidOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorOrchidOverlay", theme: "AccentColorOrchid", diff --git a/packages/overlays/AccentColorPaletteOverlay/Android.bp b/packages/overlays/AccentColorPaletteOverlay/Android.bp index eeefd1623a49..a6cc1dec37dd 100644 --- a/packages/overlays/AccentColorPaletteOverlay/Android.bp +++ b/packages/overlays/AccentColorPaletteOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorPaletteOverlay", theme: "AccentColorPalette", diff --git a/packages/overlays/AccentColorPurpleOverlay/Android.bp b/packages/overlays/AccentColorPurpleOverlay/Android.bp index ead95df18d9e..80e0ab1159b7 100644 --- a/packages/overlays/AccentColorPurpleOverlay/Android.bp +++ b/packages/overlays/AccentColorPurpleOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorPurpleOverlay", theme: "AccentColorPurple", diff --git a/packages/overlays/AccentColorSandOverlay/Android.bp b/packages/overlays/AccentColorSandOverlay/Android.bp index f70578a1ec38..771abca4c3f6 100644 --- a/packages/overlays/AccentColorSandOverlay/Android.bp +++ b/packages/overlays/AccentColorSandOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorSandOverlay", theme: "AccentColorSand", diff --git a/packages/overlays/AccentColorSpaceOverlay/Android.bp b/packages/overlays/AccentColorSpaceOverlay/Android.bp index 1d713df3cfdd..8e4abacf1ef2 100644 --- a/packages/overlays/AccentColorSpaceOverlay/Android.bp +++ b/packages/overlays/AccentColorSpaceOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorSpaceOverlay", theme: "AccentColorSpace", diff --git a/packages/overlays/AccentColorTangerineOverlay/Android.bp b/packages/overlays/AccentColorTangerineOverlay/Android.bp index d3b1e54fe823..75c708ec9fe7 100644 --- a/packages/overlays/AccentColorTangerineOverlay/Android.bp +++ b/packages/overlays/AccentColorTangerineOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "AccentColorTangerineOverlay", theme: "AccentColorTangerine", diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index cdc09034e75b..99dfd9eab891 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -16,6 +16,9 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := frameworks-base-overlays +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE LOCAL_REQUIRED_MODULES := \ AccentColorBlackOverlay \ AccentColorCinnamonOverlay \ @@ -84,6 +87,9 @@ include $(BUILD_PHONY_PACKAGE) include $(CLEAR_VARS) LOCAL_MODULE := frameworks-base-overlays-debug +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE include $(BUILD_PHONY_PACKAGE) include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp index b8def98791be..8e03809cadf0 100644 --- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/Android.bp @@ -1,3 +1,12 @@ +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"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationCornerOverlay", theme: "DisplayCutoutEmulationCorner", diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp index b64ddfd82c05..afa5b649a432 100644 --- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/Android.bp @@ -1,3 +1,12 @@ +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"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationDoubleOverlay", theme: "DisplayCutoutEmulationDouble", diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp index 86cfebfbd0db..eae907db00ff 100644 --- a/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/Android.bp @@ -1,3 +1,12 @@ +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"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationHoleOverlay", theme: "DisplayCutoutEmulationHole", diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp index 28ad9db119cb..25bc676bb027 100644 --- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.bp @@ -1,3 +1,12 @@ +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"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationNarrowOverlay", theme: "DisplayCutoutEmulationNarrow", diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp index cd00386658ed..2828612254a3 100644 --- a/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.bp @@ -1,3 +1,12 @@ +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"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationTallOverlay", theme: "DisplayCutoutEmulationTall", diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp index d5fe683d5e55..66be777649ed 100644 --- a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.bp @@ -1,3 +1,12 @@ +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"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationWaterfallOverlay", theme: "DisplayCutoutEmulationWaterfall", diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp index 0157ec48ee3a..e71cefe7643e 100644 --- a/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp +++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.bp @@ -1,3 +1,12 @@ +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"], +} + runtime_resource_overlay { name: "DisplayCutoutEmulationWideOverlay", theme: "DisplayCutoutEmulationWide", diff --git a/packages/overlays/FontNotoSerifSourceOverlay/Android.bp b/packages/overlays/FontNotoSerifSourceOverlay/Android.bp index 7fd145b57934..231295b41a27 100644 --- a/packages/overlays/FontNotoSerifSourceOverlay/Android.bp +++ b/packages/overlays/FontNotoSerifSourceOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "FontNotoSerifSourceOverlay", theme: "FontNotoSerifSource", diff --git a/packages/overlays/IconPackCircularAndroidOverlay/Android.bp b/packages/overlays/IconPackCircularAndroidOverlay/Android.bp index cd5829aae3b6..70403588da33 100644 --- a/packages/overlays/IconPackCircularAndroidOverlay/Android.bp +++ b/packages/overlays/IconPackCircularAndroidOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackCircularAndroidOverlay", theme: "IconPackCircularAndroid", diff --git a/packages/overlays/IconPackCircularLauncherOverlay/Android.bp b/packages/overlays/IconPackCircularLauncherOverlay/Android.bp index 5f2491d3cd66..4f8b6637a2b5 100644 --- a/packages/overlays/IconPackCircularLauncherOverlay/Android.bp +++ b/packages/overlays/IconPackCircularLauncherOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackCircularLauncherOverlay", theme: "IconPackCircularLauncher", diff --git a/packages/overlays/IconPackCircularSettingsOverlay/Android.bp b/packages/overlays/IconPackCircularSettingsOverlay/Android.bp index d7bc6573e986..93220c87dcf9 100644 --- a/packages/overlays/IconPackCircularSettingsOverlay/Android.bp +++ b/packages/overlays/IconPackCircularSettingsOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackCircularSettingsOverlay", theme: "IconPackCircularSettings", diff --git a/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp b/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp index 73b8cd8e7d5f..4eaa4205fe96 100644 --- a/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp +++ b/packages/overlays/IconPackCircularSystemUIOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackCircularSystemUIOverlay", theme: "IconPackCircularSystemUI", diff --git a/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp b/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp index 50639322607e..5105b7931922 100644 --- a/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackCircularThemePickerOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackCircularThemePickerOverlay", theme: "IconPackCircularThemePicker", diff --git a/packages/overlays/IconPackFilledAndroidOverlay/Android.bp b/packages/overlays/IconPackFilledAndroidOverlay/Android.bp index 83f365678caf..3c4025d6026c 100644 --- a/packages/overlays/IconPackFilledAndroidOverlay/Android.bp +++ b/packages/overlays/IconPackFilledAndroidOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackFilledAndroidOverlay", theme: "IconPackFilledAndroid", diff --git a/packages/overlays/IconPackFilledLauncherOverlay/Android.bp b/packages/overlays/IconPackFilledLauncherOverlay/Android.bp index 6ca256638a03..3c5078ce5933 100644 --- a/packages/overlays/IconPackFilledLauncherOverlay/Android.bp +++ b/packages/overlays/IconPackFilledLauncherOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackFilledLauncherOverlay", theme: "IconPackFilledLauncher", diff --git a/packages/overlays/IconPackFilledSettingsOverlay/Android.bp b/packages/overlays/IconPackFilledSettingsOverlay/Android.bp index 8551bd54d588..b5148c23e053 100644 --- a/packages/overlays/IconPackFilledSettingsOverlay/Android.bp +++ b/packages/overlays/IconPackFilledSettingsOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackFilledSettingsOverlay", theme: "IconPackFilledSettings", diff --git a/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp b/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp index 684deb453d74..eb040a5a132d 100644 --- a/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp +++ b/packages/overlays/IconPackFilledSystemUIOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackFilledSystemUIOverlay", theme: "IconPackFilledSystemUI", diff --git a/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp b/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp index dae378f67774..bee48089109b 100644 --- a/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackFilledThemePickerOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackFilledThemePickerOverlay", theme: "IconPackFilledThemePicker", diff --git a/packages/overlays/IconPackKaiAndroidOverlay/Android.bp b/packages/overlays/IconPackKaiAndroidOverlay/Android.bp index 4161e252c312..ee588c1f1c55 100644 --- a/packages/overlays/IconPackKaiAndroidOverlay/Android.bp +++ b/packages/overlays/IconPackKaiAndroidOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackKaiAndroidOverlay", theme: "IconPackKaiAndroid", diff --git a/packages/overlays/IconPackKaiLauncherOverlay/Android.bp b/packages/overlays/IconPackKaiLauncherOverlay/Android.bp index 4bf8f11a8475..dcdad7aaed4e 100644 --- a/packages/overlays/IconPackKaiLauncherOverlay/Android.bp +++ b/packages/overlays/IconPackKaiLauncherOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackKaiLauncherOverlay", theme: "IconPackKaiLauncher", diff --git a/packages/overlays/IconPackKaiSettingsOverlay/Android.bp b/packages/overlays/IconPackKaiSettingsOverlay/Android.bp index c43f9e58a0c9..974bb540f4e7 100644 --- a/packages/overlays/IconPackKaiSettingsOverlay/Android.bp +++ b/packages/overlays/IconPackKaiSettingsOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackKaiSettingsOverlay", theme: "IconPackKaiSettings", diff --git a/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp b/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp index 22a8c2843956..b04ca6132c6d 100644 --- a/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp +++ b/packages/overlays/IconPackKaiSystemUIOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackKaiSystemUIOverlay", theme: "IconPackKaiSystemUI", diff --git a/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp b/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp index 85eb1c46272f..875cd1d44d92 100644 --- a/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackKaiThemePickerOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackKaiThemePickerOverlay", theme: "IconPackKaiThemePicker", diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp b/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp index 948f015cabec..cb7b01361da3 100644 --- a/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp +++ b/packages/overlays/IconPackRoundedAndroidOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackRoundedAndroidOverlay", theme: "IconPackRoundedAndroid", diff --git a/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp b/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp index 5fbe6352f889..8ab6d957720e 100644 --- a/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp +++ b/packages/overlays/IconPackRoundedLauncherOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackRoundedLauncherOverlay", theme: "IconPackRoundedLauncher", diff --git a/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp b/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp index b1a07137eaa2..ee2f98a96864 100644 --- a/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp +++ b/packages/overlays/IconPackRoundedSettingsOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackRoundedSettingsOverlay", theme: "IconPackRoundedSettings", diff --git a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp index 643299898169..ee0220a44943 100644 --- a/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp +++ b/packages/overlays/IconPackRoundedSystemUIOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackRoundedSystemUIOverlay", theme: "IconPackRoundedSystemUI", diff --git a/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp index 95e1d3a2ac6c..d74765c33607 100644 --- a/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackRoundedThemePickerOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackRoundedThemePickerOverlay", theme: "IconPackRoundedTheme", diff --git a/packages/overlays/IconPackSamAndroidOverlay/Android.bp b/packages/overlays/IconPackSamAndroidOverlay/Android.bp index e8c33b10fb9e..2e9dc3472674 100644 --- a/packages/overlays/IconPackSamAndroidOverlay/Android.bp +++ b/packages/overlays/IconPackSamAndroidOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackSamAndroidOverlay", theme: "IconPackSamAndroid", diff --git a/packages/overlays/IconPackSamLauncherOverlay/Android.bp b/packages/overlays/IconPackSamLauncherOverlay/Android.bp index a46964665862..aa0cf0077ab9 100644 --- a/packages/overlays/IconPackSamLauncherOverlay/Android.bp +++ b/packages/overlays/IconPackSamLauncherOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackSamLauncherOverlay", theme: "IconPackSamLauncher", diff --git a/packages/overlays/IconPackSamSettingsOverlay/Android.bp b/packages/overlays/IconPackSamSettingsOverlay/Android.bp index bc1fa458ad9a..a62037f3d5c2 100644 --- a/packages/overlays/IconPackSamSettingsOverlay/Android.bp +++ b/packages/overlays/IconPackSamSettingsOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackSamSettingsOverlay", theme: "IconPackSamSettings", diff --git a/packages/overlays/IconPackSamSystemUIOverlay/Android.bp b/packages/overlays/IconPackSamSystemUIOverlay/Android.bp index db77352f42b9..96ba7a096e6a 100644 --- a/packages/overlays/IconPackSamSystemUIOverlay/Android.bp +++ b/packages/overlays/IconPackSamSystemUIOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackSamSystemUIOverlay", theme: "IconPackSamSystemUI", diff --git a/packages/overlays/IconPackSamThemePickerOverlay/Android.bp b/packages/overlays/IconPackSamThemePickerOverlay/Android.bp index c216868a2a5e..7376f03a2c7f 100644 --- a/packages/overlays/IconPackSamThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackSamThemePickerOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackSamThemePickerOverlay", theme: "IconPackSamThemePicker", diff --git a/packages/overlays/IconPackVictorAndroidOverlay/Android.bp b/packages/overlays/IconPackVictorAndroidOverlay/Android.bp index de62af1c6287..ee7377863287 100644 --- a/packages/overlays/IconPackVictorAndroidOverlay/Android.bp +++ b/packages/overlays/IconPackVictorAndroidOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackVictorAndroidOverlay", theme: "IconPackVictorAndroid", diff --git a/packages/overlays/IconPackVictorLauncherOverlay/Android.bp b/packages/overlays/IconPackVictorLauncherOverlay/Android.bp index fc4c3606de8e..a0cd45a81e20 100644 --- a/packages/overlays/IconPackVictorLauncherOverlay/Android.bp +++ b/packages/overlays/IconPackVictorLauncherOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackVictorLauncherOverlay", theme: "IconPackVictorLauncher", diff --git a/packages/overlays/IconPackVictorSettingsOverlay/Android.bp b/packages/overlays/IconPackVictorSettingsOverlay/Android.bp index 046bb3d25a12..7807c6bcccc8 100644 --- a/packages/overlays/IconPackVictorSettingsOverlay/Android.bp +++ b/packages/overlays/IconPackVictorSettingsOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackVictorSettingsOverlay", theme: "IconPackVictorSettings", diff --git a/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp b/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp index b8a9e77ebf7d..2deb6cd73ba1 100644 --- a/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp +++ b/packages/overlays/IconPackVictorSystemUIOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackVictorSystemUIOverlay", theme: "IconPackVictorSystemUI", diff --git a/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp b/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp index 6f0144e9e351..a18ebb3eaea5 100644 --- a/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp +++ b/packages/overlays/IconPackVictorThemePickerOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconPackVictorThemePickerOverlay", theme: "IconPackVictorThemePicker", diff --git a/packages/overlays/IconShapeHeartOverlay/Android.bp b/packages/overlays/IconShapeHeartOverlay/Android.bp index ec55712f2309..1da8f4ff7fb3 100644 --- a/packages/overlays/IconShapeHeartOverlay/Android.bp +++ b/packages/overlays/IconShapeHeartOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconShapeHeartOverlay", theme: "IconShapeHeart", diff --git a/packages/overlays/IconShapePebbleOverlay/Android.bp b/packages/overlays/IconShapePebbleOverlay/Android.bp index 7dc4fde140c8..fa2a5bb825f3 100644 --- a/packages/overlays/IconShapePebbleOverlay/Android.bp +++ b/packages/overlays/IconShapePebbleOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconShapePebbleOverlay", theme: "IconShapePebble", diff --git a/packages/overlays/IconShapeRoundedRectOverlay/Android.bp b/packages/overlays/IconShapeRoundedRectOverlay/Android.bp index b8b85314a211..5052d08f1edf 100644 --- a/packages/overlays/IconShapeRoundedRectOverlay/Android.bp +++ b/packages/overlays/IconShapeRoundedRectOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconShapeRoundedRectOverlay", theme: "IconShapeRoundedRect", diff --git a/packages/overlays/IconShapeSquareOverlay/Android.bp b/packages/overlays/IconShapeSquareOverlay/Android.bp index fdeffeee25d2..1176abddd71f 100644 --- a/packages/overlays/IconShapeSquareOverlay/Android.bp +++ b/packages/overlays/IconShapeSquareOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconShapeSquareOverlay", theme: "IconShapeSquare", diff --git a/packages/overlays/IconShapeSquircleOverlay/Android.bp b/packages/overlays/IconShapeSquircleOverlay/Android.bp index 468f0f7ee2c7..8c219f340040 100644 --- a/packages/overlays/IconShapeSquircleOverlay/Android.bp +++ b/packages/overlays/IconShapeSquircleOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconShapeSquircleOverlay", theme: "IconShapeSquircle", diff --git a/packages/overlays/IconShapeTaperedRectOverlay/Android.bp b/packages/overlays/IconShapeTaperedRectOverlay/Android.bp index 1e48cd154317..78855e8daba2 100644 --- a/packages/overlays/IconShapeTaperedRectOverlay/Android.bp +++ b/packages/overlays/IconShapeTaperedRectOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconShapeTaperedRectOverlay", theme: "IconShapeTaperedRect", diff --git a/packages/overlays/IconShapeTeardropOverlay/Android.bp b/packages/overlays/IconShapeTeardropOverlay/Android.bp index 017d58ec4e77..dd36f4ffc995 100644 --- a/packages/overlays/IconShapeTeardropOverlay/Android.bp +++ b/packages/overlays/IconShapeTeardropOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconShapeTeardropOverlay", theme: "IconShapeTeardrop", diff --git a/packages/overlays/IconShapeVesselOverlay/Android.bp b/packages/overlays/IconShapeVesselOverlay/Android.bp index ba3b30954e7f..2e7f8bc6cf66 100644 --- a/packages/overlays/IconShapeVesselOverlay/Android.bp +++ b/packages/overlays/IconShapeVesselOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "IconShapeVesselOverlay", theme: "IconShapeVessel", diff --git a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp index e4fcce15250f..35f671bab875 100644 --- a/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp +++ b/packages/overlays/NavigationBarMode2ButtonOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "NavigationBarMode2ButtonOverlay", theme: "NavigationBarMode2Button", diff --git a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp index 9b6c9988b9bb..fe9cc81f0d52 100644 --- a/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp +++ b/packages/overlays/NavigationBarMode3ButtonOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "NavigationBarMode3ButtonOverlay", theme: "NavigationBarMode3Button", diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp index ba290459a279..791f4205481f 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp +++ b/packages/overlays/NavigationBarModeGesturalOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "NavigationBarModeGesturalOverlay", theme: "NavigationBarModeGestural", diff --git a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp index 0c688a988533..28f9f3341307 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp +++ b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "NavigationBarModeGesturalOverlayExtraWideBack", theme: "NavigationBarModeGesturalExtraWideBack", diff --git a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp index 85d514f7fbac..f8a56030e0e4 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp +++ b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "NavigationBarModeGesturalOverlayNarrowBack", theme: "NavigationBarModeGesturalNarrowBack", diff --git a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp index 84be626eefec..60ee6d540dc8 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp +++ b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "NavigationBarModeGesturalOverlayWideBack", theme: "NavigationBarModeGesturalWideBack", diff --git a/packages/overlays/OneHandedModeGesturalOverlay/Android.bp b/packages/overlays/OneHandedModeGesturalOverlay/Android.bp index 9c9d0ef380a7..468069dd8334 100644 --- a/packages/overlays/OneHandedModeGesturalOverlay/Android.bp +++ b/packages/overlays/OneHandedModeGesturalOverlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + runtime_resource_overlay { name: "OneHandedModeGesturalOverlay", theme: "OneHandedModeGestural", diff --git a/packages/overlays/tests/Android.bp b/packages/overlays/tests/Android.bp index 728ebe21533f..b781602399a3 100644 --- a/packages/overlays/tests/Android.bp +++ b/packages/overlays/tests/Android.bp @@ -11,6 +11,16 @@ // WITHOUT WARRANTIES 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"], +} + android_test { name: "OverlayTests", certificate: "platform", diff --git a/packages/services/CameraExtensionsProxy/Android.bp b/packages/services/CameraExtensionsProxy/Android.bp index 54b7453da585..ea0703e2bc24 100644 --- a/packages/services/CameraExtensionsProxy/Android.bp +++ b/packages/services/CameraExtensionsProxy/Android.bp @@ -14,10 +14,20 @@ // 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"], +} + android_app { name: "CameraExtensionsProxy", srcs: ["src/**/*.java"], libs: ["androidx.camera.extensions.stub"], + optional_uses_libs: ["androidx.camera.extensions.impl"], platform_apis: true, certificate: "platform", } diff --git a/packages/services/PacProcessor/Android.bp b/packages/services/PacProcessor/Android.bp index 1fd972ce4b3d..dd50e25547c3 100644 --- a/packages/services/PacProcessor/Android.bp +++ b/packages/services/PacProcessor/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_app { name: "PacProcessor", srcs: ["src/**/*.java"], diff --git a/packages/services/Proxy/Android.bp b/packages/services/Proxy/Android.bp index d93c9f8d9941..4d76db725d1f 100644 --- a/packages/services/Proxy/Android.bp +++ b/packages/services/Proxy/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "ProxyHandler", srcs: ["src/**/*.java"], diff --git a/proto/Android.bp b/proto/Android.bp index 86d8ee3a662f..a5e13350ebd2 100644 --- a/proto/Android.bp +++ b/proto/Android.bp @@ -1,3 +1,12 @@ +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_static { name: "framework-protos", host_supported: true, diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk index 5c3f2d83fb00..e41073bcdb76 100644 --- a/rs/jni/Android.mk +++ b/rs/jni/Android.mk @@ -28,6 +28,9 @@ LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code LOCAL_MODULE:= librs_jni +LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS:= notice +LOCAL_NOTICE_FILE:= $(LOCAL_PATH)/../../NOTICE LOCAL_MODULE_TAGS := optional LOCAL_REQUIRED_MODULES := libRS diff --git a/samples/demo/haptic-assessment/Android.bp b/samples/demo/haptic-assessment/Android.bp index 1c006091a755..4d54550f3762 100644 --- a/samples/demo/haptic-assessment/Android.bp +++ b/samples/demo/haptic-assessment/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_app { name: "HapticAssessment", manifest: "AndroidManifest.xml", @@ -31,4 +40,4 @@ android_app { "res", ], dxflags: ["--multi-dex"], -}
\ No newline at end of file +} diff --git a/sax/tests/saxtests/Android.bp b/sax/tests/saxtests/Android.bp index 5889f769a645..cbd19c36dd73 100644 --- a/sax/tests/saxtests/Android.bp +++ b/sax/tests/saxtests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "FrameworksSaxTests", // Include all test java files. diff --git a/services/Android.bp b/services/Android.bp index 315462838485..f6bb157312e0 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -1,3 +1,12 @@ +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: "services-main-sources", srcs: ["java/**/*.java"], @@ -6,7 +15,7 @@ filegroup { } filegroup { - name: "services-all-sources", + name: "services-non-updatable-sources", srcs: [ ":services.core-sources", ":services.core-sources-am-wm", @@ -38,7 +47,15 @@ filegroup { ":services.usb-sources", ":services.voiceinteraction-sources", ":services.wifi-sources", - ":service-media-s-sources", // TODO (b/177640454) + ], + visibility: ["//visibility:private"], +} + +filegroup { + name: "services-all-sources", + srcs: [ + ":services-non-updatable-sources", + ":service-media-s-sources", ":service-permission-sources", ":service-statsd-sources", ], @@ -125,9 +142,8 @@ filegroup { // API stub // ============================================================= -droidstubs { - name: "services-stubs.sources", - srcs: [":services-all-sources"], +stubs_defaults { + name: "services-stubs-default", installable: false, args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)" + " --hide-annotation android.annotation.Hide" + @@ -137,7 +153,13 @@ droidstubs { " --hide DeprecationMismatch" + " --hide HiddenTypedefConstant", visibility: ["//visibility:private"], - filter_packages: ["com.android."], + filter_packages: ["com.android."] +} + +droidstubs { + name: "services-stubs.sources", + srcs: [":services-all-sources"], + defaults: ["services-stubs-default"], check_api: { current: { api_file: "api/current.txt", @@ -183,3 +205,34 @@ java_library { dir: "apistubs/android/system-server", }, } + +droidstubs { + name: "services-non-updatable-stubs.sources", + srcs: [":services-non-updatable-sources"], + defaults: ["services-stubs-default"], + check_api: { + current: { + api_file: "api/non-updatable-current.txt", + removed_api_file: "api/non-updatable-removed.txt", + }, + api_lint: { + enabled: true, + new_since: ":android-non-updatable.api.system-server.latest", + baseline_file: "api/non-updatable-lint-baseline.txt", + }, + }, + dists: [ + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system-server/api", + dest: "android-non-updatable.txt", + tag: ".api.txt" + }, + { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system-server/api", + dest: "android-non-updatable-removed.txt", + tag: ".removed-api.txt", + }, + ] +}
\ No newline at end of file diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp index 65313fcfc426..1698e9a70245 100644 --- a/services/accessibility/Android.bp +++ b/services/accessibility/Android.bp @@ -1,3 +1,12 @@ +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: "services.accessibility-sources", srcs: ["java/**/*.java"], diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index ea1473ea3db7..c63c2e1a257d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -777,6 +777,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // performs the current profile parent resolution. final int resolvedUserId = mSecurityPolicy .resolveCallingUserIdEnforcingPermissionsLocked(userId); + + if (Binder.getCallingPid() == OWN_PROCESS_ID) { + return new ArrayList<>(getUserStateLocked(resolvedUserId).mInstalledServices); + } return getUserStateLocked(resolvedUserId).mInstalledServices; } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index a9e8ea03eab8..675626841d17 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -376,7 +376,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect if (mSecurityPolicy.canPerformGestures(this)) { MotionEventInjector motionEventInjector = mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId); - if (mWindowManagerService.isTouchOrFaketouchDevice()) { + if (motionEventInjector != null + && mWindowManagerService.isTouchOrFaketouchDevice()) { motionEventInjector.injectEvents( gestureSteps.getList(), mServiceInterface, sequence, displayId); } else { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index 22efd3718d7a..eb30fdeddd90 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -118,6 +118,9 @@ class AccessibilityUserState { private int mNonInteractiveUiTimeout = 0; private int mInteractiveUiTimeout = 0; private int mLastSentClientState = -1; + + /** {@code true} if the device config supports magnification area. */ + private final boolean mSupportMagnificationArea; // The magnification mode of default display. private int mMagnificationMode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; // The magnification capabilities used to know magnification mode could be switched. @@ -138,6 +141,10 @@ class AccessibilityUserState { private int mSoftKeyboardShowMode = SHOW_MODE_AUTO; boolean isValidMagnificationModeLocked() { + if (!mSupportMagnificationArea + && mMagnificationMode == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) { + return false; + } return (mMagnificationCapabilities & mMagnificationMode) != 0; } @@ -156,6 +163,8 @@ class AccessibilityUserState { R.color.accessibility_focus_highlight_color); mFocusStrokeWidth = mFocusStrokeWidthDefaultValue; mFocusColor = mFocusColorDefaultValue; + mSupportMagnificationArea = mContext.getResources().getBoolean( + R.bool.config_magnification_area); } boolean isHandlingAccessibilityEventsLocked() { diff --git a/services/api/Android.bp b/services/api/Android.bp new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/services/api/Android.bp diff --git a/services/api/non-updatable-current.txt b/services/api/non-updatable-current.txt new file mode 100644 index 000000000000..3c72d38927bc --- /dev/null +++ b/services/api/non-updatable-current.txt @@ -0,0 +1,55 @@ +// Signature format: 2.0 +package com.android.server { + + public final class LocalManagerRegistry { + method public static <T> void addManager(@NonNull Class<T>, @NonNull T); + method @Nullable public static <T> T getManager(@NonNull Class<T>); + } + + public abstract class SystemService { + ctor public SystemService(@NonNull android.content.Context); + method @NonNull public final android.content.Context getContext(); + method public boolean isUserSupported(@NonNull com.android.server.SystemService.TargetUser); + method public void onBootPhase(int); + method public abstract void onStart(); + method public void onUserStarting(@NonNull com.android.server.SystemService.TargetUser); + method public void onUserStopped(@NonNull com.android.server.SystemService.TargetUser); + method public void onUserStopping(@NonNull com.android.server.SystemService.TargetUser); + method public void onUserSwitching(@Nullable com.android.server.SystemService.TargetUser, @NonNull com.android.server.SystemService.TargetUser); + method public void onUserUnlocked(@NonNull com.android.server.SystemService.TargetUser); + method public void onUserUnlocking(@NonNull com.android.server.SystemService.TargetUser); + method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder); + method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder, boolean); + field public static final int PHASE_ACTIVITY_MANAGER_READY = 550; // 0x226 + field public static final int PHASE_BOOT_COMPLETED = 1000; // 0x3e8 + field public static final int PHASE_DEVICE_SPECIFIC_SERVICES_READY = 520; // 0x208 + field public static final int PHASE_LOCK_SETTINGS_READY = 480; // 0x1e0 + field public static final int PHASE_SYSTEM_SERVICES_READY = 500; // 0x1f4 + field public static final int PHASE_THIRD_PARTY_APPS_CAN_START = 600; // 0x258 + field public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100; // 0x64 + } + + public static final class SystemService.TargetUser { + method @NonNull public android.os.UserHandle getUserHandle(); + } + +} + +package com.android.server.role { + + public interface RoleServicePlatformHelper { + method @NonNull public String computePackageStateHash(int); + method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getLegacyRoleState(int); + } + +} + +package com.android.server.wifi { + + public class SupplicantManager { + method public static void start(); + method public static void stop(); + } + +} + diff --git a/services/api/non-updatable-lint-baseline.txt b/services/api/non-updatable-lint-baseline.txt new file mode 100644 index 000000000000..b46d21edd44c --- /dev/null +++ b/services/api/non-updatable-lint-baseline.txt @@ -0,0 +1,9 @@ +// Baseline format: 1.0 +NotCloseable: com.android.server.wifi.SupplicantManager: + Classes that release resources (stop()) should implement AutoClosable and CloseGuard: class com.android.server.wifi.SupplicantManager + + +ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder): + Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder)} +ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder, boolean): + Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder,boolean)} diff --git a/services/api/non-updatable-removed.txt b/services/api/non-updatable-removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/services/api/non-updatable-removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/services/appprediction/Android.bp b/services/appprediction/Android.bp index bc43db1fa116..53c461b23651 100644 --- a/services/appprediction/Android.bp +++ b/services/appprediction/Android.bp @@ -1,3 +1,12 @@ +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: "services.appprediction-sources", srcs: ["java/**/*.java"], diff --git a/services/appwidget/Android.bp b/services/appwidget/Android.bp index e46e5c82a917..8119073fdf7f 100644 --- a/services/appwidget/Android.bp +++ b/services/appwidget/Android.bp @@ -1,3 +1,12 @@ +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: "services.appwidget-sources", srcs: ["java/**/*.java"], diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 809304bb24ae..50ad6617b1fe 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -137,7 +137,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -181,9 +180,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } switch (action) { - case Intent.ACTION_CONFIGURATION_CHANGED: - onConfigurationChanged(); - break; case Intent.ACTION_MANAGED_PROFILE_AVAILABLE: case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE: synchronized (mLock) { @@ -243,8 +239,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private Handler mSaveStateHandler; private Handler mCallbackHandler; - private Locale mLocale; - private final SparseIntArray mNextAppWidgetIds = new SparseIntArray(); private boolean mSafeMode; @@ -290,13 +284,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } private void registerBroadcastReceiver() { - // Register for configuration changes so we can update the names - // of the widgets when the locale changes. - IntentFilter configFilter = new IntentFilter(); - configFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, - configFilter, null, null); - // Register for broadcasts about package install, etc., so we can // update the provider list. IntentFilter packageFilter = new IntentFilter(); @@ -338,62 +325,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku mSafeMode = safeMode; } - private void onConfigurationChanged() { - if (DEBUG) { - Slog.i(TAG, "onConfigurationChanged()"); - } - - Locale revised = Locale.getDefault(); - if (revised == null || mLocale == null || !revised.equals(mLocale)) { - mLocale = revised; - - synchronized (mLock) { - SparseIntArray changedGroups = null; - - // Note: updateProvidersForPackageLocked() may remove providers, so we must copy the - // list of installed providers and skip providers that we don't need to update. - // Also note that remove the provider does not clear the Provider component data. - ArrayList<Provider> installedProviders = new ArrayList<>(mProviders); - HashSet<ProviderId> removedProviders = new HashSet<>(); - - int N = installedProviders.size(); - for (int i = N - 1; i >= 0; i--) { - Provider provider = installedProviders.get(i); - - final int userId = provider.getUserId(); - if (!mUserManager.isUserUnlockingOrUnlocked(userId) || - isProfileWithLockedParent(userId)) { - continue; - } - ensureGroupStateLoadedLocked(userId); - - if (!removedProviders.contains(provider.id)) { - final boolean changed = updateProvidersForPackageLocked( - provider.id.componentName.getPackageName(), - provider.getUserId(), removedProviders); - - if (changed) { - if (changedGroups == null) { - changedGroups = new SparseIntArray(); - } - final int groupId = mSecurityPolicy.getGroupParent( - provider.getUserId()); - changedGroups.put(groupId, groupId); - } - } - } - - if (changedGroups != null) { - final int groupCount = changedGroups.size(); - for (int i = 0; i < groupCount; i++) { - final int groupId = changedGroups.get(i); - saveGroupStateAsync(groupId); - } - } - } - } - } - private void onPackageBroadcastReceived(Intent intent, int userId) { final String action = intent.getAction(); boolean added = false; @@ -2673,7 +2604,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN); info.widgetFeatures = sa.getInt( com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures, 0); - info.descriptionResource = sa.getResourceId( + info.descriptionRes = sa.getResourceId( com.android.internal.R.styleable.AppWidgetProviderInfo_description, Resources.ID_NULL); sa.recycle(); diff --git a/services/autofill/Android.bp b/services/autofill/Android.bp index c44806642427..eb23f2f9b435 100644 --- a/services/autofill/Android.bp +++ b/services/autofill/Android.bp @@ -1,3 +1,12 @@ +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: "services.autofill-sources", srcs: ["java/**/*.java"], diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 890208720f97..27ea3d618afa 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -203,7 +203,7 @@ final class FillUi { .getInteger(com.android.internal.R.integer.autofill_max_visible_datasets); } - final RemoteViews.OnClickHandler interceptionHandler = (view, pendingIntent, r) -> { + final RemoteViews.InteractionHandler interceptionHandler = (view, pendingIntent, r) -> { if (pendingIntent != null) { mCallback.startIntentSender(pendingIntent.getIntentSender()); } @@ -258,10 +258,11 @@ final class FillUi { + mVisibleDatasetsMaxCount); } - RemoteViews.OnClickHandler clickBlocker = null; + RemoteViews.InteractionHandler interactionBlocker = null; if (headerPresentation != null) { - clickBlocker = newClickBlocker(); - mHeader = headerPresentation.applyWithTheme(mContext, null, clickBlocker, mThemeId); + interactionBlocker = newInteractionBlocker(); + mHeader = headerPresentation.applyWithTheme( + mContext, null, interactionBlocker, mThemeId); final LinearLayout headerContainer = decor.findViewById(R.id.autofill_dataset_header); applyCancelAction(mHeader, response.getCancelIds()); @@ -276,11 +277,11 @@ final class FillUi { final LinearLayout footerContainer = decor.findViewById(R.id.autofill_dataset_footer); if (footerContainer != null) { - if (clickBlocker == null) { // already set for header - clickBlocker = newClickBlocker(); + if (interactionBlocker == null) { // already set for header + interactionBlocker = newInteractionBlocker(); } mFooter = footerPresentation.applyWithTheme( - mContext, null, clickBlocker, mThemeId); + mContext, null, interactionBlocker, mThemeId); applyCancelAction(mFooter, response.getCancelIds()); // Footer not supported on some platform e.g. TV if (sVerbose) Slog.v(TAG, "adding footer"); @@ -397,9 +398,9 @@ final class FillUi { } /** - * Creates a remoteview interceptor used to block clicks. + * Creates a remoteview interceptor used to block clicks or other interactions. */ - private RemoteViews.OnClickHandler newClickBlocker() { + private RemoteViews.InteractionHandler newInteractionBlocker() { return (view, pendingIntent, response) -> { if (sVerbose) Slog.v(TAG, "Ignoring click on " + view); return true; diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 9d46c26022bf..826a98afe2f8 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -388,18 +388,20 @@ final class SaveUi { } } - final RemoteViews.OnClickHandler handler = (view, pendingIntent, response) -> { - Intent intent = response.getLaunchOptions(view).first; - final boolean isValid = isValidLink(pendingIntent, intent); - if (!isValid) { - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_SAVE_LINK_TAPPED, mType); - log.setType(MetricsEvent.TYPE_UNKNOWN); - mMetricsLogger.write(log); - return false; - } + final RemoteViews.InteractionHandler handler = + (view, pendingIntent, response) -> { + Intent intent = response.getLaunchOptions(view).first; + final boolean isValid = isValidLink(pendingIntent, intent); + if (!isValid) { + final LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_SAVE_LINK_TAPPED, mType); + log.setType(MetricsEvent.TYPE_UNKNOWN); + mMetricsLogger.write(log); + return false; + } - startIntentSenderWithRestore(pendingIntent, intent); - return true; + startIntentSenderWithRestore(pendingIntent, intent); + return true; }; try { diff --git a/services/backup/Android.bp b/services/backup/Android.bp index 68376c5ad178..ead8aff30147 100644 --- a/services/backup/Android.bp +++ b/services/backup/Android.bp @@ -1,3 +1,12 @@ +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: "services.backup-sources", srcs: ["java/**/*.java"], diff --git a/services/backup/backuplib/Android.bp b/services/backup/backuplib/Android.bp index 00f51c960636..5a28891f88c9 100644 --- a/services/backup/backuplib/Android.bp +++ b/services/backup/backuplib/Android.bp @@ -1,3 +1,12 @@ +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: "backuplib-sources", srcs: ["java/**/*.java"], diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 6c30999f63a4..38275f7cd348 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -1245,10 +1245,9 @@ public class BackupManagerService extends IBackupManager.Stub { @Override public IRestoreSession beginRestoreSessionForUser( - int userId, String packageName, String transportID, - @OperationType int operationType) throws RemoteException { + int userId, String packageName, String transportID) throws RemoteException { return isUserReadyForBackup(userId) - ? beginRestoreSession(userId, packageName, transportID, operationType) : null; + ? beginRestoreSession(userId, packageName, transportID) : null; } /** @@ -1257,15 +1256,13 @@ public class BackupManagerService extends IBackupManager.Stub { */ @Nullable public IRestoreSession beginRestoreSession( - @UserIdInt int userId, String packageName, String transportName, - @OperationType int operationType) { + @UserIdInt int userId, String packageName, String transportName) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()"); return userBackupManagerService == null ? null - : userBackupManagerService.beginRestoreSession(packageName, transportName, - operationType); + : userBackupManagerService.beginRestoreSession(packageName, transportName); } @Override @@ -1350,15 +1347,15 @@ public class BackupManagerService extends IBackupManager.Stub { if (!isUserReadyForBackup(userId)) { return BackupManager.ERROR_BACKUP_NOT_ALLOWED; } - return requestBackup(userId, packages, observer, monitor, flags, OperationType.BACKUP); + return requestBackup(userId, packages, observer, monitor, flags); } @Override public int requestBackup(String[] packages, IBackupObserver observer, - IBackupManagerMonitor monitor, int flags, @OperationType int operationType) + IBackupManagerMonitor monitor, int flags) throws RemoteException { return requestBackup(binderGetCallingUserId(), packages, - observer, monitor, flags, operationType); + observer, monitor, flags); } /** @@ -1370,15 +1367,13 @@ public class BackupManagerService extends IBackupManager.Stub { String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, - int flags, - @OperationType int operationType) { + int flags) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "requestBackup()"); return userBackupManagerService == null ? BackupManager.ERROR_BACKUP_NOT_ALLOWED - : userBackupManagerService.requestBackup(packages, observer, monitor, flags, - operationType); + : userBackupManagerService.requestBackup(packages, observer, monitor, flags); } @Override diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 136cd22fad83..9ee0159e903a 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -96,6 +96,7 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.AtomicFile; import android.util.EventLog; +import android.util.FeatureFlagUtils; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -127,6 +128,7 @@ import com.android.server.backup.params.RestoreParams; import com.android.server.backup.restore.ActiveRestoreSession; import com.android.server.backup.restore.PerformUnifiedRestoreTask; import com.android.server.backup.transport.TransportClient; +import com.android.server.backup.transport.TransportNotAvailableException; import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.backup.utils.BackupEligibilityRules; import com.android.server.backup.utils.BackupManagerMonitorUtils; @@ -1860,19 +1862,10 @@ public class UserBackupManagerService { /** * Requests a backup for the inputted {@code packages} with a specified {@link - * IBackupManagerMonitor}. - */ - public int requestBackup(String[] packages, IBackupObserver observer, - IBackupManagerMonitor monitor, int flags) { - return requestBackup(packages, observer, monitor, flags, OperationType.BACKUP); - } - - /** - * Requests a backup for the inputted {@code packages} with a specified {@link * IBackupManagerMonitor} and {@link OperationType}. */ public int requestBackup(String[] packages, IBackupObserver observer, - IBackupManagerMonitor monitor, int flags, @OperationType int operationType) { + IBackupManagerMonitor monitor, int flags) { mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup"); if (packages == null || packages.length < 1) { @@ -1903,13 +1896,16 @@ public class UserBackupManagerService { final TransportClient transportClient; final String transportDirName; + int operationType; try { transportDirName = mTransportManager.getTransportDirName( mTransportManager.getCurrentTransportName()); transportClient = mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()"); - } catch (TransportNotRegisteredException e) { + operationType = getOperationTypeFromTransport(transportClient); + } catch (TransportNotRegisteredException | TransportNotAvailableException + | RemoteException e) { BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED); monitor = BackupManagerMonitorUtils.monitorEvent(monitor, BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL, @@ -4024,15 +4020,13 @@ public class UserBackupManagerService { } /** Hand off a restore session. */ - public IRestoreSession beginRestoreSession(String packageName, String transport, - @OperationType int operationType) { + public IRestoreSession beginRestoreSession(String packageName, String transport) { if (DEBUG) { Slog.v( TAG, addUserIdToLogMessage( mUserId, - "beginRestoreSession: pkg=" + packageName + " transport=" + transport - + "operationType=" + operationType)); + "beginRestoreSession: pkg=" + packageName + " transport=" + transport)); } boolean needPermission = true; @@ -4073,6 +4067,17 @@ public class UserBackupManagerService { } } + int operationType; + try { + operationType = getOperationTypeFromTransport( + mTransportManager.getTransportClientOrThrow(transport, /* caller */ + "BMS.beginRestoreSession")); + } catch (TransportNotAvailableException | TransportNotRegisteredException + | RemoteException e) { + Slog.w(TAG, "Failed to get operation type from transport: " + e); + return null; + } + synchronized (this) { if (mActiveRestoreSession != null) { Slog.i( @@ -4356,6 +4361,34 @@ public class UserBackupManagerService { } } + @VisibleForTesting + @OperationType int getOperationTypeFromTransport(TransportClient transportClient) + throws TransportNotAvailableException, RemoteException { + if (!shouldUseNewBackupEligibilityRules()) { + // Return the default to stick to the legacy behaviour. + return OperationType.BACKUP; + } + + long oldCallingId = Binder.clearCallingIdentity(); + try { + IBackupTransport transport = transportClient.connectOrThrow( + /* caller */ "BMS.getOperationTypeFromTransport"); + if ((transport.getTransportFlags() & BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER) != 0) { + return OperationType.MIGRATION; + } else { + return OperationType.BACKUP; + } + } finally { + Binder.restoreCallingIdentity(oldCallingId); + } + } + + @VisibleForTesting + boolean shouldUseNewBackupEligibilityRules() { + return FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES); + } + private static String addUserIdToLogMessage(int userId, String message) { return "[UserID:" + userId + "] " + message; } diff --git a/services/companion/Android.bp b/services/companion/Android.bp index 6aa54c48e3ed..4ae9365d3b9f 100644 --- a/services/companion/Android.bp +++ b/services/companion/Android.bp @@ -1,3 +1,12 @@ +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: "services.companion-sources", srcs: ["java/**/*.java"], diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 76c8d3001158..21cae453d702 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -142,18 +142,13 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Function; -//TODO onStop schedule unbind in 5 seconds -//TODO make sure APIs are only callable from currently focused app -//TODO schedule stopScan on activity destroy(except if configuration change) -//TODO on associate called again after configuration change -> replace old callback with new -//TODO avoid leaking calling activity in IFindDeviceCallback (see PrintManager#print for example) /** @hide */ @SuppressLint("LongLogTag") public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient { private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative( CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, - ".DeviceDiscoveryService"); + ".CompanionDeviceDiscoveryService"); private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000; private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000; @@ -747,6 +742,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } } + + if (association.isNotifyOnDeviceNearby()) { + ServiceConnector<ICompanionDeviceService> serviceConnector = + mDeviceListenerServiceConnectors.forUser(association.getUserId()) + .get(association.getPackageName()); + if (serviceConnector != null) { + serviceConnector.unbind(); + } + } } private void updateSpecialAccessPermissionForAssociatedPackage(Association association) { @@ -1422,7 +1426,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } @Override - public void onDeviceDisconnected(BluetoothDevice device) { + public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) { + Slog.d(LOG_TAG, device.getAddress() + " disconnected w/ reason: (" + reason + ") " + + BluetoothAdapter.BluetoothConnectionCallback.disconnectReasonText(reason)); CompanionDeviceManagerService.this.onDeviceDisconnected(device.getAddress()); } } diff --git a/services/contentcapture/Android.bp b/services/contentcapture/Android.bp index 688c0b1d3eec..434f239dbddc 100644 --- a/services/contentcapture/Android.bp +++ b/services/contentcapture/Android.bp @@ -1,3 +1,12 @@ +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: "services.contentcapture-sources", srcs: ["java/**/*.java"], diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 0be485b747e5..220f87db8bd4 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -795,6 +795,23 @@ public final class ContentCaptureManagerService extends new ContentCaptureManagerServiceShellCommand(ContentCaptureManagerService.this).exec( this, in, out, err, args, callback, resultReceiver); } + + @Override + public void resetTemporaryService(@UserIdInt int userId) { + ContentCaptureManagerService.this.resetTemporaryService(userId); + } + + @Override + public void setTemporaryService( + @UserIdInt int userId, @NonNull String serviceName, int duration) { + ContentCaptureManagerService.this.setTemporaryService( + userId, serviceName, duration); + } + + @Override + public void setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) { + ContentCaptureManagerService.this.setDefaultServiceEnabled(userId, enabled); + } } private final class LocalService extends ContentCaptureManagerInternal { diff --git a/services/contentsuggestions/Android.bp b/services/contentsuggestions/Android.bp index 1b4d7e29c551..9f28b724e408 100644 --- a/services/contentsuggestions/Android.bp +++ b/services/contentsuggestions/Android.bp @@ -1,3 +1,12 @@ +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: "services.contentsuggestions-sources", srcs: ["java/**/*.java"], diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java index 00c91fee845e..57c643bb08a1 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java @@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.contentsuggestions.ClassificationsRequest; import android.app.contentsuggestions.ContentSuggestionsManager; import android.app.contentsuggestions.IClassificationsCallback; @@ -253,6 +254,23 @@ public class ContentSuggestionsManagerService extends receiver.send(isDisabled ? 0 : 1, null); } + @Override + public void resetTemporaryService(@UserIdInt int userId) { + ContentSuggestionsManagerService.this.resetTemporaryService(userId); + } + + @Override + public void setTemporaryService( + @UserIdInt int userId, @NonNull String serviceName, int duration) { + ContentSuggestionsManagerService.this.setTemporaryService( + userId, serviceName, duration); + } + + @Override + public void setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) { + ContentSuggestionsManagerService.this.setDefaultServiceEnabled(userId, enabled); + } + public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback, diff --git a/services/core/Android.bp b/services/core/Android.bp index 0725bb277e51..b67bdc20f7fa 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -1,3 +1,12 @@ +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: "services.core-sources-am-wm", srcs: [ diff --git a/services/core/java/android/power/PowerStatsInternal.java b/services/core/java/android/power/PowerStatsInternal.java index 40107a5d1578..27da14230110 100644 --- a/services/core/java/android/power/PowerStatsInternal.java +++ b/services/core/java/android/power/PowerStatsInternal.java @@ -50,7 +50,8 @@ public abstract class PowerStatsInternal { * requested. * * @return A Future containing a list of {@link EnergyConsumerResult} objects containing energy - * consumer results for all listed {@link EnergyConsumerId}. + * consumer results for all listed {@link EnergyConsumerId}. null if + * {@link EnergyConsumer} not supported */ @NonNull public abstract CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync( @@ -59,8 +60,10 @@ public abstract class PowerStatsInternal { /** * Returns the power entity info for all available {@link PowerEntity} * - * @return List of available {@link PowerEntity} + * @return List of available {@link PowerEntity} or null if {@link PowerEntity} not + * supported */ + @Nullable public abstract PowerEntity[] getPowerEntityInfo(); /** @@ -71,7 +74,8 @@ public abstract class PowerStatsInternal { * requested. * * @return A Future containing a list of {@link StateResidencyResult} objects containing state - * residency results for all listed {@link PowerEntity.id}. + * residency results for all listed {@link PowerEntity.id}. null if {@link PowerEntity} + * not supported */ @NonNull public abstract CompletableFuture<StateResidencyResult[]> getStateResidencyAsync( @@ -80,8 +84,9 @@ public abstract class PowerStatsInternal { /** * Returns the channel info for all available {@link Channel} * - * @return List of available {@link Channel} + * @return List of available {@link Channel} or null if {@link Channel} not supported */ + @Nullable public abstract Channel[] getEnergyMeterInfo(); /** @@ -91,7 +96,8 @@ public abstract class PowerStatsInternal { * @param channelIds Array of {@link Channel.id} for accumulated energy is being requested. * * @return A Future containing a list of {@link EnergyMeasurement} objects containing - * accumulated energy measurements for all listed {@link Channel.id}. + * accumulated energy measurements for all listed {@link Channel.id}. null if + * {@link Channel} not supported */ @NonNull public abstract CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync( diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 154e1831ceee..542d527177a1 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -44,6 +44,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE; @@ -90,7 +91,7 @@ import android.net.IConnectivityDiagnosticsCallback; import android.net.IConnectivityManager; import android.net.IDnsResolver; import android.net.INetd; -import android.net.INetworkManagementEventObserver; +import android.net.INetworkActivityListener; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; import android.net.INetworkPolicyListener; @@ -147,13 +148,13 @@ import android.net.netlink.InetDiagMessage; import android.net.shared.PrivateDnsConfig; import android.net.util.MultinetworkPolicyTracker; import android.net.util.NetdService; +import android.os.BatteryStatsManager; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; -import android.os.INetworkActivityListener; import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; @@ -163,6 +164,7 @@ import android.os.Parcelable; import android.os.PersistableBundle; import android.os.PowerManager; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.SystemClock; @@ -172,6 +174,7 @@ import android.os.UserManager; import android.provider.Settings; import android.telephony.TelephonyManager; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.LocalLog; import android.util.Log; @@ -185,14 +188,17 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.logging.MetricsLogger; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.AsyncChannel; +import com.android.internal.util.BitUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.LocationPermissionChecker; import com.android.internal.util.MessageUtils; import com.android.modules.utils.BasicShellCommandHandler; +import com.android.net.module.util.BaseNetdUnsolicitedEventListener; +import com.android.net.module.util.CollectionUtils; import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult; import com.android.net.module.util.LinkPropertiesUtils.CompareResult; +import com.android.net.module.util.PermissionUtils; import com.android.server.am.BatteryStatsService; import com.android.server.connectivity.AutodestructReference; import com.android.server.connectivity.DataConnectionStats; @@ -209,7 +215,6 @@ import com.android.server.connectivity.NetworkRanker; import com.android.server.connectivity.PermissionMonitor; import com.android.server.connectivity.ProxyTracker; import com.android.server.connectivity.QosCallbackTracker; -import com.android.server.net.BaseNetworkObserver; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.utils.PriorityDump; @@ -327,6 +332,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private INetworkStatsService mStatsService; private NetworkPolicyManager mPolicyManager; private NetworkPolicyManagerInternal mPolicyManagerInternal; + private final NetdCallback mNetdCallback; /** * TestNetworkService (lazily) created upon first usage. Locked to prevent creation of multiple @@ -559,6 +565,11 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_SET_OEM_NETWORK_PREFERENCE = 48; /** + * Used to indicate the system default network becomes active. + */ + private static final int EVENT_REPORT_NETWORK_ACTIVITY = 49; + + /** * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification * should be shown. */ @@ -1192,7 +1203,14 @@ public class ConnectivityService extends IConnectivityManager.Stub mUserAllContext.registerReceiver(mIntentReceiver, intentFilter, null /* broadcastPermission */, mHandler); - mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mNMS); + mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mHandler, mNMS, mNetd); + + mNetdCallback = new NetdCallback(); + try { + mNetd.registerUnsolicitedEventListener(mNetdCallback); + } catch (RemoteException | ServiceSpecificException e) { + loge("Error registering event listener :" + e); + } mSettingsObserver = new SettingsObserver(mContext, mHandler); registerSettingsCallbacks(); @@ -1231,6 +1249,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) { final NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); netCap.removeCapability(NET_CAPABILITY_NOT_VPN); netCap.setSingleUid(uid); return netCap; @@ -1245,6 +1264,7 @@ public class ConnectivityService extends IConnectivityManager.Stub int transportType, NetworkRequest.Type type) { final NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName()); if (transportType > TYPE_NONE) { netCap.addTransportType(transportType); @@ -1502,7 +1522,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public Network getActiveNetworkForUid(int uid, boolean ignoreBlocked) { - NetworkStack.checkNetworkStackPermission(mContext); + PermissionUtils.enforceNetworkStackPermission(mContext); return getActiveNetworkForUidInternal(uid, ignoreBlocked); } @@ -1525,7 +1545,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) { - NetworkStack.checkNetworkStackPermission(mContext); + PermissionUtils.enforceNetworkStackPermission(mContext); final NetworkState state = getUnfilteredActiveNetworkState(uid); filterNetworkStateForUid(state, uid, ignoreBlocked); return state.networkInfo; @@ -1869,7 +1889,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkState[] getAllNetworkState() { // This contains IMSI details, so make sure the caller is privileged. - NetworkStack.checkNetworkStackPermission(mContext); + PermissionUtils.enforceNetworkStackPermission(mContext); final ArrayList<NetworkState> result = new ArrayList<>(); for (Network network : getAllNetworks()) { @@ -2293,7 +2313,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Public because it's used by mLockdownTracker. public void sendConnectedBroadcast(NetworkInfo info) { - NetworkStack.checkNetworkStackPermission(mContext); + PermissionUtils.enforceNetworkStackPermission(mContext); sendGeneralBroadcast(info, CONNECTIVITY_ACTION); } @@ -2404,7 +2424,7 @@ public class ConnectivityService extends IConnectivityManager.Stub */ @Override public void registerNetworkActivityListener(@NonNull INetworkActivityListener l) { - // TODO: Replace network activity listener registry in ConnectivityManager from NMS to here + mNetworkActivityTracker.registerNetworkActivityListener(l); } /** @@ -2412,7 +2432,7 @@ public class ConnectivityService extends IConnectivityManager.Stub */ @Override public void unregisterNetworkActivityListener(@NonNull INetworkActivityListener l) { - // TODO: Replace network activity listener registry in ConnectivityManager from NMS to here + mNetworkActivityTracker.unregisterNetworkActivityListener(l); } /** @@ -2420,8 +2440,7 @@ public class ConnectivityService extends IConnectivityManager.Stub */ @Override public boolean isDefaultNetworkActive() { - // TODO: Replace isNetworkActive() in NMS. - return false; + return mNetworkActivityTracker.isDefaultNetworkActive(); } /** @@ -2558,13 +2577,13 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!checkDumpPermission(mContext, TAG, pw)) return; if (asProto) return; - if (ArrayUtils.contains(args, DIAG_ARG)) { + if (CollectionUtils.contains(args, DIAG_ARG)) { dumpNetworkDiagnostics(pw); return; - } else if (ArrayUtils.contains(args, NETWORK_ARG)) { + } else if (CollectionUtils.contains(args, NETWORK_ARG)) { dumpNetworks(pw); return; - } else if (ArrayUtils.contains(args, REQUEST_ARG)) { + } else if (CollectionUtils.contains(args, REQUEST_ARG)) { dumpNetworkRequests(pw); return; } @@ -2635,7 +2654,7 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.println(); - if (ArrayUtils.contains(args, SHORT_ARG) == false) { + if (!CollectionUtils.contains(args, SHORT_ARG)) { pw.println(); pw.println("mNetworkRequestInfoLogs (most recent first):"); pw.increaseIndent(); @@ -2686,6 +2705,12 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.increaseIndent(); mPermissionMonitor.dump(pw); pw.decreaseIndent(); + + pw.println(); + pw.println("Legacy network activity:"); + pw.increaseIndent(); + mNetworkActivityTracker.dump(pw); + pw.decreaseIndent(); } private void dumpNetworks(IndentingPrintWriter pw) { @@ -3721,7 +3746,12 @@ public class ConnectivityService extends IConnectivityManager.Stub // Looking up the app passed param request in mRequests isn't possible since it may return // null for a request managed by a per-app default. Therefore use getNriForAppRequest() to // do the lookup since that will also find per-app default managed requests. - final NetworkRequestInfo nri = getNriForAppRequest(request); + // Additionally, this lookup needs to be relatively fast (hence the lookup optimization) + // to avoid potential race conditions when validating a package->uid mapping when sending + // the callback on the very low-chance that an application shuts down prior to the callback + // being sent. + final NetworkRequestInfo nri = mNetworkRequests.get(request) != null + ? mNetworkRequests.get(request) : getNriForAppRequest(request); if (nri != null) { if (Process.SYSTEM_UID != callingUid && Process.NETWORK_STACK_UID != callingUid @@ -4447,6 +4477,9 @@ public class ConnectivityService extends IConnectivityManager.Stub loge("handleMessage.EVENT_SET_OEM_NETWORK_PREFERENCE failed", e); } break; + case EVENT_REPORT_NETWORK_ACTIVITY: + mNetworkActivityTracker.handleReportNetworkActivity(); + break; } } } @@ -4663,7 +4696,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void setGlobalProxy(final ProxyInfo proxyProperties) { - NetworkStack.checkNetworkStackPermission(mContext); + PermissionUtils.enforceNetworkStackPermission(mContext); mProxyTracker.setGlobalProxy(proxyProperties); } @@ -4788,7 +4821,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - if (ArrayUtils.isEmpty(underlyingNetworks)) return null; + if (CollectionUtils.isEmpty(underlyingNetworks)) return null; List<String> interfaces = new ArrayList<>(); for (Network network : underlyingNetworks) { @@ -4832,7 +4865,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!nai.supportsUnderlyingNetworks()) return false; final Network[] underlying = underlyingNetworksOrDefault( nai.networkCapabilities.getOwnerUid(), nai.declaredUnderlyingNetworks); - return ArrayUtils.contains(underlying, network); + return CollectionUtils.contains(underlying, network); } /** @@ -4865,7 +4898,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void setRequireVpnForUids(boolean requireVpn, UidRange[] ranges) { - NetworkStack.checkNetworkStackPermission(mContext); + PermissionUtils.enforceNetworkStackPermission(mContext); mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_REQUIRE_VPN_FOR_UIDS, encodeBool(requireVpn), 0 /* arg2 */, ranges)); } @@ -5296,8 +5329,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } } - // TODO: use NetworkStackUtils.convertToIntArray after moving it - return ArrayUtils.convertToIntArray(new ArrayList<>(thresholds)); + return CollectionUtils.toIntArray(new ArrayList<>(thresholds)); } private void updateSignalStrengthThresholds( @@ -6416,7 +6448,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) { underlyingNetworks = underlyingNetworksOrDefault( agentCaps.getOwnerUid(), underlyingNetworks); - int[] transportTypes = agentCaps.getTransportTypes(); + long transportTypes = BitUtils.packBits(agentCaps.getTransportTypes()); int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; // metered if any underlying is metered, or originally declared metered by the agent. @@ -6435,7 +6467,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkCapabilities underlyingCaps = underlying.networkCapabilities; hadUnderlyingNetworks = true; for (int underlyingType : underlyingCaps.getTransportTypes()) { - transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType); + transportTypes |= 1L << underlyingType; } // Merge capabilities of this underlying network. For bandwidth, assume the @@ -6466,7 +6498,7 @@ public class ConnectivityService extends IConnectivityManager.Stub suspended = false; } - newNc.setTransportTypes(transportTypes); + newNc.setTransportTypes(BitUtils.unpackBits(transportTypes)); newNc.setLinkDownstreamBandwidthKbps(downKbps); newNc.setLinkUpstreamBandwidthKbps(upKbps); newNc.setCapability(NET_CAPABILITY_NOT_METERED, !metered); @@ -8531,14 +8563,14 @@ public class ConnectivityService extends IConnectivityManager.Stub for (NetworkAgentInfo virtual : mNetworkAgentInfos) { if (virtual.supportsUnderlyingNetworks() && virtual.networkCapabilities.getOwnerUid() == callbackUid - && ArrayUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) { + && CollectionUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) { return true; } } // Administrator UIDs also contains the Owner UID final int[] administratorUids = nai.networkCapabilities.getAdministratorUids(); - return ArrayUtils.contains(administratorUids, callbackUid); + return CollectionUtils.contains(administratorUids, callbackUid); } @Override @@ -8627,6 +8659,14 @@ public class ConnectivityService extends IConnectivityManager.Stub notifyDataStallSuspected(p, network.getNetId()); } + private class NetdCallback extends BaseNetdUnsolicitedEventListener { + @Override + public void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, + long timestampNs, int uid) { + mNetworkActivityTracker.setAndReportNetworkActive(isActive, timerLabel, timestampNs); + } + } + private final LegacyNetworkActivityTracker mNetworkActivityTracker; /** @@ -8634,30 +8674,78 @@ public class ConnectivityService extends IConnectivityManager.Stub * changes. */ private static final class LegacyNetworkActivityTracker { + private static final int NO_UID = -1; private final Context mContext; - private final INetworkManagementService mNMS; + private final INetd mNetd; + private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners = + new RemoteCallbackList<>(); + // Indicate the current system default network activity is active or not. + @GuardedBy("mActiveIdleTimers") + private boolean mNetworkActive; + @GuardedBy("mActiveIdleTimers") + private final ArrayMap<String, IdleTimerParams> mActiveIdleTimers = new ArrayMap(); + private final Handler mHandler; + + private class IdleTimerParams { + public final int timeout; + public final int transportType; - LegacyNetworkActivityTracker(@NonNull Context context, - @NonNull INetworkManagementService nms) { + IdleTimerParams(int timeout, int transport) { + this.timeout = timeout; + this.transportType = transport; + } + } + + LegacyNetworkActivityTracker(@NonNull Context context, @NonNull Handler handler, + @NonNull INetworkManagementService nms, @NonNull INetd netd) { mContext = context; - mNMS = nms; - try { - mNMS.registerObserver(mDataActivityObserver); - } catch (RemoteException e) { - loge("Error registering observer :" + e); + mNetd = netd; + mHandler = handler; + } + + public void setAndReportNetworkActive(boolean active, int transportType, long tsNanos) { + sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active, tsNanos); + synchronized (mActiveIdleTimers) { + mNetworkActive = active; + // If there are no idle timers, it means that system is not monitoring + // activity, so the system default network for those default network + // unspecified apps is always considered active. + // + // TODO: If the mActiveIdleTimers is empty, netd will actually not send + // any network activity change event. Whenever this event is received, + // the mActiveIdleTimers should be always not empty. The legacy behavior + // is no-op. Remove to refer to mNetworkActive only. + if (mNetworkActive || mActiveIdleTimers.isEmpty()) { + mHandler.sendMessage(mHandler.obtainMessage(EVENT_REPORT_NETWORK_ACTIVITY)); + } } } - // TODO: Migrate away the dependency with INetworkManagementEventObserver. - private final INetworkManagementEventObserver mDataActivityObserver = - new BaseNetworkObserver() { - @Override - public void interfaceClassDataActivityChanged(int transportType, boolean active, - long tsNanos, int uid) { - sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active, - tsNanos); + // The network activity should only be updated from ConnectivityService handler thread + // when mActiveIdleTimers lock is held. + @GuardedBy("mActiveIdleTimers") + private void reportNetworkActive() { + final int length = mNetworkActivityListeners.beginBroadcast(); + if (DDBG) log("reportNetworkActive, notify " + length + " listeners"); + try { + for (int i = 0; i < length; i++) { + try { + mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive(); + } catch (RemoteException | RuntimeException e) { + loge("Fail to send network activie to listener " + e); } - }; + } + } finally { + mNetworkActivityListeners.finishBroadcast(); + } + } + + @GuardedBy("mActiveIdleTimers") + public void handleReportNetworkActivity() { + synchronized (mActiveIdleTimers) { + reportNetworkActive(); + } + } // This is deprecated and only to support legacy use cases. private int transportTypeToLegacyType(int type) { @@ -8723,10 +8811,17 @@ public class ConnectivityService extends IConnectivityManager.Stub return; // do not track any other networks } + updateRadioPowerState(true /* isActive */, type); + if (timeout > 0 && iface != null) { try { - // TODO: Access INetd directly instead of NMS - mNMS.addIdleTimer(iface, timeout, type); + synchronized (mActiveIdleTimers) { + // Networks start up. + mNetworkActive = true; + mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, type)); + mNetd.idletimerAddInterface(iface, timeout, Integer.toString(type)); + reportNetworkActive(); + } } catch (Exception e) { // You shall not crash! loge("Exception in setupDataActivityTracking " + e); @@ -8741,16 +8836,28 @@ public class ConnectivityService extends IConnectivityManager.Stub final String iface = networkAgent.linkProperties.getInterfaceName(); final NetworkCapabilities caps = networkAgent.networkCapabilities; - if (iface != null && (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) - || caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) { - try { - // the call fails silently if no idle timer setup for this interface - // TODO: Access INetd directly instead of NMS - mNMS.removeIdleTimer(iface); - } catch (Exception e) { - // You shall not crash! - loge("Exception in removeDataActivityTracking " + e); + if (iface == null) return; + + final int type; + if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + type = NetworkCapabilities.TRANSPORT_CELLULAR; + } else if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + type = NetworkCapabilities.TRANSPORT_WIFI; + } else { + return; // do not track any other networks + } + + try { + updateRadioPowerState(false /* isActive */, type); + synchronized (mActiveIdleTimers) { + final IdleTimerParams params = mActiveIdleTimers.remove(iface); + // The call fails silently if no idle timer setup for this interface + mNetd.idletimerRemoveInterface(iface, params.timeout, + Integer.toString(params.transportType)); } + } catch (Exception e) { + // You shall not crash! + loge("Exception in removeDataActivityTracking " + e); } } @@ -8766,6 +8873,53 @@ public class ConnectivityService extends IConnectivityManager.Stub removeDataActivityTracking(oldNetwork); } } + + private void updateRadioPowerState(boolean isActive, int transportType) { + final BatteryStatsManager bs = mContext.getSystemService(BatteryStatsManager.class); + switch (transportType) { + case NetworkCapabilities.TRANSPORT_CELLULAR: + bs.reportMobileRadioPowerState(isActive, NO_UID); + break; + case NetworkCapabilities.TRANSPORT_WIFI: + bs.reportWifiRadioPowerState(isActive, NO_UID); + break; + default: + logw("Untracked transport type:" + transportType); + } + } + + public boolean isDefaultNetworkActive() { + synchronized (mActiveIdleTimers) { + // If there are no idle timers, it means that system is not monitoring activity, + // so the default network is always considered active. + // + // TODO : Distinguish between the cases where mActiveIdleTimers is empty because + // tracking is disabled (negative idle timer value configured), or no active default + // network. In the latter case, this reports active but it should report inactive. + return mNetworkActive || mActiveIdleTimers.isEmpty(); + } + } + + public void registerNetworkActivityListener(@NonNull INetworkActivityListener l) { + mNetworkActivityListeners.register(l); + } + + public void unregisterNetworkActivityListener(@NonNull INetworkActivityListener l) { + mNetworkActivityListeners.unregister(l); + } + + public void dump(IndentingPrintWriter pw) { + synchronized (mActiveIdleTimers) { + pw.print("mNetworkActive="); pw.println(mNetworkActive); + pw.println("Idle timers:"); + for (HashMap.Entry<String, IdleTimerParams> ent : mActiveIdleTimers.entrySet()) { + pw.print(" "); pw.print(ent.getKey()); pw.println(":"); + final IdleTimerParams params = ent.getValue(); + pw.print(" timeout="); pw.print(params.timeout); + pw.print(" type="); pw.println(params.transportType); + } + } + } } /** @@ -8883,7 +9037,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private void updateDefaultNetworksForOemNetworkPreference( @NonNull final Set<NetworkRequestInfo> nris) { - handleRemoveNetworkRequests(mDefaultNetworkRequests); + // Pass in a defensive copy as this collection will be updated on remove. + handleRemoveNetworkRequests(new ArraySet<>(mDefaultNetworkRequests)); addPerAppDefaultNetworkRequests(nris); } diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index 88ce2208adcb..e29e894a5cc0 100644 --- a/services/core/java/com/android/server/DynamicSystemService.java +++ b/services/core/java/com/android/server/DynamicSystemService.java @@ -28,6 +28,7 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.image.IDynamicSystemService; +import android.os.storage.DiskInfo; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.util.Slog; @@ -40,6 +41,7 @@ import java.io.File; */ public class DynamicSystemService extends IDynamicSystemService.Stub { private static final String TAG = "DynamicSystemService"; + private static final long MINIMUM_SD_MB = (30L << 10); private static final int GSID_ROUGH_TIMEOUT_MS = 8192; private static final String PATH_DEFAULT = "/data/gsi/"; private Context mContext; @@ -95,6 +97,13 @@ public class DynamicSystemService extends IDynamicSystemService.Stub { if (!volume.isMountedWritable()) { continue; } + DiskInfo disk = volume.getDisk(); + long mega = disk.size >> 20; + Slog.i(TAG, volume.getPath() + ": " + mega + " MB"); + if (mega < MINIMUM_SD_MB) { + Slog.i(TAG, volume.getPath() + ": insufficient storage"); + continue; + } File sd_internal = volume.getInternalPathForUser(userId); if (sd_internal != null) { path = new File(sd_internal, dsuSlot).getPath(); diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index b48bc900aa84..81d4b9da63c8 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -48,7 +48,6 @@ import android.net.TrafficStats; import android.net.util.NetdService; import android.os.Binder; import android.os.IBinder; -import android.os.INetworkManagementService; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -64,6 +63,7 @@ import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; +import com.android.net.module.util.NetdUtils; import libcore.io.IoUtils; @@ -117,9 +117,6 @@ public class IpSecService extends IIpSecService.Stub { /* Binder context for this service */ private final Context mContext; - /* NetworkManager instance */ - private final INetworkManagementService mNetworkManager; - /** * The next non-repeating global ID for tracking resources between users, this service, and * kernel data structures. Accessing this variable is not thread safe, so it is only read or @@ -1014,13 +1011,13 @@ public class IpSecService extends IIpSecService.Stub { * * @param context Binder context for this service */ - private IpSecService(Context context, INetworkManagementService networkManager) { - this(context, networkManager, IpSecServiceConfiguration.GETSRVINSTANCE); + private IpSecService(Context context) { + this(context, IpSecServiceConfiguration.GETSRVINSTANCE); } - static IpSecService create(Context context, INetworkManagementService networkManager) + static IpSecService create(Context context) throws InterruptedException { - final IpSecService service = new IpSecService(context, networkManager); + final IpSecService service = new IpSecService(context); service.connectNativeNetdService(); return service; } @@ -1034,11 +1031,9 @@ public class IpSecService extends IIpSecService.Stub { /** @hide */ @VisibleForTesting - public IpSecService(Context context, INetworkManagementService networkManager, - IpSecServiceConfiguration config) { + public IpSecService(Context context, IpSecServiceConfiguration config) { this( context, - networkManager, config, (fd, uid) -> { try { @@ -1052,10 +1047,9 @@ public class IpSecService extends IIpSecService.Stub { /** @hide */ @VisibleForTesting - public IpSecService(Context context, INetworkManagementService networkManager, - IpSecServiceConfiguration config, UidFdTagger uidFdTagger) { + public IpSecService(Context context, IpSecServiceConfiguration config, + UidFdTagger uidFdTagger) { mContext = context; - mNetworkManager = Objects.requireNonNull(networkManager); mSrvConfig = config; mUidFdTagger = uidFdTagger; } @@ -1335,7 +1329,7 @@ public class IpSecService extends IIpSecService.Stub { netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId); Binder.withCleanCallingIdentity(() -> { - mNetworkManager.setInterfaceUp(intfName); + NetdUtils.setInterfaceUp(netd, intfName); }); for (int selAddrFamily : ADDRESS_FAMILIES) { diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index d30a6405e95d..44054088d937 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -69,7 +69,6 @@ import android.os.BatteryStats; import android.os.Binder; import android.os.Handler; import android.os.IBinder; -import android.os.INetworkActivityListener; import android.os.INetworkManagementService; import android.os.Process; import android.os.RemoteCallbackList; @@ -80,7 +79,6 @@ import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; -import android.telephony.DataConnectionRealTimeInfo; import android.text.TextUtils; import android.util.Log; import android.util.Slog; @@ -229,32 +227,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub { @GuardedBy("mQuotaLock") private volatile boolean mDataSaverMode; - private final Object mIdleTimerLock = new Object(); - /** Set of interfaces with active idle timers. */ - private static class IdleTimerParams { - public final int timeout; - public final int type; - public int networkCount; - - IdleTimerParams(int timeout, int type) { - this.timeout = timeout; - this.type = type; - this.networkCount = 1; - } - } - private HashMap<String, IdleTimerParams> mActiveIdleTimers = Maps.newHashMap(); - private volatile boolean mFirewallEnabled; private volatile boolean mStrictEnabled; - private boolean mMobileActivityFromRadio = false; - private int mLastPowerStateFromRadio = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; - private int mLastPowerStateFromWifi = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; - - private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners = - new RemoteCallbackList<>(); - private boolean mNetworkActive; - /** * Constructs a new NetworkManagementService instance * @@ -397,55 +372,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { */ private void notifyInterfaceClassActivity(int type, boolean isActive, long tsNanos, int uid) { - final boolean isMobile = ConnectivityManager.isNetworkTypeMobile(type); - int powerState = isActive - ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH - : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; - if (isMobile) { - if (mLastPowerStateFromRadio != powerState) { - mLastPowerStateFromRadio = powerState; - try { - // TODO: The interface changes that comes from netd are handled by BSS itself. - // There are still events caused by setting or removing idle timer, so keep - // reporting from here until setting idler timer moved to CS. - getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos, uid); - } catch (RemoteException e) { - } - } - } - - if (ConnectivityManager.isNetworkTypeWifi(type)) { - if (mLastPowerStateFromWifi != powerState) { - mLastPowerStateFromWifi = powerState; - try { - // TODO: The interface changes that comes from netd are handled by BSS itself. - // There are still events caused by setting or removing idle timer, so keep - // reporting from here until setting idler timer moved to CS. - getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid); - } catch (RemoteException e) { - } - } - } - - final boolean active = isActive; invokeForAllObservers(o -> o.interfaceClassDataActivityChanged( - type, active, tsNanos, uid)); - - boolean report = false; - synchronized (mIdleTimerLock) { - if (mActiveIdleTimers.isEmpty()) { - // If there are no idle timers, we are not monitoring activity, so we - // are always considered active. - isActive = true; - } - if (mNetworkActive != isActive) { - mNetworkActive = isActive; - report = isActive; - } - } - if (report) { - reportNetworkActive(); - } + type, isActive, tsNanos, uid)); } @Override @@ -1122,60 +1050,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub { } @Override - public void addIdleTimer(String iface, int timeout, final int type) { - NetworkStack.checkNetworkStackPermission(mContext); - - if (DBG) Slog.d(TAG, "Adding idletimer"); - - synchronized (mIdleTimerLock) { - IdleTimerParams params = mActiveIdleTimers.get(iface); - if (params != null) { - // the interface already has idletimer, update network count - params.networkCount++; - return; - } - - try { - mNetdService.idletimerAddInterface(iface, timeout, Integer.toString(type)); - } catch (RemoteException | ServiceSpecificException e) { - throw new IllegalStateException(e); - } - mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, type)); - - // Networks start up. - if (ConnectivityManager.isNetworkTypeMobile(type)) { - mNetworkActive = false; - } - mDaemonHandler.post(() -> notifyInterfaceClassActivity(type, true, - SystemClock.elapsedRealtimeNanos(), -1)); - } - } - - @Override - public void removeIdleTimer(String iface) { - NetworkStack.checkNetworkStackPermission(mContext); - - if (DBG) Slog.d(TAG, "Removing idletimer"); - - synchronized (mIdleTimerLock) { - final IdleTimerParams params = mActiveIdleTimers.get(iface); - if (params == null || --(params.networkCount) > 0) { - return; - } - - try { - mNetdService.idletimerRemoveInterface(iface, - params.timeout, Integer.toString(params.type)); - } catch (RemoteException | ServiceSpecificException e) { - throw new IllegalStateException(e); - } - mActiveIdleTimers.remove(iface); - mDaemonHandler.post(() -> notifyInterfaceClassActivity(params.type, false, - SystemClock.elapsedRealtimeNanos(), -1)); - } - } - - @Override public void setInterfaceQuota(String iface, long quotaBytes) { NetworkStack.checkNetworkStackPermission(mContext); @@ -1813,44 +1687,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub { } @Override - public void registerNetworkActivityListener(INetworkActivityListener listener) { - mNetworkActivityListeners.register(listener); - } - - @Override - public void unregisterNetworkActivityListener(INetworkActivityListener listener) { - mNetworkActivityListeners.unregister(listener); - } - - @Override - public boolean isNetworkActive() { - synchronized (mNetworkActivityListeners) { - return mNetworkActive || mActiveIdleTimers.isEmpty(); - } - } - - private void reportNetworkActive() { - final int length = mNetworkActivityListeners.beginBroadcast(); - try { - for (int i = 0; i < length; i++) { - try { - mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive(); - } catch (RemoteException | RuntimeException e) { - } - } - } finally { - mNetworkActivityListeners.finishBroadcast(); - } - } - - @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - pw.print("mMobileActivityFromRadio="); pw.print(mMobileActivityFromRadio); - pw.print(" mLastPowerStateFromRadio="); pw.println(mLastPowerStateFromRadio); - pw.print("mNetworkActive="); pw.println(mNetworkActive); - synchronized (mQuotaLock) { pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString()); pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString()); @@ -1882,17 +1721,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub { mUidFirewallRestrictedRules); } - synchronized (mIdleTimerLock) { - pw.println("Idle timers:"); - for (HashMap.Entry<String, IdleTimerParams> ent : mActiveIdleTimers.entrySet()) { - pw.print(" "); pw.print(ent.getKey()); pw.println(":"); - IdleTimerParams params = ent.getValue(); - pw.print(" timeout="); pw.print(params.timeout); - pw.print(" type="); pw.print(params.type); - pw.print(" networkCount="); pw.println(params.networkCount); - } - } - pw.print("Firewall enabled: "); pw.println(mFirewallEnabled); pw.print("Netd service status: " ); if (mNetdService == null) { diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 67f6ec9f9a41..f5c2aacaebb0 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -259,10 +259,7 @@ public class PackageWatchdog { mIsPackagesReady = true; mHealthCheckController.setCallbacks(packageName -> onHealthCheckPassed(packageName), packages -> onSupportedPackages(packages), - () -> { - syncRequestsAsync(); - mSyncRequired = true; - }); + this::onSyncRequestNotified); setPropertyChangedListenerLocked(); updateConfigs(); registerConnectivityModuleHealthListener(); @@ -537,6 +534,7 @@ public class PackageWatchdog { synchronized (mLock) { mIsHealthCheckEnabled = enabled; mHealthCheckController.setEnabled(enabled); + mSyncRequired = true; // Prune to update internal state whenever health check is enabled/disabled syncState("health check state " + (enabled ? "enabled" : "disabled")); } @@ -788,6 +786,13 @@ public class PackageWatchdog { } } + private void onSyncRequestNotified() { + synchronized (mLock) { + mSyncRequired = true; + syncRequestsAsync(); + } + } + @GuardedBy("mLock") private Set<String> getPackagesPendingHealthChecksLocked() { Set<String> packages = new ArraySet<>(); diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index edaf6a90bd4a..f2782f64995a 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -16,6 +16,7 @@ package com.android.server; +import static android.Manifest.permission.MANAGE_SENSOR_PRIVACY; import static android.app.ActivityManager.RunningServiceInfo; import static android.app.ActivityManager.RunningTaskInfo; import static android.app.AppOpsManager.MODE_ALLOWED; @@ -24,9 +25,9 @@ import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.content.Intent.EXTRA_PACKAGE_NAME; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR; +import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; +import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; import static android.os.UserHandle.USER_SYSTEM; -import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA; -import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN; import android.annotation.NonNull; @@ -197,18 +198,20 @@ public final class SensorPrivacyService extends SystemService { Intent.EXTRA_USER)).getIdentifier(), intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false); } - }, new IntentFilter(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY)); + }, new IntentFilter(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY), + MANAGE_SENSOR_PRIVACY, null); } @Override - public void onOpStarted(int code, int uid, String packageName, + public void onOpStarted(int code, int uid, String packageName, String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) { - onOpNoted(code, uid, packageName, flags, result); + onOpNoted(code, uid, packageName, attributionTag, flags, result); } @Override public void onOpNoted(int code, int uid, String packageName, - @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) { + String attributionTag, @AppOpsManager.OpFlags int flags, + @AppOpsManager.Mode int result) { if (result != MODE_ALLOWED || (flags & AppOpsManager.OP_FLAGS_ALL_TRUSTED) == 0) { return; } @@ -403,12 +406,12 @@ public final class SensorPrivacyService extends SystemService { */ @Override public void setSensorPrivacy(boolean enable) { + enforceManageSensorPrivacyPermission(); // Keep the state consistent between all users to make it a single global state forAllUsers(userId -> setSensorPrivacy(userId, enable)); } private void setSensorPrivacy(@UserIdInt int userId, boolean enable) { - enforceSensorPrivacyPermission(); synchronized (mLock) { mEnabled.put(userId, enable); persistSensorPrivacyStateLocked(); @@ -418,7 +421,7 @@ public final class SensorPrivacyService extends SystemService { @Override public void setIndividualSensorPrivacy(@UserIdInt int userId, int sensor, boolean enable) { - enforceSensorPrivacyPermission(); + enforceManageSensorPrivacyPermission(); synchronized (mLock) { SparseBooleanArray userIndividualEnabled = mIndividualEnabled.get(userId, new SparseBooleanArray()); @@ -445,6 +448,7 @@ public final class SensorPrivacyService extends SystemService { @Override public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId, int sensor, boolean enable) { + enforceManageSensorPrivacyPermission(); int parentId = mUserManagerInternal.getProfileParentId(userId); forAllUsers(userId2 -> { if (parentId == mUserManagerInternal.getProfileParentId(userId2)) { @@ -457,14 +461,27 @@ public final class SensorPrivacyService extends SystemService { * Enforces the caller contains the necessary permission to change the state of sensor * privacy. */ - private void enforceSensorPrivacyPermission() { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.MANAGE_SENSOR_PRIVACY) == PERMISSION_GRANTED) { + private void enforceManageSensorPrivacyPermission() { + enforcePermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY, + "Changing sensor privacy requires the following permission: " + + MANAGE_SENSOR_PRIVACY); + } + + /** + * Enforces the caller contains the necessary permission to observe changes to the sate of + * sensor privacy. + */ + private void enforceObserveSensorPrivacyPermission() { + enforcePermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY, + "Observing sensor privacy changes requires the following permission: " + + android.Manifest.permission.OBSERVE_SENSOR_PRIVACY); + } + + private void enforcePermission(String permission, String message) { + if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { return; } - throw new SecurityException( - "Changing sensor privacy requires the following permission: " - + android.Manifest.permission.MANAGE_SENSOR_PRIVACY); + throw new SecurityException(message); } /** @@ -472,6 +489,7 @@ public final class SensorPrivacyService extends SystemService { */ @Override public boolean isSensorPrivacyEnabled() { + enforceObserveSensorPrivacyPermission(); return isSensorPrivacyEnabled(USER_SYSTEM); } @@ -483,6 +501,7 @@ public final class SensorPrivacyService extends SystemService { @Override public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) { + enforceObserveSensorPrivacyPermission(); synchronized (mLock) { SparseBooleanArray states = mIndividualEnabled.get(userId); if (states == null) { @@ -700,6 +719,7 @@ public final class SensorPrivacyService extends SystemService { */ @Override public void addSensorPrivacyListener(ISensorPrivacyListener listener) { + enforceObserveSensorPrivacyPermission(); if (listener == null) { throw new NullPointerException("listener cannot be null"); } @@ -712,6 +732,7 @@ public final class SensorPrivacyService extends SystemService { @Override public void addIndividualSensorPrivacyListener(int userId, int sensor, ISensorPrivacyListener listener) { + enforceObserveSensorPrivacyPermission(); if (listener == null) { throw new NullPointerException("listener cannot be null"); } @@ -723,6 +744,7 @@ public final class SensorPrivacyService extends SystemService { */ @Override public void removeSensorPrivacyListener(ISensorPrivacyListener listener) { + enforceObserveSensorPrivacyPermission(); if (listener == null) { throw new NullPointerException("listener cannot be null"); } @@ -732,6 +754,7 @@ public final class SensorPrivacyService extends SystemService { @Override public void suppressIndividualSensorPrivacyReminders(int userId, String packageName, IBinder token, boolean suppress) { + enforceManageSensorPrivacyPermission(); Objects.requireNonNull(packageName); Objects.requireNonNull(token); @@ -883,13 +906,13 @@ public final class SensorPrivacyService extends SystemService { } /** - * Convert a string into a {@link SensorPrivacyManager.IndividualSensor id}. + * Convert a string into a {@link SensorPrivacyManager.Sensors.Sensor id}. * * @param sensor The name to convert * * @return The id corresponding to the name */ - private @SensorPrivacyManager.IndividualSensor int sensorStrToId(@Nullable String sensor) { + private @SensorPrivacyManager.Sensors.Sensor int sensorStrToId(@Nullable String sensor) { if (sensor == null) { return UNKNOWN; } @@ -947,7 +970,7 @@ public final class SensorPrivacyService extends SystemService { return -1; } - enforceSensorPrivacyPermission(); + enforceManageSensorPrivacyPermission(); synchronized (mLock) { SparseBooleanArray individualEnabled = diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java index c2d8fa24157a..8a2894c84cc4 100644 --- a/services/core/java/com/android/server/ServiceWatcher.java +++ b/services/core/java/com/android/server/ServiceWatcher.java @@ -45,14 +45,18 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; +import android.util.ArrayMap; import android.util.Log; +import com.android.internal.annotations.Immutable; import com.android.internal.content.PackageMonitor; import com.android.internal.util.Preconditions; +import com.android.internal.util.function.pooled.PooledLambda; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.Predicate; @@ -87,26 +91,30 @@ public class ServiceWatcher implements ServiceConnection { /** Function to run on binder interface when first bound. */ public interface OnBindRunner { /** Called to run client code with the binder. */ - void run(IBinder binder, ComponentName service) throws RemoteException; + void run(IBinder binder, BoundService service) throws RemoteException; } /** * Information on the service ServiceWatcher has selected as the best option for binding. */ - private static final class ServiceInfo implements Comparable<ServiceInfo> { + @Immutable + public static final class BoundService implements Comparable<BoundService> { - public static final ServiceInfo NONE = new ServiceInfo(Integer.MIN_VALUE, null, - UserHandle.USER_NULL, false); + public static final BoundService NONE = new BoundService(Integer.MIN_VALUE, null, + false, null, -1); public final int version; - @Nullable public final ComponentName component; - @UserIdInt public final int userId; + @Nullable + public final ComponentName component; public final boolean serviceIsMultiuser; + public final int uid; + @Nullable + public final Bundle metadata; - ServiceInfo(ResolveInfo resolveInfo, int currentUserId) { + BoundService(ResolveInfo resolveInfo) { Preconditions.checkArgument(resolveInfo.serviceInfo.getComponentName() != null); - Bundle metadata = resolveInfo.serviceInfo.metaData; + metadata = resolveInfo.serviceInfo.metaData; if (metadata != null) { version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE); serviceIsMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false); @@ -116,16 +124,17 @@ public class ServiceWatcher implements ServiceConnection { } component = resolveInfo.serviceInfo.getComponentName(); - userId = serviceIsMultiuser ? UserHandle.USER_SYSTEM : currentUserId; + uid = resolveInfo.serviceInfo.applicationInfo.uid; } - private ServiceInfo(int version, @Nullable ComponentName component, int userId, - boolean serviceIsMultiuser) { + private BoundService(int version, @Nullable ComponentName component, + boolean serviceIsMultiuser, @Nullable Bundle metadata, int uid) { Preconditions.checkArgument(component != null || version == Integer.MIN_VALUE); this.version = version; this.component = component; - this.userId = userId; this.serviceIsMultiuser = serviceIsMultiuser; + this.metadata = metadata; + this.uid = uid; } public @Nullable String getPackageName() { @@ -137,21 +146,21 @@ public class ServiceWatcher implements ServiceConnection { if (this == o) { return true; } - if (!(o instanceof ServiceInfo)) { + if (!(o instanceof BoundService)) { return false; } - ServiceInfo that = (ServiceInfo) o; - return version == that.version && userId == that.userId + BoundService that = (BoundService) o; + return version == that.version && uid == that.uid && Objects.equals(component, that.component); } @Override public int hashCode() { - return Objects.hash(version, component, userId); + return Objects.hash(version, component, uid); } @Override - public int compareTo(ServiceInfo that) { + public int compareTo(BoundService that) { // ServiceInfos with higher version numbers always win (having a version number > // MIN_VALUE implies having a non-null component). if version numbers are equal, a // non-null component wins over a null component. if the version numbers are equal and @@ -164,10 +173,11 @@ public class ServiceWatcher implements ServiceConnection { } else if (component != null && that.component == null) { ret = 1; } else { - if (userId != UserHandle.USER_SYSTEM && that.userId == UserHandle.USER_SYSTEM) { + if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM + && UserHandle.getUserId(that.uid) == UserHandle.USER_SYSTEM) { ret = -1; - } else if (userId == UserHandle.USER_SYSTEM - && that.userId != UserHandle.USER_SYSTEM) { + } else if (UserHandle.getUserId(uid) == UserHandle.USER_SYSTEM + && UserHandle.getUserId(that.uid) != UserHandle.USER_SYSTEM) { ret = 1; } } @@ -180,7 +190,8 @@ public class ServiceWatcher implements ServiceConnection { if (component == null) { return "none"; } else { - return component.toShortString() + "@" + version + "[u" + userId + "]"; + return component.toShortString() + "@" + version + "[u" + + UserHandle.getUserId(uid) + "]"; } } } @@ -227,17 +238,23 @@ public class ServiceWatcher implements ServiceConnection { } }; - @Nullable private final OnBindRunner mOnBind; - @Nullable private final Runnable mOnUnbind; + // read/write from handler thread only + private final Map<ComponentName, BoundService> mPendingBinds = new ArrayMap<>(); + + @Nullable + private final OnBindRunner mOnBind; + + @Nullable + private final Runnable mOnUnbind; - // write from caller thread only, read anywhere - private volatile boolean mRegistered; + // read/write from handler thread only + private boolean mRegistered; // read/write from handler thread only private int mCurrentUserId; // write from handler thread only, read anywhere - private volatile ServiceInfo mTargetService; + private volatile BoundService mTargetService; private volatile IBinder mBinder; public ServiceWatcher(Context context, String action, @@ -274,7 +291,7 @@ public class ServiceWatcher implements ServiceConnection { mCurrentUserId = UserHandle.USER_NULL; - mTargetService = ServiceInfo.NONE; + mTargetService = BoundService.NONE; mBinder = null; } @@ -299,6 +316,11 @@ public class ServiceWatcher implements ServiceConnection { * Starts the process of determining the best matching service and maintaining a binding to it. */ public void register() { + mHandler.sendMessage(PooledLambda.obtainMessage(ServiceWatcher::registerInternal, + ServiceWatcher.this)); + } + + private void registerInternal() { Preconditions.checkState(!mRegistered); mPackageMonitor.register(mContext, UserHandle.ALL, true, mHandler); @@ -309,6 +331,8 @@ public class ServiceWatcher implements ServiceConnection { mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, intentFilter, null, mHandler); + // TODO: This makes the behavior of the class unpredictable as the caller needs + // to know the internal impl detail that calling register would pick the current user. mCurrentUserId = ActivityManager.getCurrentUser(); mRegistered = true; @@ -320,6 +344,11 @@ public class ServiceWatcher implements ServiceConnection { * Stops the process of determining the best matching service and releases any binding. */ public void unregister() { + mHandler.sendMessage(PooledLambda.obtainMessage(ServiceWatcher::unregisterInternal, + ServiceWatcher.this)); + } + + private void unregisterInternal() { Preconditions.checkState(mRegistered); mRegistered = false; @@ -333,7 +362,7 @@ public class ServiceWatcher implements ServiceConnection { private void onBestServiceChanged(boolean forceRebind) { Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - ServiceInfo bestServiceInfo = ServiceInfo.NONE; + BoundService bestServiceInfo = BoundService.NONE; if (mRegistered) { List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser( @@ -344,7 +373,7 @@ public class ServiceWatcher implements ServiceConnection { if (!mServiceCheckPredicate.test(resolveInfo)) { continue; } - ServiceInfo serviceInfo = new ServiceInfo(resolveInfo, mCurrentUserId); + BoundService serviceInfo = new BoundService(resolveInfo); if (serviceInfo.compareTo(bestServiceInfo) > 0) { bestServiceInfo = serviceInfo; } @@ -356,21 +385,22 @@ public class ServiceWatcher implements ServiceConnection { } } - private void rebind(ServiceInfo newServiceInfo) { + private void rebind(BoundService newServiceInfo) { Preconditions.checkState(Looper.myLooper() == mHandler.getLooper()); - if (!mTargetService.equals(ServiceInfo.NONE)) { + if (!mTargetService.equals(BoundService.NONE)) { if (D) { Log.d(TAG, "[" + mIntent.getAction() + "] unbinding from " + mTargetService); } mContext.unbindService(this); onServiceDisconnected(mTargetService.component); - mTargetService = ServiceInfo.NONE; + mPendingBinds.remove(mTargetService.component); + mTargetService = BoundService.NONE; } mTargetService = newServiceInfo; - if (mTargetService.equals(ServiceInfo.NONE)) { + if (mTargetService.equals(BoundService.NONE)) { return; } @@ -381,10 +411,12 @@ public class ServiceWatcher implements ServiceConnection { Intent bindIntent = new Intent(mIntent).setComponent(mTargetService.component); if (!mContext.bindServiceAsUser(bindIntent, this, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE, - mHandler, UserHandle.of(mTargetService.userId))) { - mTargetService = ServiceInfo.NONE; + mHandler, UserHandle.of(UserHandle.getUserId(mTargetService.uid)))) { + mTargetService = BoundService.NONE; Log.e(TAG, getLogPrefix() + " unexpected bind failure - retrying later"); mHandler.postDelayed(() -> onBestServiceChanged(false), RETRY_DELAY_MS); + } else { + mPendingBinds.put(mTargetService.component, mTargetService); } } @@ -397,10 +429,15 @@ public class ServiceWatcher implements ServiceConnection { Log.d(TAG, getLogPrefix() + " connected to " + component.toShortString()); } + final BoundService boundService = mPendingBinds.remove(component); + if (boundService == null) { + return; + } + mBinder = binder; if (mOnBind != null) { try { - mOnBind.run(binder, component); + mOnBind.run(binder, boundService); } catch (RuntimeException | RemoteException e) { // binders may propagate some specific non-RemoteExceptions from the other side // through the binder as well - we cannot allow those to crash the system server diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 2f9819997257..233a50d417ad 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -51,6 +51,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.app.AnrController; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.KeyguardManager; @@ -938,14 +939,29 @@ class StorageManagerService extends IStorageManager.Stub if (transcodeEnabled) { LocalServices.getService(ActivityManagerInternal.class) - .registerAnrController((packageName, uid) -> { - try { - return mStorageSessionController.getAnrDelayMillis(packageName, uid); - } catch (ExternalStorageServiceException e) { - Log.e(TAG, "Failed to get ANR delay for " + packageName, e); - return 0; - } - }); + .registerAnrController(new ExternalStorageServiceAnrController()); + } + } + + // TODO(b/170486601): Check transcoding status based on events pushed from the MediaProvider + private class ExternalStorageServiceAnrController implements AnrController { + @Override + public long getAnrDelayMillis(String packageName, int uid) { + int delay = SystemProperties.getInt("sys.fuse.transcode_anr_delay", 0); + Log.d(TAG, "getAnrDelayMillis: " + packageName + ". Delaying for " + delay + "ms"); + return delay; + } + + @Override + public void onAnrDelayStarted(String packageName, int uid) { + Log.d(TAG, "onAnrDelayStarted: " + packageName); + } + + @Override + public boolean onAnrDelayCompleted(String packageName, int uid) { + boolean show = SystemProperties.getBoolean("sys.fuse.transcode_anr_dialog_show", true); + Log.d(TAG, "onAnrDelayCompleted: " + packageName + ". Show: " + show); + return show; } } @@ -3322,6 +3338,39 @@ class StorageManagerService extends IStorageManager.Stub } } + @Override + public void notifyAppIoBlocked(String volumeUuid, int uid, int tid, int reason) { + enforceExternalStorageService(); + + mStorageSessionController.notifyAppIoBlocked(volumeUuid, uid, tid, reason); + } + + @Override + public void notifyAppIoResumed(String volumeUuid, int uid, int tid, int reason) { + enforceExternalStorageService(); + + mStorageSessionController.notifyAppIoResumed(volumeUuid, uid, tid, reason); + } + + private boolean isAppIoBlocked(int uid) { + return mStorageSessionController.isAppIoBlocked(uid); + } + + /** + * Enforces that the caller is the {@link ExternalStorageService} + * + * @throws SecurityException if the caller doesn't have the + * {@link android.Manifest.permission.WRITE_MEDIA_STORAGE} permission or is not the + * {@link ExternalStorageService} + */ + private void enforceExternalStorageService() { + enforcePermission(android.Manifest.permission.WRITE_MEDIA_STORAGE); + int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); + if (callingAppId != mMediaStoreAuthorityAppId) { + throw new SecurityException("Only the ExternalStorageService is permitted"); + } + } + /** Not thread safe */ class AppFuseMountScope extends AppFuseBridge.MountScope { private boolean mMounted = false; diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index b09b6ca61377..5a5f1a3f3723 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -313,9 +313,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private List<PhysicalChannelConfig> mPhysicalChannelConfigs; - private boolean mIsDataEnabled = false; + private boolean[] mIsDataEnabled; - private int mDataEnabledReason; + private int[] mDataEnabledReason; private Map<Integer, Long> mAllowedNetworkTypesList; @@ -524,6 +524,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones); mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones); mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones); + mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones); + mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones); // ds -> ss switch. if (mNumPhones < oldNumPhones) { @@ -565,6 +567,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPreciseDataConnectionStates.add(new ArrayMap<>()); mBarringInfo.add(i, new BarringInfo()); mTelephonyDisplayInfos[i] = null; + mIsDataEnabled[i] = false; + mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER; mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); } } @@ -626,6 +630,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones]; mPhysicalChannelConfigs = new ArrayList<>(); mAllowedNetworkTypesList = new HashMap<>(); + mIsDataEnabled = new boolean[numPhones]; + mDataEnabledReason = new int[numPhones]; for (int i = 0; i < numPhones; i++) { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE; @@ -655,6 +661,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPreciseDataConnectionStates.add(new ArrayMap<>()); mBarringInfo.add(i, new BarringInfo()); mTelephonyDisplayInfos[i] = null; + mIsDataEnabled[i] = false; + mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER; mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); } @@ -1150,7 +1158,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (events.contains( PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) { try { - r.callback.onDataEnabledChanged(mIsDataEnabled, mDataEnabledReason); + r.callback.onDataEnabledChanged( + mIsDataEnabled[phoneId], mDataEnabledReason[phoneId]); } catch (RemoteException ex) { remove(r.binder); } @@ -2370,30 +2379,36 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { /** * Notify that the data enabled has changed. * + * @param phoneId the phone id. + * @param subId the subId. * @param enabled True if data is enabled, otherwise disabled. * @param reason Reason for data enabled/disabled. See {@code DATA_*} in * {@link TelephonyManager}. */ - public void notifyDataEnabled(boolean enabled, + public void notifyDataEnabled(int phoneId, int subId, boolean enabled, @TelephonyManager.DataEnabledReason int reason) { if (!checkNotifyPermission("notifyDataEnabled()")) { return; } if (VDBG) { - log("notifyDataEnabled: enabled=" + enabled + " reason=" + reason); + log("notifyDataEnabled: PhoneId=" + phoneId + " subId=" + subId + + " enabled=" + enabled + " reason=" + reason); } - mIsDataEnabled = enabled; - mDataEnabledReason = reason; synchronized (mRecords) { - for (Record r : mRecords) { - if (r.matchPhoneStateListenerEvent( - PhoneStateListener.EVENT_DATA_ENABLED_CHANGED)) { - try { - r.callback.onDataEnabledChanged(enabled, reason); - } catch (RemoteException ex) { - mRemoveList.add(r.binder); + if (validatePhoneId(phoneId)) { + mIsDataEnabled[phoneId] = enabled; + mDataEnabledReason[phoneId] = reason; + for (Record r : mRecords) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.EVENT_DATA_ENABLED_CHANGED) + && idMatch(r.subId, subId, phoneId)) { + try { + r.callback.onDataEnabledChanged(enabled, reason); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } } } } @@ -2481,6 +2496,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]); pw.println("mBarringInfo=" + mBarringInfo.get(i)); pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]); + pw.println("mIsDataEnabled=" + mIsDataEnabled); + pw.println("mDataEnabledReason=" + mDataEnabledReason); pw.decreaseIndent(); } pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState); @@ -2491,8 +2508,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mDefaultPhoneId=" + mDefaultPhoneId); pw.println("mDefaultSubId=" + mDefaultSubId); pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs); - pw.println("mIsDataEnabled=" + mIsDataEnabled); - pw.println("mDataEnabledReason=" + mDataEnabledReason); pw.decreaseIndent(); diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index ed4e1d951b43..8d5d3d939e4b 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -16,6 +16,8 @@ package com.android.server; +import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE; + import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback; @@ -33,6 +35,7 @@ import android.net.vcn.IVcnManagementService; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; +import android.net.vcn.VcnManager.VcnErrorCode; import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.net.wifi.WifiInfo; import android.os.Binder; @@ -304,8 +307,8 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, - @NonNull VcnSafeModeCallback safeModeCallback) { - return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safeModeCallback); + @NonNull VcnCallback vcnCallback) { + return new Vcn(vcnContext, subscriptionGroup, config, snapshot, vcnCallback); } /** Gets the subId indicated by the given {@link WifiInfo}. */ @@ -457,12 +460,10 @@ public class VcnManagementService extends IVcnManagementService.Stub { // TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active // VCN. - final VcnSafeModeCallbackImpl safeModeCallback = - new VcnSafeModeCallbackImpl(subscriptionGroup); + final VcnCallbackImpl vcnCallback = new VcnCallbackImpl(subscriptionGroup); final Vcn newInstance = - mDeps.newVcn( - mVcnContext, subscriptionGroup, config, mLastSnapshot, safeModeCallback); + mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback); mVcns.put(subscriptionGroup, newInstance); // Now that a new VCN has started, notify all registered listeners to refresh their @@ -784,20 +785,47 @@ public class VcnManagementService extends IVcnManagementService.Stub { } // TODO(b/180452282): Make name more generic and implement directly with VcnManagementService - /** Callback for signalling when a Vcn has entered safe mode. */ - public interface VcnSafeModeCallback { + /** Callback for Vcn signals sent up to VcnManagementService. */ + public interface VcnCallback { /** Called by a Vcn to signal that it has entered safe mode. */ void onEnteredSafeMode(); + + /** Called by a Vcn to signal that an error occurred. */ + void onGatewayConnectionError( + @NonNull int[] networkCapabilities, + @VcnErrorCode int errorCode, + @Nullable String exceptionClass, + @Nullable String exceptionMessage); } - /** VcnSafeModeCallback is used by Vcns to notify VcnManagementService on entering safe mode. */ - private class VcnSafeModeCallbackImpl implements VcnSafeModeCallback { + /** VcnCallbackImpl for Vcn signals sent up to VcnManagementService. */ + private class VcnCallbackImpl implements VcnCallback { @NonNull private final ParcelUuid mSubGroup; - private VcnSafeModeCallbackImpl(@NonNull final ParcelUuid subGroup) { + private VcnCallbackImpl(@NonNull final ParcelUuid subGroup) { mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup"); } + private boolean isCallbackPermissioned(@NonNull VcnStatusCallbackInfo cbInfo) { + if (!mSubGroup.equals(cbInfo.mSubGroup)) { + return false; + } + + if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup( + mSubGroup, cbInfo.mPkgName)) { + return false; + } + + if (!mLocationPermissionChecker.checkLocationPermission( + cbInfo.mPkgName, + "VcnStatusCallback" /* featureId */, + cbInfo.mUid, + null /* message */)) { + return false; + } + return true; + } + @Override public void onEnteredSafeMode() { synchronized (mLock) { @@ -810,23 +838,39 @@ public class VcnManagementService extends IVcnManagementService.Stub { // Notify all registered StatusCallbacks for this subGroup for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { - if (!mSubGroup.equals(cbInfo.mSubGroup)) { - continue; - } - if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup( - mSubGroup, cbInfo.mPkgName)) { - continue; + if (isCallbackPermissioned(cbInfo)) { + Binder.withCleanCallingIdentity( + () -> + cbInfo.mCallback.onVcnStatusChanged( + VCN_STATUS_CODE_SAFE_MODE)); } + } + } + } - if (!mLocationPermissionChecker.checkLocationPermission( - cbInfo.mPkgName, - "VcnStatusCallback" /* featureId */, - cbInfo.mUid, - null /* message */)) { - continue; - } + @Override + public void onGatewayConnectionError( + @NonNull int[] networkCapabilities, + @VcnErrorCode int errorCode, + @Nullable String exceptionClass, + @Nullable String exceptionMessage) { + synchronized (mLock) { + // Ignore if this subscription group doesn't exist anymore + if (!mVcns.containsKey(mSubGroup)) { + return; + } - Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode()); + // Notify all registered StatusCallbacks for this subGroup + for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { + if (isCallbackPermissioned(cbInfo)) { + Binder.withCleanCallingIdentity( + () -> + cbInfo.mCallback.onGatewayConnectionError( + networkCapabilities, + errorCode, + exceptionClass, + exceptionMessage)); + } } } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index a4ff230b0678..d998ebbf4aff 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -20,10 +20,35 @@ import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT; +import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT; import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; +import static android.os.PowerWhitelistManager.REASON_ACTIVITY_STARTER; +import static android.os.PowerWhitelistManager.REASON_ALLOWLISTED_PACKAGE; +import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_BACKGROUND_FGS_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_COMPANION_DEVICE_MANAGER; +import static android.os.PowerWhitelistManager.REASON_DENIED; +import static android.os.PowerWhitelistManager.REASON_DEVICE_DEMO_MODE; +import static android.os.PowerWhitelistManager.REASON_DEVICE_OWNER; +import static android.os.PowerWhitelistManager.REASON_EXEMPTED_PACKAGE; +import static android.os.PowerWhitelistManager.REASON_FGS_BINDING; +import static android.os.PowerWhitelistManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_PROC_STATE_PERSISTENT; +import static android.os.PowerWhitelistManager.REASON_PROC_STATE_PERSISTENT_UI; +import static android.os.PowerWhitelistManager.REASON_PROC_STATE_TOP; +import static android.os.PowerWhitelistManager.REASON_PROFILE_OWNER; +import static android.os.PowerWhitelistManager.REASON_START_ACTIVITY_FLAG; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_UID; +import static android.os.PowerWhitelistManager.REASON_UID_VISIBLE; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; +import static android.os.PowerWhitelistManager.getReasonCodeFromProcState; +import static android.os.PowerWhitelistManager.reasonCodeToString; import static android.os.Process.NFC_UID; import static android.os.Process.ROOT_UID; import static android.os.Process.SHELL_UID; @@ -43,7 +68,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE_E import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -52,6 +76,7 @@ import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.BroadcastOptions; +import android.app.ForegroundServiceStartNotAllowedException; import android.app.IApplicationThread; import android.app.IServiceConnection; import android.app.Notification; @@ -85,6 +110,8 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerWhitelistManager; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; @@ -129,8 +156,6 @@ import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Comparator; @@ -151,58 +176,6 @@ public final class ActiveServices { private static final boolean SHOW_DUNGEON_NOTIFICATION = false; - public static final int FGS_FEATURE_DENIED = 0; - public static final int FGS_FEATURE_ALLOWED_BY_UID_STATE = 1; - public static final int FGS_FEATURE_ALLOWED_BY_PROC_STATE = 2; - public static final int FGS_FEATURE_ALLOWED_BY_UID_VISIBLE = 3; - public static final int FGS_FEATURE_ALLOWED_BY_FLAG = 4; - public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_UID = 5; - public static final int FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 6; - public static final int FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION = 7; - public static final int FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN = 8; - public static final int FGS_FEATURE_ALLOWED_BY_FGS_TOKEN = 9; - public static final int FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION = 10; - public static final int FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION = 12; - public static final int FGS_FEATURE_ALLOWED_BY_ALLOWLIST = 13; - public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER = 14; - public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST = 15; - public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION = 16; - public static final int FGS_FEATURE_ALLOWED_BY_FGS_BINDING = 17; - public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE = 18; - public static final int FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD = 19; - public static final int FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES = 20; - public static final int FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER = 21; - public static final int FGS_FEATURE_ALLOWED_BY_COMPANION_APP = 22; - public static final int FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER = 23; - - @IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = { - FGS_FEATURE_DENIED, - FGS_FEATURE_ALLOWED_BY_UID_STATE, - FGS_FEATURE_ALLOWED_BY_PROC_STATE, - FGS_FEATURE_ALLOWED_BY_UID_VISIBLE, - FGS_FEATURE_ALLOWED_BY_FLAG, - FGS_FEATURE_ALLOWED_BY_SYSTEM_UID, - FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION, - FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION, - FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN, - FGS_FEATURE_ALLOWED_BY_FGS_TOKEN, - FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION, - FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION, - FGS_FEATURE_ALLOWED_BY_ALLOWLIST, - FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER, - FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST, - FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION, - FGS_FEATURE_ALLOWED_BY_FGS_BINDING, - FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE, - FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD, - FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES, - FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER, - FGS_FEATURE_ALLOWED_BY_COMPANION_APP, - FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER - }) - @Retention(RetentionPolicy.SOURCE) - public @interface FgsFeatureRetCode {} - // How long we wait for a service to finish executing. static final int SERVICE_TIMEOUT = 20*1000; @@ -274,7 +247,7 @@ public final class ActiveServices { AppWidgetManagerInternal mAppWidgetManagerInternal; - // white listed packageName. + // allowlisted packageName. ArraySet<String> mAllowListWhileInUsePermissionInFgs = new ArraySet<>(); // TODO: remove this after feature development is done @@ -674,7 +647,7 @@ public final class ActiveServices { if (fgRequired) { logFgsBackgroundStart(r); - if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) { + if (r.mAllowStartForeground == REASON_DENIED && isBgFgsRestrictionEnabled(r)) { String msg = "startForegroundService() not allowed due to " + "mAllowStartForeground false: service " + r.shortInstanceName; @@ -693,7 +666,7 @@ public final class ActiveServices { + "could not resolve client package " + callingPackage); } if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) { - throw new IllegalStateException(msg); + throw new ForegroundServiceStartNotAllowedException(msg); } return null; } @@ -1767,8 +1740,7 @@ public final class ActiveServices { if (!ignoreForeground) { logFgsBackgroundStart(r); - if (r.mAllowStartForeground == FGS_FEATURE_DENIED - && isBgFgsRestrictionEnabled(r)) { + if (r.mAllowStartForeground == REASON_DENIED && isBgFgsRestrictionEnabled(r)) { final String msg = "Service.startForeground() not allowed due to " + "mAllowStartForeground false: service " + r.shortInstanceName; @@ -1778,7 +1750,7 @@ public final class ActiveServices { ignoreForeground = true; if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, r.appInfo.uid)) { - throw new IllegalStateException(msg); + throw new ForegroundServiceStartNotAllowedException(msg); } } } @@ -2111,7 +2083,8 @@ public final class ActiveServices { private final AppOpsManager.OnOpNotedListener mOpNotedCallback = new AppOpsManager.OnOpNotedListener() { @Override - public void onOpNoted(int op, int uid, String pkgName, int flags, int result) { + public void onOpNoted(int op, int uid, String pkgName, + String attributionTag, int flags, int result) { incrementOpCountIfNeeded(op, uid, result); } }; @@ -2119,7 +2092,8 @@ public final class ActiveServices { private final AppOpsManager.OnOpStartedListener mOpStartedCallback = new AppOpsManager.OnOpStartedListener() { @Override - public void onOpStarted(int op, int uid, String pkgName, int flags, + public void onOpStarted(int op, int uid, String pkgName, + String attributionTag, int flags, int result) { incrementOpCountIfNeeded(op, uid, result); } @@ -2247,7 +2221,7 @@ public final class ActiveServices { psr.mAllowlistManager = false; for (int i = psr.numberOfRunningServices() - 1; i >= 0; i--) { ServiceRecord sr = psr.getRunningServiceAt(i); - if (sr.whitelistManager) { + if (sr.allowlistManager) { psr.mAllowlistManager = true; break; } @@ -2258,7 +2232,7 @@ public final class ActiveServices { final ProcessServiceRecord psr = service.app.mServices; psr.stopService(service); psr.updateBoundClientUids(); - if (service.whitelistManager) { + if (service.allowlistManager) { updateAllowlistManagerLocked(psr); } } @@ -2480,7 +2454,7 @@ public final class ActiveServices { clientPsr.setHasAboveClient(true); } if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { - s.whitelistManager = true; + s.allowlistManager = true; } if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) { s.setAllowedBgActivityStartsByBinding(true); @@ -2517,7 +2491,7 @@ public final class ActiveServices { if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { servicePsr.setTreatLikeActivity(true); } - if (s.whitelistManager) { + if (s.allowlistManager) { servicePsr.mAllowlistManager = true; } // This could have made the service more important. @@ -2946,7 +2920,6 @@ public final class ActiveServices { final ServiceRestarter res = new ServiceRestarter(); r = new ServiceRecord(mAm, className, name, definingPackageName, definingUid, filter, sInfo, callingFromFg, res); - r.mRecentCallingPackage = callingPackage; res.setService(r); smap.mServicesByInstanceName.put(name, r); smap.mServicesByIntent.put(filter, r); @@ -2975,6 +2948,8 @@ public final class ActiveServices { } } if (r != null) { + r.mRecentCallingPackage = callingPackage; + r.mRecentCallingUid = callingUid; if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid, r.packageName, r.appInfo.uid)) { String msg = "association not allowed between packages " @@ -3437,12 +3412,13 @@ public final class ActiveServices { if (r.fgRequired) { if (DEBUG_FOREGROUND_SERVICE) { - Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid) + Slog.v(TAG, "Allowlisting " + UserHandle.formatUid(r.appInfo.uid) + " for fg-service launch"); } mAm.tempAllowlistUidLocked(r.appInfo.uid, - SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch", - BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED); + SERVICE_START_FOREGROUND_TIMEOUT, PowerWhitelistManager.REASON_SERVICE_LAUNCH, + "fg-service-launch", TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + r.mRecentCallingUid); } if (!mPendingServices.contains(r)) { @@ -3548,7 +3524,7 @@ public final class ActiveServices { } } - if (r.whitelistManager) { + if (r.allowlistManager) { psr.mAllowlistManager = true; } @@ -3865,6 +3841,8 @@ public final class ActiveServices { r.name.getClassName()); stopServiceAndUpdateAllowlistManagerLocked(r); if (r.app.getThread() != null) { + // Bump the process to the top of LRU list + mAm.updateLruProcessLocked(r.app, false, null); updateServiceForegroundLocked(r.app.mServices, false); try { bumpServiceExecutingLocked(r, false, "destroy"); @@ -3938,11 +3916,11 @@ public final class ActiveServices { if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) { psr.updateHasAboveClientLocked(); } - // If this connection requested whitelist management, see if we should + // If this connection requested allowlist management, see if we should // now clear that state. if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { - s.updateWhitelistManager(); - if (!s.whitelistManager && s.app != null) { + s.updateAllowlistManager(); + if (!s.allowlistManager && s.app != null) { updateAllowlistManagerLocked(s.app.mServices); } } @@ -5397,13 +5375,13 @@ public final class ActiveServices { } if (!r.mAllowWhileInUsePermissionInFgs - || (r.mAllowStartForeground == FGS_FEATURE_DENIED)) { - final @FgsFeatureRetCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked( + || (r.mAllowStartForeground == REASON_DENIED)) { + final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked( callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts); if (!r.mAllowWhileInUsePermissionInFgs) { - r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != FGS_FEATURE_DENIED); + r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED); } - if (r.mAllowStartForeground == FGS_FEATURE_DENIED) { + if (r.mAllowStartForeground == REASON_DENIED) { r.mAllowStartForeground = shouldAllowFgsStartForegroundLocked(allowWhileInUse, callingPackage, callingPid, callingUid, intent, r, allowBackgroundActivityStarts); @@ -5417,37 +5395,37 @@ public final class ActiveServices { * @param callingPackage caller app's package name. * @param callingUid caller app's uid. * @param r the service to start. - * @return {@link FgsFeatureRetCode} + * @return {@link ReasonCode} */ - private @FgsFeatureRetCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage, + private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage, int callingPid, int callingUid, ServiceRecord r, boolean allowBackgroundActivityStarts) { - int ret = FGS_FEATURE_DENIED; + int ret = REASON_DENIED; final int uidState = mAm.getUidStateLocked(callingUid); - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { // Is the calling UID at PROCESS_STATE_TOP or above? if (uidState <= PROCESS_STATE_TOP) { - ret = FGS_FEATURE_ALLOWED_BY_UID_STATE; + ret = getReasonCodeFromProcState(uidState); } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { // Does the calling UID have any visible activity? final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid); if (isCallingUidVisible) { - ret = FGS_FEATURE_ALLOWED_BY_UID_VISIBLE; + ret = REASON_UID_VISIBLE; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { // Is the allow activity background start flag on? if (allowBackgroundActivityStarts) { - ret = FGS_FEATURE_ALLOWED_BY_FLAG; + ret = REASON_START_ACTIVITY_FLAG; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { boolean isCallerSystem = false; final int callingAppId = UserHandle.getAppId(callingUid); switch (callingAppId) { @@ -5463,15 +5441,15 @@ public final class ActiveServices { } if (isCallerSystem) { - ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_UID; + ret = REASON_SYSTEM_UID; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, pr -> { if (pr.uid == callingUid) { if (pr.getWindowProcessController().areBackgroundFgsStartsAllowed()) { - return FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER; + return REASON_ACTIVITY_STARTER; } } return null; @@ -5481,35 +5459,35 @@ public final class ActiveServices { } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (r.app != null) { ActiveInstrumentation instr = r.app.getActiveInstrumentation(); if (instr != null && instr.mHasBackgroundActivityStartsPermission) { - ret = FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION; + ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION; } } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION; + ret = REASON_BACKGROUND_ACTIVITY_PERMISSION; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { final boolean isAllowedPackage = mAllowListWhileInUsePermissionInFgs.contains(callingPackage); if (isAllowedPackage) { - ret = FGS_FEATURE_ALLOWED_BY_ALLOWLIST; + ret = REASON_ALLOWLISTED_PACKAGE; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { // Is the calling UID a device owner app? final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid); if (isDeviceOwner) { - ret = FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER; + ret = REASON_DEVICE_OWNER; } } return ret; @@ -5524,38 +5502,40 @@ public final class ActiveServices { * @param callingUid caller app's uid. * @param intent intent to start/bind service. * @param r the service to start. - * @return {@link FgsFeatureRetCode} + * @return {@link ReasonCode} */ - private @FgsFeatureRetCode int shouldAllowFgsStartForegroundLocked( - @FgsFeatureRetCode int allowWhileInUse, String callingPackage, int callingPid, + private @ReasonCode int shouldAllowFgsStartForegroundLocked( + @ReasonCode int allowWhileInUse, String callingPackage, int callingPid, int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) { int ret = allowWhileInUse; + FgsStartTempAllowList.TempFgsAllowListEntry tempAllowListReason = + r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid); final StringBuilder sb = new StringBuilder(64); final int uidState = mAm.getUidStateLocked(callingUid); - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { // Is the calling UID at PROCESS_STATE_TOP or above? if (uidState <= PROCESS_STATE_TOP) { sb.append("uidState=").append(uidState); - ret = FGS_FEATURE_ALLOWED_BY_UID_STATE; + ret = getReasonCodeFromProcState(uidState); } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, app -> { if (app.uid == callingUid) { final ProcessStateRecord state = app.mState; - if (state.getAllowedStartFgs() != FGS_FEATURE_DENIED) { + if (state.getAllowedStartFgs() != REASON_DENIED) { return state.getAllowedStartFgs(); } else if (state.isAllowedStartFgsState()) { - return FGS_FEATURE_ALLOWED_BY_PROC_STATE; + return getReasonCodeFromProcState(state.getAllowStartFgsState()); } else if (state.areBackgroundFgsStartsAllowedByToken()) { - return FGS_FEATURE_ALLOWED_BY_FGS_BINDING; + return REASON_FGS_BINDING; } else { final ActiveInstrumentation instr = app.getActiveInstrumentation(); if (instr != null && instr.mHasBackgroundForegroundServiceStartsPermission) { - return FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION; + return REASON_INSTR_BACKGROUND_FGS_PERMISSION; } } } @@ -5566,55 +5546,59 @@ public final class ActiveServices { } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (mAm.checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callingPid, callingUid) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION; + ret = REASON_BACKGROUND_FGS_PERMISSION; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (mAm.checkPermission(SYSTEM_ALERT_WINDOW, callingPid, callingUid) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION; + ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION; } } - if (ret == FGS_FEATURE_DENIED) { - if (mAm.isAllowlistedForFgsStartLOSP(callingUid)) { - // uid is on DeviceIdleController's user/system allowlist - // or AMS's FgsStartTempAllowList. - ret = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST; + if (ret == REASON_DENIED) { + FgsStartTempAllowList.TempFgsAllowListEntry entry = + mAm.isAllowlistedForFgsStartLOSP(callingUid); + if (entry != null) { + if (entry == ActivityManagerService.FAKE_TEMP_ALLOWLIST_ENTRY) { + ret = REASON_SYSTEM_ALLOW_LISTED; + } else { + ret = entry.mReasonCode; + } } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (UserManager.isDeviceInDemoMode(mAm.mContext)) { - ret = FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE; + ret = REASON_DEVICE_DEMO_MODE; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { // Is the calling UID a profile owner app? final boolean isProfileOwner = mAm.mInternal.isProfileOwner(callingUid); if (isProfileOwner) { - ret = FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER; + ret = REASON_PROFILE_OWNER; } } // NOTE this should always be the last check. - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (isPackageExemptedFromFgsRestriction(r.appInfo.packageName, r.appInfo.uid) || isPackageExemptedFromFgsRestriction(callingPackage, callingUid)) { - ret = FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES; + ret = REASON_EXEMPTED_PACKAGE; } } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { final boolean isCompanionApp = mAm.mInternal.isAssociatedCompanionApp( UserHandle.getUserId(callingUid), callingUid); if (isCompanionApp) { - ret = FGS_FEATURE_ALLOWED_BY_COMPANION_APP; + ret = REASON_COMPANION_DEVICE_MANAGER; } } @@ -5623,7 +5607,15 @@ public final class ActiveServices { + "; callingUid: " + callingUid + "; uidState: " + ProcessList.makeProcStateString(uidState) + "; intent: " + intent - + "; code:" + fgsCodeToString(ret) + + "; code:" + reasonCodeToString(ret) + + "; tempAllowListReason:<" + + (tempAllowListReason == null ? null : + (tempAllowListReason.mReason + + ",reasonCode:" + + reasonCodeToString(tempAllowListReason.mReasonCode) + + ",duration:" + tempAllowListReason.mDuration + + ",callingUid:" + tempAllowListReason.mCallingUid)) + + ">" + "; extra:" + sb.toString() + "; targetSdkVersion:" + r.appInfo.targetSdkVersion + "]"; @@ -5657,62 +5649,11 @@ public final class ActiveServices { return CompatChanges.isChangeEnabled(FGS_BG_START_USE_EXEMPTION_LIST_CHANGE_ID, uid); } - static String fgsCodeToString(@FgsFeatureRetCode int code) { - switch (code) { - case FGS_FEATURE_DENIED: - return "DENIED"; - case FGS_FEATURE_ALLOWED_BY_UID_STATE: - return "ALLOWED_BY_UID_STATE"; - case FGS_FEATURE_ALLOWED_BY_PROC_STATE: - return "ALLOWED_BY_PROC_STATE"; - case FGS_FEATURE_ALLOWED_BY_UID_VISIBLE: - return "ALLOWED_BY_UID_VISIBLE"; - case FGS_FEATURE_ALLOWED_BY_FLAG: - return "ALLOWED_BY_FLAG"; - case FGS_FEATURE_ALLOWED_BY_SYSTEM_UID: - return "ALLOWED_BY_SYSTEM_UID"; - case FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION: - return "ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION: - return "ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN: - return "ALLOWED_BY_ACTIVITY_TOKEN"; - case FGS_FEATURE_ALLOWED_BY_FGS_TOKEN: - return "ALLOWED_BY_FGS_TOKEN"; - case FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION: - return "ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION: - return "ALLOWED_BY_BACKGROUND_FGS_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_ALLOWLIST: - return "ALLOWED_BY_ALLOWLIST"; - case FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER: - return "ALLOWED_BY_DEVICE_OWNER"; - case FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST: - return "ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST"; - case FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION: - return "ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_FGS_BINDING: - return "ALLOWED_BY_FGS_BINDING"; - case FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE: - return "ALLOWED_BY_DEVICE_DEMO_MODE"; - case FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD: - return "ALLOWED_BY_PROCESS_RECORD"; - case FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES: - return "FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES"; - case FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER: - return "ALLOWED_BY_ACTIVITY_STARTER"; - case FGS_FEATURE_ALLOWED_BY_COMPANION_APP: - return "ALLOWED_BY_COMPANION_APP"; - case FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER: - return "ALLOWED_BY_PROFILE_OWNER"; - default: - return ""; - } - } - - private static boolean isFgsBgStart(@FgsFeatureRetCode int code) { - return code != FGS_FEATURE_ALLOWED_BY_UID_STATE - && code != FGS_FEATURE_ALLOWED_BY_UID_VISIBLE; + private static boolean isFgsBgStart(@ReasonCode int code) { + return code != REASON_PROC_STATE_PERSISTENT + && code != REASON_PROC_STATE_PERSISTENT_UI + && code != REASON_PROC_STATE_TOP + && code != REASON_UID_VISIBLE; } // TODO: remove this notification after feature development is done @@ -5751,10 +5692,10 @@ public final class ActiveServices { } if (!r.mLoggedInfoAllowStartForeground) { final String msg = "Background started FGS: " - + ((r.mAllowStartForeground != FGS_FEATURE_DENIED) ? "Allowed " : "Disallowed ") + + ((r.mAllowStartForeground != REASON_DENIED) ? "Allowed " : "Disallowed ") + r.mInfoAllowStartForeground; Slog.wtfQuiet(TAG, msg); - if (r.mAllowStartForeground != FGS_FEATURE_DENIED) { + if (r.mAllowStartForeground != REASON_DENIED) { Slog.i(TAG, msg); } else { Slog.w(TAG, msg); diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index 171b20c03689..9d1c83894d46 100644 --- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java +++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java @@ -68,7 +68,7 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_UID_OBSERVERS = DEBUG_ALL || false; static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false; static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false; - static final boolean DEBUG_WHITELISTS = DEBUG_ALL || false; + static final boolean DEBUG_ALLOWLISTS = DEBUG_ALL || false; static final String POSTFIX_BACKUP = (APPEND_CATEGORY_NAME) ? "_Backup" : ""; static final String POSTFIX_BROADCAST = (APPEND_CATEGORY_NAME) ? "_Broadcast" : ""; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 0b1c1154ba75..7cd494976c94 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -33,7 +33,6 @@ import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.AppOpsManager.OP_NONE; -import static android.app.BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT; import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES; import static android.content.pm.PackageManager.MATCH_ALL; @@ -50,8 +49,12 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import static android.os.IServiceManager.DUMP_FLAG_PROTO; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED; +import static android.os.PowerWhitelistManager.REASON_UNKNOWN; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.os.Process.BLUETOOTH_UID; import static android.os.Process.FIRST_APPLICATION_UID; +import static android.os.Process.INVALID_UID; import static android.os.Process.NETWORK_STACK_UID; import static android.os.Process.NFC_UID; import static android.os.Process.PHONE_UID; @@ -101,7 +104,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE; -import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALLOWLISTS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP; @@ -130,6 +133,7 @@ import static com.android.server.wm.ActivityTaskManagerService.DUMP_LASTANR_TRAC import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_CMD; import static com.android.server.wm.ActivityTaskManagerService.DUMP_RECENTS_SHORT_CMD; import static com.android.server.wm.ActivityTaskManagerService.DUMP_STARTER_CMD; +import static com.android.server.wm.ActivityTaskManagerService.DUMP_TOP_RESUMED_ACTIVITY; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.ActivityTaskManagerService.relaunchReasonToString; @@ -141,6 +145,8 @@ import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityClient; import android.app.ActivityManager; +import android.app.ActivityManager.PendingIntentInfo; +import android.app.ActivityManager.ProcessCapability; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManagerInternal; import android.app.ActivityTaskManager.RootTaskInfo; @@ -172,6 +178,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.ProcessMemoryState; import android.app.ProfilerInfo; +import android.app.PropertyInvalidatedCache; import android.app.WaitResult; import android.app.backup.BackupManager.OperationType; import android.app.backup.IBackupManager; @@ -180,7 +187,6 @@ import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; -import android.compat.Compatibility; import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; @@ -250,6 +256,7 @@ import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.PowerWhitelistManager.TempAllowListType; import android.os.Process; import android.os.RemoteCallback; @@ -304,8 +311,8 @@ import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.app.ProcessMap; import com.android.internal.app.SystemUserHomeActivity; +import com.android.internal.app.procstats.ProcessState; import com.android.internal.app.procstats.ProcessStats; -import com.android.internal.compat.CompatibilityChangeConfig; import com.android.internal.content.PackageHelper; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; @@ -353,6 +360,7 @@ import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.firewall.IntentFirewall; import com.android.server.graphics.fonts.FontManagerInternal; import com.android.server.job.JobSchedulerInternal; +import com.android.server.os.NativeTombstoneManager; import com.android.server.pm.Installer; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.uri.GrantUri; @@ -621,7 +629,7 @@ public class ActivityManagerService extends IActivityManager.Stub */ private volatile String mDeviceOwnerName; - private volatile int mDeviceOwnerUid = Process.INVALID_UID; + private volatile int mDeviceOwnerUid = INVALID_UID; /** * Map userId to its companion app uids. @@ -1147,13 +1155,13 @@ public class ActivityManagerService extends IActivityManager.Stub DeviceIdleInternal mLocalDeviceIdleController; /** - * Power-save whitelisted app-ids (not including except-idle-whitelisted ones). + * Power-save allowlisted app-ids (not including except-idle-allowlisted ones). */ @CompositeRWLock({"this", "mProcLock"}) int[] mDeviceIdleAllowlist = new int[0]; /** - * Power-save whitelisted app-ids (including except-idle-whitelisted ones). + * Power-save allowlisted app-ids (including except-idle-allowlisted ones). */ @CompositeRWLock({"this", "mProcLock"}) int[] mDeviceIdleExceptIdleAllowlist = new int[0]; @@ -1169,20 +1177,27 @@ public class ActivityManagerService extends IActivityManager.Stub final long duration; final String tag; final int type; + final @ReasonCode int reasonCode; - PendingTempAllowlist(int targetUid, long duration, String tag, int type) { + PendingTempAllowlist(int targetUid, long duration, @ReasonCode int reasonCode, String tag, + int type) { this.targetUid = targetUid; this.duration = duration; this.tag = tag; this.type = type; + this.reasonCode = reasonCode; } void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); - proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid); - proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS, duration); + proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID, + targetUid); + proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS, + duration); proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TAG, tag); proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TYPE, type); + proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.REASON_CODE, + reasonCode); proto.end(token); } } @@ -1196,6 +1211,9 @@ public class ActivityManagerService extends IActivityManager.Stub @CompositeRWLock({"this", "mProcLock"}) final FgsStartTempAllowList mFgsStartTempAllowList = new FgsStartTempAllowList(); + static final FgsStartTempAllowList.TempFgsAllowListEntry FAKE_TEMP_ALLOWLIST_ENTRY = new + FgsStartTempAllowList.TempFgsAllowListEntry(Long.MAX_VALUE, Long.MAX_VALUE, + REASON_SYSTEM_ALLOW_LISTED, "", INVALID_UID); /** * Information about and control over application operations */ @@ -1817,6 +1835,15 @@ public class ActivityManagerService extends IActivityManager.Stub ncl.start(); } + /** + * Sets a policy for handling app ops. + * + * @param appOpsPolicy The policy. + */ + public void setAppOpsPolicy(@Nullable CheckOpsDelegate appOpsPolicy) { + mAppOpsService.setAppOpsPolicy(appOpsPolicy); + } + public IAppOpsService getAppOpsService() { return mAppOpsService; } @@ -3776,10 +3803,11 @@ public class ActivityManagerService extends IActivityManager.Stub mi.getTotalUss(), mi.getTotalRss(), false, ProcessStats.ADD_PSS_EXTERNAL_SLOW, duration); proc.getPkgList().forEachPackageProcessStats(holder -> { + final ProcessState state = holder.state; FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED, proc.info.uid, - holder.state.getName(), - holder.state.getPackage(), + state != null ? state.getName() : proc.processName, + state != null ? state.getPackage() : proc.info.packageName, mi.getTotalPss(), mi.getTotalUss(), mi.getTotalRss(), @@ -4815,12 +4843,12 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public int sendIntentSender(IIntentSender target, IBinder whitelistToken, int code, + public int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { if (target instanceof PendingIntentRecord) { return ((PendingIntentRecord)target).sendWithResult(code, intent, resolvedType, - whitelistToken, finishedReceiver, requiredPermission, options); + allowlistToken, finishedReceiver, requiredPermission, options); } else { if (intent == null) { // Weird case: someone has given us their own custom IIntentSender, and now @@ -4832,7 +4860,7 @@ public class ActivityManagerService extends IActivityManager.Stub intent = new Intent(Intent.ACTION_MAIN); } try { - target.send(code, intent, resolvedType, whitelistToken, null, + target.send(code, intent, resolvedType, allowlistToken, null, requiredPermission, options); } catch (RemoteException e) { } @@ -4857,19 +4885,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public String getPackageForIntentSender(IIntentSender pendingResult) { - if (!(pendingResult instanceof PendingIntentRecord)) { - return null; - } - try { - PendingIntentRecord res = (PendingIntentRecord)pendingResult; - return res.key.packageName; - } catch (ClassCastException e) { - } - return null; - } - - @Override public void registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) { mPendingIntentController.registerIntentSenderCancelListener(sender, receiver); } @@ -4881,15 +4896,17 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public int getUidForIntentSender(IIntentSender sender) { + public PendingIntentInfo getInfoForIntentSender(IIntentSender sender) { if (sender instanceof PendingIntentRecord) { - try { - PendingIntentRecord res = (PendingIntentRecord)sender; - return res.uid; - } catch (ClassCastException e) { - } + PendingIntentRecord res = (PendingIntentRecord) sender; + return new PendingIntentInfo( + res.key.packageName, + res.uid, + (res.key.flags & PendingIntent.FLAG_IMMUTABLE) != 0, + res.key.type); + } else { + throw new IllegalArgumentException(); } - return -1; } @Override @@ -4915,15 +4932,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public boolean isIntentSenderImmutable(IIntentSender pendingResult) { - if (pendingResult instanceof PendingIntentRecord) { - final PendingIntentRecord res = (PendingIntentRecord) pendingResult; - return (res.key.flags & PendingIntent.FLAG_IMMUTABLE) != 0; - } - return false; - } - - @Override public boolean isIntentSenderAnActivity(IIntentSender pendingResult) { if (!(pendingResult instanceof PendingIntentRecord)) { return false; @@ -4940,33 +4948,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public boolean isIntentSenderAForegroundService(IIntentSender pendingResult) { - if (pendingResult instanceof PendingIntentRecord) { - final PendingIntentRecord res = (PendingIntentRecord) pendingResult; - return res.key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE; - } - return false; - } - - @Override - public boolean isIntentSenderAService(IIntentSender pendingResult) { - if (pendingResult instanceof PendingIntentRecord) { - final PendingIntentRecord res = (PendingIntentRecord) pendingResult; - return res.key.type == ActivityManager.INTENT_SENDER_SERVICE; - } - return false; - } - - @Override - public boolean isIntentSenderABroadcast(IIntentSender pendingResult) { - if (pendingResult instanceof PendingIntentRecord) { - final PendingIntentRecord res = (PendingIntentRecord) pendingResult; - return res.key.type == ActivityManager.INTENT_SENDER_BROADCAST; - } - return false; - } - - @Override public Intent getIntentForIntentSender(IIntentSender pendingResult) { enforceCallingPermission(Manifest.permission.GET_INTENT_SENDER_INTENT, "getIntentForIntentSender()"); @@ -5447,7 +5428,7 @@ public class ActivityManagerService extends IActivityManager.Stub } switch (appop) { case AppOpsManager.MODE_ALLOWED: - // If force-background-check is enabled, restrict all apps that aren't whitelisted. + // If force-background-check is enabled, restrict all apps that aren't allowlisted. if (mForceBackgroundCheck && !UserHandle.isCore(uid) && !isOnDeviceIdleAllowlistLOSP(uid, /*allowExceptIdleToo=*/ true)) { @@ -5483,7 +5464,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (uidOnBackgroundAllowlistLOSP(uid)) { if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "App " + uid + "/" + packageName - + " on background whitelist; not restricted in background"); + + " on background allowlist; not restricted in background"); } return ActivityManager.APP_START_MODE_NORMAL; } @@ -5492,7 +5473,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (isOnDeviceIdleAllowlistLOSP(uid, /*allowExceptIdleToo=*/ false)) { if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "App " + uid + "/" + packageName - + " on idle whitelist; not restricted in background"); + + " on idle allowlist; not restricted in background"); } return ActivityManager.APP_START_MODE_NORMAL; } @@ -5580,10 +5561,19 @@ public class ActivityManagerService extends IActivityManager.Stub || mPendingTempAllowlist.indexOfKey(uid) >= 0; } + /** + * Is the uid allowlisted to start FGS? + * @param uid + * @return a TempAllowListEntry if the uid is allowed. + * null if the uid is not allowed. + */ + @Nullable @GuardedBy(anyOf = {"this", "mProcLock"}) - boolean isAllowlistedForFgsStartLOSP(int uid) { - return Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, UserHandle.getAppId(uid)) >= 0 - || mFgsStartTempAllowList.isAllowed(uid); + FgsStartTempAllowList.TempFgsAllowListEntry isAllowlistedForFgsStartLOSP(int uid) { + if (Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, UserHandle.getAppId(uid)) >= 0) { + return FAKE_TEMP_ALLOWLIST_ENTRY; + } + return mFgsStartTempAllowList.getAllowedDurationAndReason(uid); } /** @@ -6055,13 +6045,13 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void backgroundWhitelistUid(final int uid) { + public void backgroundAllowlistUid(final int uid) { if (Binder.getCallingUid() != Process.SYSTEM_UID) { - throw new SecurityException("Only the OS may call backgroundWhitelistUid()"); + throw new SecurityException("Only the OS may call backgroundAllowlistUid()"); } if (DEBUG_BACKGROUND_CHECK) { - Slog.i(TAG, "Adding uid " + uid + " to bg uid whitelist"); + Slog.i(TAG, "Adding uid " + uid + " to bg uid allowlist"); } synchronized (this) { synchronized (mProcLock) { @@ -6081,10 +6071,18 @@ public class ActivityManagerService extends IActivityManager.Stub abiOverride, zygotePolicyFlags); } - // TODO: Move to ProcessList? @GuardedBy("this") final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) { + return addAppLocked(info, customProcess, isolated, disableHiddenApiChecks, + false /* disableTestApiChecks */, abiOverride, zygotePolicyFlags); + } + + // TODO: Move to ProcessList? + @GuardedBy("this") + final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, + boolean disableHiddenApiChecks, boolean disableTestApiChecks, + String abiOverride, int zygotePolicyFlags) { ProcessRecord app; if (!isolated) { app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName, @@ -6119,7 +6117,8 @@ public class ActivityManagerService extends IActivityManager.Stub mPersistentStartingProcesses.add(app); mProcessList.startProcessLocked(app, new HostingRecord("added application", customProcess != null ? customProcess : app.processName), - zygotePolicyFlags, disableHiddenApiChecks, abiOverride); + zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks, + abiOverride); } return app; @@ -6622,6 +6621,18 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public @ProcessCapability int getUidProcessCapabilities(int uid, String callingPackage) { + if (!hasUsageStatsPermission(callingPackage)) { + enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, + "getUidProcessState"); + } + + synchronized (mProcLock) { + return mProcessList.getUidProcessCapabilityLOSP(uid); + } + } + + @Override public void registerUidObserver(IUidObserver observer, int which, int cutpoint, String callingPackage) { if (!hasUsageStatsPermission(callingPackage)) { @@ -7237,6 +7248,9 @@ public class ActivityManagerService extends IActivityManager.Stub final long memoryGrowthThreshold = Math.max(totalMemoryInKb / 100, MINIMUM_MEMORY_GROWTH_THRESHOLD); mProcessList.forEachLruProcessesLOSP(false, proc -> { + if (proc.getThread() == null) { + return; + } final ProcessProfileRecord pr = proc.mProfile; final ProcessStateRecord state = proc.mState; final int setProcState = state.getSetProcState(); @@ -8273,18 +8287,23 @@ public class ActivityManagerService extends IActivityManager.Stub mUserController.handleIncomingUser(callingPid, callingUid, userId, true, ALLOW_NON_FULL, "getHistoricalProcessExitReasons", null); + NativeTombstoneManager tombstoneService = LocalServices.getService( + NativeTombstoneManager.class); + final ArrayList<ApplicationExitInfo> results = new ArrayList<ApplicationExitInfo>(); if (!TextUtils.isEmpty(packageName)) { final int uid = enforceDumpPermissionForPackage(packageName, userId, callingUid, "getHistoricalProcessExitReasons"); - if (uid != Process.INVALID_UID) { + if (uid != INVALID_UID) { mProcessList.mAppExitInfoTracker.getExitInfo( packageName, uid, pid, maxNum, results); + tombstoneService.collectTombstones(results, uid, pid, maxNum); } } else { // If no package name is given, use the caller's uid as the filter uid. mProcessList.mAppExitInfoTracker.getExitInfo( packageName, callingUid, pid, maxNum, results); + tombstoneService.collectTombstones(results, callingUid, pid, maxNum); } return new ParceledListSlice<ApplicationExitInfo>(results); @@ -8309,7 +8328,7 @@ public class ActivityManagerService extends IActivityManager.Stub int enforceDumpPermissionForPackage(String packageName, int userId, int callingUid, String function) { final long identity = Binder.clearCallingIdentity(); - int uid = Process.INVALID_UID; + int uid = INVALID_UID; try { uid = mPackageManagerInt.getPackageUid(packageName, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId); @@ -8668,7 +8687,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (DUMP_ACTIVITIES_CMD.equals(cmd) || DUMP_ACTIVITIES_SHORT_CMD.equals(cmd) || DUMP_LASTANR_CMD.equals(cmd) || DUMP_LASTANR_TRACES_CMD.equals(cmd) || DUMP_STARTER_CMD.equals(cmd) || DUMP_CONTAINERS_CMD.equals(cmd) - || DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd)) { + || DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd) + || DUMP_TOP_RESUMED_ACTIVITY.equals(cmd)) { mAtmInternal.dump( cmd, fd, pw, args, opti, true /* dumpAll */, dumpClient, dumpPackage); } else if ("binder-proxies".equals(cmd)) { @@ -9200,6 +9220,8 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(ptw.tag); pw.print(" "); pw.print(ptw.type); + pw.print(" "); + pw.print(ptw.reasonCode); } } } @@ -9855,6 +9877,10 @@ public class ActivityManagerService extends IActivityManager.Stub if (thread != null) { pw.println("\n\n** Cache info for pid " + pid + " [" + r.processName + "] **"); pw.flush(); + if (pid == MY_PID) { + PropertyInvalidatedCache.dumpCacheInfo(fd, args); + continue; + } try { TransferPipe tp = new TransferPipe(); try { @@ -10057,6 +10083,7 @@ public class ActivityManagerService extends IActivityManager.Stub ProcessList.PERSISTENT_SERVICE_ADJ, ProcessList.FOREGROUND_APP_ADJ, ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_LOW_APP_ADJ, + ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ, ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ, ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ, ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MIN_ADJ @@ -10064,7 +10091,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final String[] DUMP_MEM_OOM_LABEL = new String[] { "Native", "System", "Persistent", "Persistent Service", "Foreground", - "Visible", "Perceptible", "Perceptible Low", + "Visible", "Perceptible", "Perceptible Low", "Perceptible Medium", "Heavy Weight", "Backup", "A Services", "Home", "Previous", "B Services", "Cached" @@ -10072,7 +10099,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] { "native", "sys", "pers", "persvc", "fore", - "vis", "percept", "perceptl", + "vis", "percept", "perceptl", "perceptm", "heavy", "backup", "servicea", "home", "prev", "serviceb", "cached" @@ -10380,6 +10407,8 @@ public class ActivityManagerService extends IActivityManager.Stub } endTime = SystemClock.currentThreadTimeMillis(); hasSwapPss = mi.hasSwappedOutPss; + memtrackGraphics = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GRAPHICS); + memtrackGl = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GL); } else { reportType = ProcessStats.ADD_PSS_EXTERNAL; startTime = SystemClock.currentThreadTimeMillis(); @@ -10531,6 +10560,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (!Debug.getMemoryInfo(st.pid, info)) { return; } + memtrackGraphics = info.getOtherPrivate(Debug.MemoryInfo.OTHER_GRAPHICS); + memtrackGl = info.getOtherPrivate(Debug.MemoryInfo.OTHER_GL); } else { long pss = Debug.getPss(st.pid, tmpLong, memtrackTmp); if (pss == 0) { @@ -12548,7 +12579,7 @@ public class ActivityManagerService extends IActivityManager.Stub BroadcastOptions brOptions = null; if (bOptions != null) { brOptions = new BroadcastOptions(bOptions); - if (brOptions.getTemporaryAppWhitelistDuration() > 0) { + if (brOptions.getTemporaryAppAllowlistDuration() > 0) { // See if the caller is allowed to do this. Note we are checking against // the actual real caller (not whoever provided the operation as say a // PendingIntent), because that who is actually supplied the arguments. @@ -13549,8 +13580,6 @@ public class ActivityManagerService extends IActivityManager.Stub if (disableHiddenApiChecks || disableTestApiChecks) { enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS, "disable hidden API checks"); - - enableTestApiAccess(ai.packageName); } final long origId = Binder.clearCallingIdentity(); @@ -13568,8 +13597,8 @@ public class ActivityManagerService extends IActivityManager.Stub mUsageStatsService.reportEvent(ii.targetPackage, userId, UsageEvents.Event.SYSTEM_INTERACTION); } - app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, abiOverride, - ZYGOTE_POLICY_FLAG_EMPTY); + app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, + disableTestApiChecks, abiOverride, ZYGOTE_POLICY_FLAG_EMPTY); } app.setActiveInstrumentation(activeInstr); @@ -13724,25 +13753,6 @@ public class ActivityManagerService extends IActivityManager.Stub app.userId, "finished inst"); } - - disableTestApiAccess(app.info.packageName); - } - - private void enableTestApiAccess(String packageName) { - if (mPlatformCompat != null) { - Compatibility.ChangeConfig config = new Compatibility.ChangeConfig( - Collections.singleton(166236554L /* VMRuntime.ALLOW_TEST_API_ACCESS */), - Collections.emptySet()); - CompatibilityChangeConfig override = new CompatibilityChangeConfig(config); - mPlatformCompat.setOverridesForTest(override, packageName); - } - } - - private void disableTestApiAccess(String packageName) { - if (mPlatformCompat != null) { - mPlatformCompat.clearOverrideForTest(166236554L /* VMRuntime.ALLOW_TEST_API_ACCESS */, - packageName); - } } public void finishInstrumentation(IApplicationThread target, @@ -13951,7 +13961,7 @@ public class ActivityManagerService extends IActivityManager.Stub } void noteUidProcessState(final int uid, final int state, - final @ActivityManager.ProcessCapability int capability) { + final @ProcessCapability int capability) { mBatteryStatsService.noteUidProcessState(uid, state); mAppOpsService.updateUidProcState(uid, state, capability); if (mTrackingAssociations) { @@ -14002,6 +14012,9 @@ public class ActivityManagerService extends IActivityManager.Stub final long uptimeSince = curUptime - mLastPowerCheckUptime; mLastPowerCheckUptime = curUptime; mProcessList.forEachLruProcessesLOSP(false, app -> { + if (app.getThread() == null) { + return; + } if (app.mState.getSetProcState() >= ActivityManager.PROCESS_STATE_HOME) { int cpuLimit; long checkDur = curUptime - app.mState.getWhenUnimportant(); @@ -14103,11 +14116,12 @@ public class ActivityManagerService extends IActivityManager.Stub mBatteryStatsService.reportExcessiveCpu(app.info.uid, app.processName, uptimeSince, cputimeUsed); app.getPkgList().forEachPackageProcessStats(holder -> { + final ProcessState state = holder.state; FrameworkStatsLog.write( FrameworkStatsLog.EXCESSIVE_CPU_USAGE_REPORTED, app.info.uid, processName, - holder.state.getPackage(), + state != null ? state.getPackage() : app.info.packageName, holder.appVersion); }); return true; @@ -14447,8 +14461,8 @@ public class ActivityManagerService extends IActivityManager.Stub */ @GuardedBy("this") void tempAllowlistForPendingIntentLocked(int callerPid, int callerUid, int targetUid, - long duration, int type, String tag) { - if (DEBUG_WHITELISTS) { + long duration, int type, @ReasonCode int reasonCode, String reason) { + if (DEBUG_ALLOWLISTS) { Slog.d(TAG, "tempAllowlistForPendingIntentLocked(" + callerPid + ", " + callerUid + ", " + targetUid + ", " + duration + ", " + type + ")"); } @@ -14467,7 +14481,7 @@ public class ActivityManagerService extends IActivityManager.Stub != PackageManager.PERMISSION_GRANTED && checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callerPid, callerUid) != PackageManager.PERMISSION_GRANTED) { - if (DEBUG_WHITELISTS) { + if (DEBUG_ALLOWLISTS) { Slog.d(TAG, "tempAllowlistForPendingIntentLocked() for target " + targetUid + ": pid " + callerPid + " is not allowed"); } @@ -14476,22 +14490,23 @@ public class ActivityManagerService extends IActivityManager.Stub } } - tempAllowlistUidLocked(targetUid, duration, tag, type); + tempAllowlistUidLocked(targetUid, duration, reasonCode, reason, type, callerUid); } /** * Allowlists {@code targetUid} to temporarily bypass Power Save mode. */ @GuardedBy("this") - void tempAllowlistUidLocked(int targetUid, long duration, String tag, int type) { + void tempAllowlistUidLocked(int targetUid, long duration, @ReasonCode int reasonCode, + String reason, int type, int callingUid) { synchronized (mProcLock) { mPendingTempAllowlist.put(targetUid, - new PendingTempAllowlist(targetUid, duration, tag, type)); + new PendingTempAllowlist(targetUid, duration, reasonCode, reason, type)); setUidTempAllowlistStateLSP(targetUid, true); mUiHandler.obtainMessage(PUSH_TEMP_ALLOWLIST_UI_MSG).sendToTarget(); - if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { - mFgsStartTempAllowList.add(targetUid, duration); + if (type == TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { + mFgsStartTempAllowList.add(targetUid, duration, reasonCode, reason, callingUid); } } } @@ -14517,7 +14532,7 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i = 0; i < N; i++) { PendingTempAllowlist ptw = list[i]; mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid, - ptw.duration, ptw.type, true, ptw.tag); + ptw.duration, ptw.type, true, ptw.reasonCode, ptw.tag); } } @@ -15105,10 +15120,10 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken, - long duration, int type) { - mPendingIntentController.setPendingIntentWhitelistDuration(target, whitelistToken, - duration, type); + public void setPendingIntentAllowlistDuration(IIntentSender target, IBinder allowlistToken, + long duration, int type, @ReasonCode int reasonCode, @Nullable String reason) { + mPendingIntentController.setPendingIntentAllowlistDuration(target, allowlistToken, + duration, type, reasonCode, reason); } @Override @@ -15118,32 +15133,32 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void setPendingIntentAllowBgActivityStarts(IIntentSender target, - IBinder whitelistToken, int flags) { + IBinder allowlistToken, int flags) { if (!(target instanceof PendingIntentRecord)) { Slog.w(TAG, "setPendingIntentAllowBgActivityStarts():" + " not a PendingIntentRecord: " + target); return; } synchronized (ActivityManagerService.this) { - ((PendingIntentRecord) target).setAllowBgActivityStarts(whitelistToken, flags); + ((PendingIntentRecord) target).setAllowBgActivityStarts(allowlistToken, flags); } } @Override public void clearPendingIntentAllowBgActivityStarts(IIntentSender target, - IBinder whitelistToken) { + IBinder allowlistToken) { if (!(target instanceof PendingIntentRecord)) { Slog.w(TAG, "clearPendingIntentAllowBgActivityStarts():" + " not a PendingIntentRecord: " + target); return; } synchronized (ActivityManagerService.this) { - ((PendingIntentRecord) target).clearAllowBgActivityStarts(whitelistToken); + ((PendingIntentRecord) target).clearAllowBgActivityStarts(allowlistToken); } } @Override - public void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids) { + public void setDeviceIdleAllowlist(int[] allAppids, int[] exceptIdleAppids) { synchronized (ActivityManagerService.this) { synchronized (mProcLock) { mDeviceIdleAllowlist = allAppids; @@ -15153,17 +15168,19 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, boolean adding, - long durationMs, @TempAllowListType int type) { + public void updateDeviceIdleTempAllowlist(int[] appids, int changingUid, boolean adding, + long durationMs, @TempAllowListType int type, @ReasonCode int reasonCode, + @Nullable String reason, int callingUid) { synchronized (ActivityManagerService.this) { synchronized (mProcLock) { mDeviceIdleTempAllowlist = appids; if (adding) { - if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { - mFgsStartTempAllowList.add(changingUid, durationMs); + if (type == TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED) { + mFgsStartTempAllowList.add(changingUid, durationMs, reasonCode, reason, + callingUid); } + setAppIdTempAllowlistStateLSP(changingUid, adding); } - setAppIdTempAllowlistStateLSP(changingUid, adding); } } } @@ -15524,11 +15541,11 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid, - long duration, int type, String tag) { + public void tempAllowlistForPendingIntent(int callerPid, int callerUid, int targetUid, + long duration, int type, @ReasonCode int reasonCode, String reason) { synchronized (ActivityManagerService.this) { ActivityManagerService.this.tempAllowlistForPendingIntentLocked( - callerPid, callerUid, targetUid, duration, type, tag); + callerPid, callerUid, targetUid, duration, type, reasonCode, reason); } } diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java index 48222cb075cd..a9e557103966 100644 --- a/services/core/java/com/android/server/am/AppErrorDialog.java +++ b/services/core/java/com/android/server/am/AppErrorDialog.java @@ -18,10 +18,7 @@ package com.android.server.am; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; @@ -138,19 +135,6 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE); } - @Override - public void onStart() { - super.onStart(); - getContext().registerReceiver(mReceiver, - new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); - } - - @Override - protected void onStop() { - super.onStop(); - getContext().unregisterReceiver(mReceiver); - } - private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { setResult(msg.what); @@ -204,15 +188,6 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen } } - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { - cancel(); - } - } - }; - static class Data { AppErrorResult result; int taskId; diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index e5a5cff409b3..3602f44cd785 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -29,6 +29,7 @@ import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_N import android.app.ActivityManager; import android.app.ActivityOptions; +import android.app.AnrController; import android.app.ApplicationErrorReport; import android.app.ApplicationExitInfo; import android.content.ActivityNotFoundException; @@ -1058,7 +1059,26 @@ class AppErrors { Settings.Secure.ANR_SHOW_BACKGROUND, 0, mService.mUserController.getCurrentUserId()) != 0; if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) { - errState.getDialogController().showAnrDialogs(data); + AnrController anrController = errState.getDialogController().getAnrController(); + if (anrController == null) { + errState.getDialogController().showAnrDialogs(data); + } else { + String packageName = proc.info.packageName; + int uid = proc.info.uid; + boolean showDialog = anrController.onAnrDelayCompleted(packageName, uid); + + if (showDialog) { + Slog.d(TAG, "ANR delay completed. Showing ANR dialog for package: " + + packageName); + errState.getDialogController().showAnrDialogs(data); + } else { + Slog.d(TAG, "ANR delay completed. Cancelling ANR dialog for package: " + + packageName); + errState.setNotResponding(false); + errState.setNotRespondingReport(null); + errState.getDialogController().clearAnrDialogs(); + } + } } else { MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR, AppNotRespondingDialog.CANT_SHOW); diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java index 17be2100414f..b85d7292e738 100644 --- a/services/core/java/com/android/server/am/AppExitInfoTracker.java +++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java @@ -63,8 +63,10 @@ import com.android.internal.app.ProcessMap; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.IoThread; +import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemServiceManager; +import com.android.server.os.NativeTombstoneManager; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -78,6 +80,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiConsumer; @@ -762,6 +765,10 @@ public final class AppExitInfoTracker { * Helper function for shell command */ void clearHistoryProcessExitInfo(String packageName, int userId) { + NativeTombstoneManager tombstoneService = LocalServices.getService( + NativeTombstoneManager.class); + Optional<Integer> appId = Optional.empty(); + if (TextUtils.isEmpty(packageName)) { synchronized (mLock) { removeByUserIdLocked(userId); @@ -769,10 +776,13 @@ public final class AppExitInfoTracker { } else { final int uid = mService.mPackageManagerInt.getPackageUid(packageName, PackageManager.MATCH_ALL, userId); + appId = Optional.of(UserHandle.getAppId(uid)); synchronized (mLock) { removePackageLocked(packageName, uid, true, userId); } } + + tombstoneService.purge(Optional.of(userId), appId); schedulePersistProcessExitInfo(true); } diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java index 77d2898842c6..b233a2ccc6e3 100644 --- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java @@ -181,6 +181,11 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli } }; + @Override + protected void closeDialog() { + mHandler.obtainMessage(FORCE_CLOSE).sendToTarget(); + } + static class Data { final ProcessRecord proc; final ApplicationInfo aInfo; diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java index c8630fa52973..f8494d8a7c04 100644 --- a/services/core/java/com/android/server/am/AppProfiler.java +++ b/services/core/java/com/android/server/am/AppProfiler.java @@ -1327,6 +1327,8 @@ public class AppProfiler { // Get a list of Stats that have vsize > 0 final List<ProcessCpuTracker.Stats> stats = getCpuStats(st -> st.vsize > 0); final int statsCount = stats.size(); + long totalMemtrackGraphics = 0; + long totalMemtrackGl = 0; for (int i = 0; i < statsCount; i++) { ProcessCpuTracker.Stats st = stats.get(i); long pss = Debug.getPss(st.pid, swaptrackTmp, memtrackTmp); @@ -1337,6 +1339,8 @@ public class AppProfiler { mi.pss = pss; mi.swapPss = swaptrackTmp[1]; mi.memtrack = memtrackTmp[0]; + totalMemtrackGraphics += memtrackTmp[1]; + totalMemtrackGl += memtrackTmp[2]; memInfos.add(mi); } } @@ -1345,20 +1349,18 @@ public class AppProfiler { long totalPss = 0; long totalSwapPss = 0; long totalMemtrack = 0; - long totalMemtrackGraphics = 0; - long totalMemtrackGl = 0; for (int i = 0, size = memInfos.size(); i < size; i++) { ProcessMemInfo mi = memInfos.get(i); if (mi.pss == 0) { mi.pss = Debug.getPss(mi.pid, swaptrackTmp, memtrackTmp); mi.swapPss = swaptrackTmp[1]; mi.memtrack = memtrackTmp[0]; + totalMemtrackGraphics += memtrackTmp[1]; + totalMemtrackGl += memtrackTmp[2]; } totalPss += mi.pss; totalSwapPss += mi.swapPss; totalMemtrack += mi.memtrack; - totalMemtrackGraphics += memtrackTmp[1]; - totalMemtrackGl += memtrackTmp[2]; } Collections.sort(memInfos, new Comparator<ProcessMemInfo>() { @Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) { diff --git a/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java b/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java index 3ce24712b42f..262e79521e8c 100644 --- a/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java +++ b/services/core/java/com/android/server/am/AppWaitingForDebuggerDialog.java @@ -61,6 +61,11 @@ final class AppWaitingForDebuggerDialog extends BaseErrorDialog { public void onStop() { } + @Override + protected void closeDialog() { + /* Do nothing */ + } + private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { diff --git a/services/core/java/com/android/server/am/BaseErrorDialog.java b/services/core/java/com/android/server/am/BaseErrorDialog.java index aabb5877764e..7b5f2cd78947 100644 --- a/services/core/java/com/android/server/am/BaseErrorDialog.java +++ b/services/core/java/com/android/server/am/BaseErrorDialog.java @@ -16,16 +16,19 @@ package com.android.server.am; -import com.android.internal.R; - import android.app.AlertDialog; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.Handler; import android.os.Message; import android.view.KeyEvent; import android.view.WindowManager; import android.widget.Button; +import com.android.internal.R; + public class BaseErrorDialog extends AlertDialog { private static final int ENABLE_BUTTONS = 0; private static final int DISABLE_BUTTONS = 1; @@ -44,10 +47,19 @@ public class BaseErrorDialog extends AlertDialog { getWindow().setAttributes(attrs); } + @Override public void onStart() { super.onStart(); mHandler.sendEmptyMessage(DISABLE_BUTTONS); mHandler.sendMessageDelayed(mHandler.obtainMessage(ENABLE_BUTTONS), 1000); + getContext().registerReceiver(mReceiver, + new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + } + + @Override + protected void onStop() { + super.onStop(); + getContext().unregisterReceiver(mReceiver); } public boolean dispatchKeyEvent(KeyEvent event) { @@ -84,4 +96,24 @@ public class BaseErrorDialog extends AlertDialog { } } }; + + /** + * Called when received ACTION_CLOSE_SYSTEM_DIALOGS. + */ + protected void closeDialog() { + if (mCancelable) { + cancel(); + } else { + dismiss(); + } + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { + closeDialog(); + } + } + }; } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 6500f6d1b6d3..167c2b1ad66c 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -216,6 +216,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub return; } + if (results == null) return; + for (int i = 0; i < results.length; i++) { final StateResidencyResult result = results[i]; RpmStats.PowerStateSubsystem subsystem = @@ -257,7 +259,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub return EMPTY; } - if (results.length == 0) return EMPTY; + if (results == null || results.length == 0) return EMPTY; int charsLeft = MAX_LOW_POWER_STATS_SIZE; StringBuilder builder = new StringBuilder("SubsystemPowerState"); diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 8b6fabd7faff..29061930cd84 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -22,6 +22,7 @@ import static android.text.TextUtils.formatSimple; import static com.android.server.am.ActivityManagerDebugConfig.*; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -43,6 +44,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerWhitelistManager; import android.os.PowerWhitelistManager.TempAllowListType; import android.os.Process; import android.os.RemoteException; @@ -825,7 +827,8 @@ public final class BroadcastQueue { r.resultExtras, r.ordered, r.initialSticky, r.userId); // parallel broadcasts are fire-and-forget, not bookended by a call to // finishReceiverLocked(), so we manage their activity-start token here - if (r.allowBackgroundActivityStarts && !r.ordered) { + if (filter.receiverList.app != null + && r.allowBackgroundActivityStarts && !r.ordered) { postActivityStartTokenRemoval(filter.receiverList.app, r); } } @@ -902,8 +905,9 @@ public final class BroadcastQueue { return false; } - final void scheduleTempWhitelistLocked(int uid, long duration, BroadcastRecord r, - @TempAllowListType int type) { + final void scheduleTempAllowlistLocked(int uid, long duration, BroadcastRecord r, + @TempAllowListType int type, @PowerWhitelistManager.ReasonCode int reasonCode, + @Nullable String reason) { if (duration > Integer.MAX_VALUE) { duration = Integer.MAX_VALUE; } @@ -925,10 +929,11 @@ public final class BroadcastQueue { b.append(r.intent.getData()); } if (DEBUG_BROADCAST) { - Slog.v(TAG, "Broadcast temp whitelist uid=" + uid + " duration=" + duration + Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration + " type=" + type + " : " + b.toString()); } - mService.tempAllowlistUidLocked(uid, duration, b.toString(), type); + mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type, + r.callingUid); } /** @@ -1331,10 +1336,12 @@ public final class BroadcastQueue { // r is guaranteed ordered at this point, so we know finishReceiverLocked() // will get a callback and handle the activity start token lifecycle. } - if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) { - scheduleTempWhitelistLocked(filter.owningUid, - brOptions.getTemporaryAppWhitelistDuration(), r, - brOptions.getTemporaryAppWhitelistType()); + if (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0) { + scheduleTempAllowlistLocked(filter.owningUid, + brOptions.getTemporaryAppAllowlistDuration(), r, + brOptions.getTemporaryAppAllowlistType(), + brOptions.getTemporaryAppAllowlistReasonCode(), + brOptions.getTemporaryAppAllowlistReason()); } } return; @@ -1618,11 +1625,13 @@ public final class BroadcastQueue { } final boolean isActivityCapable = - (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0); + (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0); if (isActivityCapable) { - scheduleTempWhitelistLocked(receiverUid, - brOptions.getTemporaryAppWhitelistDuration(), r, - brOptions.getTemporaryAppWhitelistType()); + scheduleTempAllowlistLocked(receiverUid, + brOptions.getTemporaryAppAllowlistDuration(), r, + brOptions.getTemporaryAppAllowlistType(), + brOptions.getTemporaryAppAllowlistReasonCode(), + brOptions.getTemporaryAppAllowlistReason()); } // Broadcast is being executed, its package can't be stopped. diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 7bdf43c9c744..c5f082a0f9e3 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -66,6 +66,10 @@ public final class CachedAppOptimizer { @VisibleForTesting static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4"; @VisibleForTesting static final String KEY_COMPACT_THROTTLE_5 = "compact_throttle_5"; @VisibleForTesting static final String KEY_COMPACT_THROTTLE_6 = "compact_throttle_6"; + @VisibleForTesting static final String KEY_COMPACT_THROTTLE_MIN_OOM_ADJ = + "compact_throttle_min_oom_adj"; + @VisibleForTesting static final String KEY_COMPACT_THROTTLE_MAX_OOM_ADJ = + "compact_throttle_max_oom_adj"; @VisibleForTesting static final String KEY_COMPACT_STATSD_SAMPLE_RATE = "compact_statsd_sample_rate"; @VisibleForTesting static final String KEY_FREEZER_STATSD_SAMPLE_RATE = @@ -101,6 +105,10 @@ public final class CachedAppOptimizer { @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000; @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_5 = 10 * 60 * 1000; @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_6 = 10 * 60 * 1000; + @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ = + ProcessList.CACHED_APP_MIN_ADJ; + @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ = + ProcessList.CACHED_APP_MAX_ADJ; // The sampling rate to push app compaction events into statsd for upload. @VisibleForTesting static final float DEFAULT_STATSD_SAMPLE_RATE = 0.1f; @VisibleForTesting static final long DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB = 12_000L; @@ -186,6 +194,10 @@ public final class CachedAppOptimizer { updateFullDeltaRssThrottle(); } else if (KEY_COMPACT_PROC_STATE_THROTTLE.equals(name)) { updateProcStateThrottle(); + } else if (KEY_COMPACT_THROTTLE_MIN_OOM_ADJ.equals(name)) { + updateMinOomAdjThrottle(); + } else if (KEY_COMPACT_THROTTLE_MAX_OOM_ADJ.equals(name)) { + updateMaxOomAdjThrottle(); } } } @@ -217,6 +229,12 @@ public final class CachedAppOptimizer { @GuardedBy("mPhenotypeFlagLock") @VisibleForTesting volatile long mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6; @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting volatile long mCompactThrottleMinOomAdj = + DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ; + @GuardedBy("mPhenotypeFlagLock") + @VisibleForTesting volatile long mCompactThrottleMaxOomAdj = + DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ; + @GuardedBy("mPhenotypeFlagLock") private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION; private volatile boolean mUseFreezer = DEFAULT_USE_FREEZER; @GuardedBy("this") @@ -282,6 +300,7 @@ public final class CachedAppOptimizer { * starts the background thread if necessary. */ public void init() { + // TODO: initialize flags to default and only update them if values are set in DeviceConfig DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener); synchronized (mPhenotypeFlagLock) { @@ -294,6 +313,8 @@ public final class CachedAppOptimizer { updateFullDeltaRssThrottle(); updateProcStateThrottle(); updateUseFreezer(); + updateMinOomAdjThrottle(); + updateMaxOomAdjThrottle(); } } @@ -328,6 +349,8 @@ public final class CachedAppOptimizer { pw.println(" " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull); pw.println(" " + KEY_COMPACT_THROTTLE_5 + "=" + mCompactThrottleBFGS); pw.println(" " + KEY_COMPACT_THROTTLE_6 + "=" + mCompactThrottlePersistent); + pw.println(" " + KEY_COMPACT_THROTTLE_MIN_OOM_ADJ + "=" + mCompactThrottleMinOomAdj); + pw.println(" " + KEY_COMPACT_THROTTLE_MAX_OOM_ADJ + "=" + mCompactThrottleMaxOomAdj); pw.println(" " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mCompactStatsdSampleRate); pw.println(" " + KEY_COMPACT_FULL_RSS_THROTTLE_KB + "=" + mFullAnonRssThrottleKb); @@ -367,12 +390,23 @@ public final class CachedAppOptimizer { @GuardedBy("mProcLock") void compactAppFull(ProcessRecord app) { - app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL); - mPendingCompactionProcesses.add(app); - mCompactionHandler.sendMessage( + // Apply OOM adj score throttle for Full App Compaction. + if ((app.mState.getSetAdj() < mCompactThrottleMinOomAdj + || app.mState.getSetAdj() > mCompactThrottleMaxOomAdj) + && app.mState.getCurAdj() >= mCompactThrottleMinOomAdj + && app.mState.getCurAdj() <= mCompactThrottleMaxOomAdj) { + app.mOptRecord.setReqCompactAction(COMPACT_PROCESS_FULL); + mPendingCompactionProcesses.add(app); + mCompactionHandler.sendMessage( mCompactionHandler.obtainMessage( - COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState())); - + COMPACT_PROCESS_MSG, app.mState.getSetAdj(), app.mState.getSetProcState())); + } else { + if (DEBUG_COMPACTION) { + Slog.d(TAG_AM, "Skipping full compaction for " + app.processName + + " oom adj score changed from " + app.mState.getSetAdj() + + " to " + app.mState.getCurAdj()); + } + } } @GuardedBy("mProcLock") @@ -502,18 +536,6 @@ public final class CachedAppOptimizer { } /** - * Enable or disable the freezer. When enable == false all frozen processes are unfrozen, - * but aren't removed from the freezer. While in this state, processes can be added or removed - * by using Process.setProcessFrozen(), but they wouldn't be actually frozen until the freezer - * is enabled. If enable == true all processes in the freezer are frozen. - * - * @param enable Specify whether to enable (true) or disable (false) the freezer. - * - * @hide - */ - private static native void enableFreezerInternal(boolean enable); - - /** * Informs binder that a process is about to be frozen. If freezer is enabled on a process via * this method, this method will synchronously dispatch all pending transactions to the * specified pid. This method will not add significant latencies when unfreezing. @@ -556,10 +578,6 @@ public final class CachedAppOptimizer { if (state == '1' || state == '0') { supported = true; - // This is a workaround after reverting the cgroup v2 uid/pid hierarchy due to - // http://b/179006802. - // TODO: remove once the uid/pid hierarchy is restored - enableFreezerInternal(true); } else { Slog.e(TAG_AM, "unexpected value in cgroup.freeze"); } @@ -629,6 +647,7 @@ public final class CachedAppOptimizer { @GuardedBy("mPhenotypeFlagLock") private void updateCompactionThrottles() { boolean useThrottleDefaults = false; + // TODO: improve efficiency by calling DeviceConfig only once for all flags. String throttleSomeSomeFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_1); @@ -647,12 +666,20 @@ public final class CachedAppOptimizer { String throttlePersistentFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_6); + String throttleMinOomAdjFlag = + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_COMPACT_THROTTLE_MIN_OOM_ADJ); + String throttleMaxOomAdjFlag = + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_COMPACT_THROTTLE_MAX_OOM_ADJ); if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag) || TextUtils.isEmpty(throttleFullSomeFlag) || TextUtils.isEmpty(throttleFullFullFlag) || TextUtils.isEmpty(throttleBFGSFlag) - || TextUtils.isEmpty(throttlePersistentFlag)) { + || TextUtils.isEmpty(throttlePersistentFlag) + || TextUtils.isEmpty(throttleMinOomAdjFlag) + || TextUtils.isEmpty(throttleMaxOomAdjFlag)) { // Set defaults for all if any are not set. useThrottleDefaults = true; } else { @@ -663,6 +690,8 @@ public final class CachedAppOptimizer { mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag); mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag); mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag); + mCompactThrottleMinOomAdj = Long.parseLong(throttleMinOomAdjFlag); + mCompactThrottleMaxOomAdj = Long.parseLong(throttleMaxOomAdjFlag); } catch (NumberFormatException e) { useThrottleDefaults = true; } @@ -675,6 +704,8 @@ public final class CachedAppOptimizer { mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4; mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5; mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6; + mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ; + mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ; } } @@ -729,6 +760,28 @@ public final class CachedAppOptimizer { } } + @GuardedBy("mPhenotypeFlagLock") + private void updateMinOomAdjThrottle() { + mCompactThrottleMinOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ); + + // Should only compact cached processes. + if (mCompactThrottleMinOomAdj < ProcessList.CACHED_APP_MIN_ADJ) { + mCompactThrottleMinOomAdj = DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ; + } + } + + @GuardedBy("mPhenotypeFlagLock") + private void updateMaxOomAdjThrottle() { + mCompactThrottleMaxOomAdj = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ); + + // Should only compact cached processes. + if (mCompactThrottleMaxOomAdj > ProcessList.CACHED_APP_MAX_ADJ) { + mCompactThrottleMaxOomAdj = DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ; + } + } + private boolean parseProcStateThrottle(String procStateThrottleString) { String[] procStates = TextUtils.split(procStateThrottleString, ","); mProcStateThrottle.clear(); diff --git a/services/core/java/com/android/server/am/ErrorDialogController.java b/services/core/java/com/android/server/am/ErrorDialogController.java index ef135d55e43c..f23d309e424a 100644 --- a/services/core/java/com/android/server/am/ErrorDialogController.java +++ b/services/core/java/com/android/server/am/ErrorDialogController.java @@ -16,6 +16,8 @@ package com.android.server.am; +import android.annotation.Nullable; +import android.app.AnrController; import android.app.Dialog; import android.content.Context; @@ -57,6 +59,13 @@ final class ErrorDialogController { @GuardedBy("mProcLock") private AppWaitingForDebuggerDialog mWaitDialog; + /** + * ANR dialog controller + */ + @GuardedBy("mProcLock") + @Nullable + private AnrController mAnrController; + @GuardedBy("mProcLock") boolean hasCrashDialogs() { return mCrashDialogs != null; @@ -118,6 +127,7 @@ final class ErrorDialogController { } forAllDialogs(mAnrDialogs, Dialog::dismiss); mAnrDialogs = null; + mAnrController = null; } @GuardedBy("mProcLock") @@ -220,6 +230,17 @@ final class ErrorDialogController { }); } + @GuardedBy("mProcLock") + @Nullable + AnrController getAnrController() { + return mAnrController; + } + + @GuardedBy("mProcLock") + void setAnrController(AnrController controller) { + mAnrController = controller; + } + /** * Helper function to collect contexts from crashed app located displays. * diff --git a/services/core/java/com/android/server/am/FgsStartTempAllowList.java b/services/core/java/com/android/server/am/FgsStartTempAllowList.java index 4d8749c05294..1f897b5928ce 100644 --- a/services/core/java/com/android/server/am/FgsStartTempAllowList.java +++ b/services/core/java/com/android/server/am/FgsStartTempAllowList.java @@ -18,24 +18,53 @@ package com.android.server.am; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import android.annotation.Nullable; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.SystemClock; import android.util.Slog; -import android.util.SparseLongArray; +import android.util.SparseArray; /** * List of uids that are temporarily allowed to start FGS from background. */ final class FgsStartTempAllowList { private static final int MAX_SIZE = 100; + + public static final class TempFgsAllowListEntry { + final long mExpirationTime; + final long mDuration; + final @ReasonCode int mReasonCode; + final String mReason; + final int mCallingUid; + + TempFgsAllowListEntry(long expirationTime, long duration, @ReasonCode int reasonCode, + String reason, int callingUid) { + mExpirationTime = expirationTime; + mDuration = duration; + mReasonCode = reasonCode; + mReason = reason; + mCallingUid = callingUid; + } + } + /** - * The key is the uid, the value is expiration elapse time in ms of this temp-allowed uid. + * The key is the uid, the value is a TempAllowListEntry. */ - private final SparseLongArray mTempAllowListFgs = new SparseLongArray(); + private final SparseArray<TempFgsAllowListEntry> mTempAllowListFgs = new SparseArray<>(); FgsStartTempAllowList() { } - void add(int uid, long duration) { + /** + * Add a uid and its duration with reason into the FGS temp-allowlist. + * @param uid + * @param duration temp-allowlisted duration in milliseconds. + * @param reason A human-readable reason for logging purposes. + * @param callingUid the callingUid that setup this temp allowlist, only valid when param adding + * is true. + */ + void add(int uid, long duration, @ReasonCode int reasonCode, @Nullable String reason, + int callingUid) { if (duration <= 0) { Slog.e(TAG_AM, "FgsStartTempAllowList bad duration:" + duration + " uid: " + uid); @@ -48,26 +77,36 @@ final class FgsStartTempAllowList { } final long now = SystemClock.elapsedRealtime(); for (int index = mTempAllowListFgs.size() - 1; index >= 0; index--) { - if (mTempAllowListFgs.valueAt(index) < now) { + if (mTempAllowListFgs.valueAt(index).mExpirationTime < now) { mTempAllowListFgs.removeAt(index); } } - final long existingExpirationTime = mTempAllowListFgs.get(uid, -1); + final TempFgsAllowListEntry existing = mTempAllowListFgs.get(uid); final long expirationTime = now + duration; - if (existingExpirationTime == -1 || existingExpirationTime < expirationTime) { - mTempAllowListFgs.put(uid, expirationTime); + if (existing == null || existing.mExpirationTime < expirationTime) { + mTempAllowListFgs.put(uid, + new TempFgsAllowListEntry(expirationTime, duration, reasonCode, + reason == null ? "" : reason, callingUid)); } } - boolean isAllowed(int uid) { + /** + * Is this uid temp-allowlisted to start FGS. + * @param uid + * @return If uid is in the temp-allowlist, return the {@link TempFgsAllowListEntry}; If not in + * temp-allowlist, return null. + */ + @Nullable + TempFgsAllowListEntry getAllowedDurationAndReason(int uid) { final int index = mTempAllowListFgs.indexOfKey(uid); if (index < 0) { - return false; - } else if (mTempAllowListFgs.valueAt(index) < SystemClock.elapsedRealtime()) { + return null; + } else if (mTempAllowListFgs.valueAt(index).mExpirationTime + < SystemClock.elapsedRealtime()) { mTempAllowListFgs.removeAt(index); - return false; + return null; } else { - return true; + return mTempAllowListFgs.valueAt(index); } } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index d79fb8a265e8..06e879b549af 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -21,6 +21,7 @@ import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; +import static android.app.ActivityManager.PROCESS_CAPABILITY_NETWORK; import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; @@ -41,6 +42,7 @@ import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; +import static android.os.PowerWhitelistManager.REASON_DENIED; import static android.os.Process.SCHED_OTHER; import static android.os.Process.THREAD_GROUP_BACKGROUND; import static android.os.Process.THREAD_GROUP_DEFAULT; @@ -51,7 +53,6 @@ import static android.os.Process.setProcessGroup; import static android.os.Process.setThreadPriority; import static android.os.Process.setThreadScheduler; -import static com.android.server.am.ActiveServices.FGS_FEATURE_DENIED; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; @@ -72,6 +73,7 @@ import static com.android.server.am.AppProfiler.TAG_PSS; import static com.android.server.am.ProcessList.TAG_PROCESS_OBSERVERS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.ApplicationExitInfo; @@ -229,6 +231,22 @@ public final class OomAdjuster { private final ArrayDeque<ProcessRecord> mTmpQueue; private final ArraySet<ProcessRecord> mPendingProcessSet = new ArraySet<>(); + /** + * Flag to mark if there is an ongoing oomAdjUpdate: potentially the oomAdjUpdate + * could be called recursively because of the indirect calls during the update; + * however the oomAdjUpdate itself doesn't support recursion - in this case we'd + * have to queue up the new targets found during the update, and perform another + * round of oomAdjUpdate at the end of last update. + */ + @GuardedBy("mService") + private boolean mOomAdjUpdateOngoing = false; + + /** + * Flag to mark if there is a pending full oomAdjUpdate. + */ + @GuardedBy("mService") + private boolean mPendingFullOomAdjUpdate = false; + private final PlatformCompatCache mPlatformCompatCache; private static class PlatformCompatCache { @@ -439,6 +457,23 @@ public final class OomAdjuster { if (oomAdjAll && mConstants.OOMADJ_UPDATE_QUICK) { return updateOomAdjLSP(app, oomAdjReason); } + if (checkAndEnqueueOomAdjTargetLocked(app)) { + // Simply return true as there is an oomAdjUpdate ongoing + return true; + } + try { + mOomAdjUpdateOngoing = true; + return performUpdateOomAdjLSP(app, oomAdjAll, oomAdjReason); + } finally { + // Kick off the handling of any pending targets enqueued during the above update + mOomAdjUpdateOngoing = false; + updateOomAdjPendingTargetsLocked(oomAdjReason); + } + } + + @GuardedBy({"mService", "mProcLock"}) + private boolean performUpdateOomAdjLSP(ProcessRecord app, boolean oomAdjAll, + String oomAdjReason) { final ProcessRecord topApp = mService.getTopApp(); final ProcessStateRecord state = app.mState; final boolean wasCached = state.isCached(); @@ -453,20 +488,21 @@ public final class OomAdjuster { ? state.getCurRawAdj() : ProcessList.UNKNOWN_ADJ; // Check if this process is in the pending list too, remove from pending list if so. mPendingProcessSet.remove(app); - boolean success = updateOomAdjLSP(app, cachedAdj, topApp, false, + boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp, false, SystemClock.uptimeMillis()); if (oomAdjAll && (wasCached != state.isCached() || state.getCurRawAdj() == ProcessList.UNKNOWN_ADJ)) { // Changed to/from cached state, so apps after it in the LRU // list may also be changed. - updateOomAdjLSP(oomAdjReason); + performUpdateOomAdjLSP(oomAdjReason); } + return success; } @GuardedBy({"mService", "mProcLock"}) - private boolean updateOomAdjLSP(ProcessRecord app, int cachedAdj, + private boolean performUpdateOomAdjLSP(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP, boolean doingAll, long now) { if (app.getThread() == null) { return false; @@ -486,8 +522,6 @@ public final class OomAdjuster { computeOomAdjLSP(app, cachedAdj, TOP_APP, doingAll, now, false, true); - boolean success = applyOomAdjLSP(app, doingAll, now, SystemClock.elapsedRealtime()); - if (uidRec != null) { // After uidRec.reset() above, for UidRecord with multiple processes (ProcessRecord), // we need to apply all ProcessRecord into UidRecord. @@ -504,7 +538,7 @@ public final class OomAdjuster { } } - return success; + return applyOomAdjLSP(app, doingAll, now, SystemClock.elapsedRealtime()); } /** @@ -519,6 +553,22 @@ public final class OomAdjuster { @GuardedBy({"mService", "mProcLock"}) private void updateOomAdjLSP(String oomAdjReason) { + if (checkAndEnqueueOomAdjTargetLocked(null)) { + // Simply return as there is an oomAdjUpdate ongoing + return; + } + try { + mOomAdjUpdateOngoing = true; + performUpdateOomAdjLSP(oomAdjReason); + } finally { + // Kick off the handling of any pending targets enqueued during the above update + mOomAdjUpdateOngoing = false; + updateOomAdjPendingTargetsLocked(oomAdjReason); + } + } + + @GuardedBy({"mService", "mProcLock"}) + private void performUpdateOomAdjLSP(String oomAdjReason) { final ProcessRecord topApp = mService.getTopApp(); // Clear any pending ones because we are doing a full update now. mPendingProcessSet.clear(); @@ -548,6 +598,23 @@ public final class OomAdjuster { return true; } + if (checkAndEnqueueOomAdjTargetLocked(app)) { + // Simply return true as there is an oomAdjUpdate ongoing + return true; + } + + try { + mOomAdjUpdateOngoing = true; + return performUpdateOomAdjLSP(app, oomAdjReason); + } finally { + // Kick off the handling of any pending targets enqueued during the above update + mOomAdjUpdateOngoing = false; + updateOomAdjPendingTargetsLocked(oomAdjReason); + } + } + + @GuardedBy({"mService", "mProcLock"}) + private boolean performUpdateOomAdjLSP(ProcessRecord app, String oomAdjReason) { final ProcessRecord topApp = mService.getTopApp(); Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason); @@ -567,7 +634,7 @@ public final class OomAdjuster { state.resetCachedInfo(); // Check if this process is in the pending list too, remove from pending list if so. mPendingProcessSet.remove(app); - boolean success = updateOomAdjLSP(app, cachedAdj, topApp, false, + boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp, false, SystemClock.uptimeMillis()); if (!success || (wasCached == state.isCached() && oldAdj != ProcessList.INVALID_ADJ && wasBackground == ActivityManager.isProcStateBackground( @@ -696,14 +763,59 @@ public final class OomAdjuster { } /** + * Check if there is an ongoing oomAdjUpdate, enqueue the given process record + * to {@link #mPendingProcessSet} if there is one. + * + * @param app The target app to get an oomAdjUpdate, or a full oomAdjUpdate if it's null. + * @return {@code true} if there is an ongoing oomAdjUpdate. + */ + @GuardedBy("mService") + private boolean checkAndEnqueueOomAdjTargetLocked(@Nullable ProcessRecord app) { + if (!mOomAdjUpdateOngoing) { + return false; + } + if (app != null) { + mPendingProcessSet.add(app); + } else { + mPendingFullOomAdjUpdate = true; + } + return true; + } + + /** * Kick off an oom adj update pass for the pending targets which are enqueued via * {@link #enqueueOomAdjTargetLocked}. */ @GuardedBy("mService") void updateOomAdjPendingTargetsLocked(String oomAdjReason) { + // First check if there is pending full update + if (mPendingFullOomAdjUpdate) { + mPendingFullOomAdjUpdate = false; + mPendingProcessSet.clear(); + updateOomAdjLocked(oomAdjReason); + return; + } if (mPendingProcessSet.isEmpty()) { return; } + + if (mOomAdjUpdateOngoing) { + // There's another oomAdjUpdate ongoing, return from here now; + // that ongoing update would call us again at the end of it. + return; + } + try { + mOomAdjUpdateOngoing = true; + performUpdateOomAdjPendingTargetsLocked(oomAdjReason); + } finally { + // Kick off the handling of any pending targets enqueued during the above update + mOomAdjUpdateOngoing = false; + updateOomAdjPendingTargetsLocked(oomAdjReason); + } + } + + @GuardedBy("mService") + private void performUpdateOomAdjPendingTargetsLocked(String oomAdjReason) { final ProcessRecord topApp = mService.getTopApp(); Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason); @@ -1846,15 +1958,15 @@ public final class OomAdjuster { computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true); } else { - cstate.setCurRawAdj(cstate.getSetAdj()); - cstate.setCurRawProcState(cstate.getSetProcState()); + cstate.setCurRawAdj(cstate.getCurAdj()); + cstate.setCurRawProcState(cstate.getCurProcState()); } int clientAdj = cstate.getCurRawAdj(); int clientProcState = cstate.getCurRawProcState(); // pass client's mAllowStartFgs to the app if client is not persistent process. - if (cstate.getAllowedStartFgs() != FGS_FEATURE_DENIED + if (cstate.getAllowedStartFgs() != REASON_DENIED && cstate.getMaxAdj() >= ProcessList.FOREGROUND_APP_ADJ) { state.setAllowStartFgs(cstate.getAllowedStartFgs()); } @@ -1868,6 +1980,21 @@ public final class OomAdjuster { capability |= cstate.getCurCapability(); } + // If an app has network capability by default + // (by having procstate <= BFGS), then the apps it binds to will get + // elevated to a high enough procstate anyway to get network unless they + // request otherwise, so don't propagate the network capability by default + // in this case unless they explicitly request it. + if ((cstate.getCurCapability() & PROCESS_CAPABILITY_NETWORK) != 0) { + if (clientProcState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + if ((cr.flags & Context.BIND_ALLOW_NETWORK_ACCESS) != 0) { + capability |= PROCESS_CAPABILITY_NETWORK; + } + } else { + capability |= PROCESS_CAPABILITY_NETWORK; + } + } + if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) { // If the other app is cached for any reason, for purposes here // we are going to consider it empty. The specific cached state @@ -1935,6 +2062,10 @@ public final class OomAdjuster { && clientAdj <= ProcessList.PERCEPTIBLE_APP_ADJ && adj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) { newAdj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ; + } else if ((cr.flags & Context.BIND_ALMOST_PERCEPTIBLE) != 0 + && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ + && adj >= ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ) { + newAdj = ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ; } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0 && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ && adj >= ProcessList.PERCEPTIBLE_APP_ADJ) { @@ -2004,13 +2135,13 @@ public final class OomAdjuster { if (enabled) { if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) { // TOP process passes all capabilities to the service. - capability |= PROCESS_CAPABILITY_ALL; + capability |= cstate.getCurCapability(); } else { // TOP process passes no capability to the service. } } else { // TOP process passes all capabilities to the service. - capability |= PROCESS_CAPABILITY_ALL; + capability |= cstate.getCurCapability(); } } } else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) { @@ -2130,8 +2261,8 @@ public final class OomAdjuster { if (computeClients) { computeOomAdjLSP(client, cachedAdj, topApp, doingAll, now, cycleReEval, true); } else { - cstate.setCurRawAdj(cstate.getSetAdj()); - cstate.setCurRawProcState(cstate.getSetProcState()); + cstate.setCurRawAdj(cstate.getCurAdj()); + cstate.setCurRawProcState(cstate.getCurProcState()); } if (shouldSkipDueToCycle(state, cstate, procState, adj, cycleReEval)) { @@ -2335,20 +2466,20 @@ public final class OomAdjuster { case PROCESS_STATE_TOP: return PROCESS_CAPABILITY_ALL; case PROCESS_STATE_BOUND_TOP: - return PROCESS_CAPABILITY_NONE; + return PROCESS_CAPABILITY_NETWORK; case PROCESS_STATE_FOREGROUND_SERVICE: if (psr.hasForegroundServices()) { // Capability from FGS are conditional depending on foreground service type in // manifest file and the mAllowWhileInUsePermissionInFgs flag. - return PROCESS_CAPABILITY_NONE; + return PROCESS_CAPABILITY_NETWORK; } else { // process has no FGS, the PROCESS_STATE_FOREGROUND_SERVICE is from client. // the implicit capability could be removed in the future, client should use // BIND_INCLUDE_CAPABILITY flag. - return PROCESS_CAPABILITY_ALL_IMPLICIT; + return PROCESS_CAPABILITY_ALL_IMPLICIT | PROCESS_CAPABILITY_NETWORK; } case PROCESS_STATE_BOUND_FOREGROUND_SERVICE: - return PROCESS_CAPABILITY_NONE; + return PROCESS_CAPABILITY_NETWORK; default: return PROCESS_CAPABILITY_NONE; } @@ -2428,9 +2559,7 @@ public final class OomAdjuster { && (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ || state.getCurAdj() == ProcessList.HOME_APP_ADJ)) { mCachedAppOptimizer.compactAppSome(app); - } else if ((state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ - || state.getSetAdj() > ProcessList.CACHED_APP_MAX_ADJ) - && state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ + } else if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) { mCachedAppOptimizer.compactAppFull(app); } diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java index 42172bf7e1df..534bd84a91a3 100644 --- a/services/core/java/com/android/server/am/PendingIntentController.java +++ b/services/core/java/com/android/server/am/PendingIntentController.java @@ -36,6 +36,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerWhitelistManager; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; @@ -300,15 +301,16 @@ public class PendingIntentController { } } - void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken, - long duration, int type) { + void setPendingIntentAllowlistDuration(IIntentSender target, IBinder allowlistToken, + long duration, int type, @PowerWhitelistManager.ReasonCode int reasonCode, + @Nullable String reason) { if (!(target instanceof PendingIntentRecord)) { Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target); return; } synchronized (mLock) { - ((PendingIntentRecord) target).setWhitelistDurationLocked(whitelistToken, duration, - type); + ((PendingIntentRecord) target).setAllowlistDurationLocked(allowlistToken, duration, + type, reasonCode, reason); } } diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 0eb48f6da60d..51666acb8134 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -31,13 +31,14 @@ import android.content.Intent; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; +import android.os.PowerWhitelistManager; +import android.os.PowerWhitelistManager.ReasonCode; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.TransactionTooLargeException; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.Pair; import android.util.Slog; import android.util.TimeUtils; @@ -67,7 +68,7 @@ public final class PendingIntentRecord extends IIntentSender.Stub { * milliseconds, Integer is allowlist type defined at * {@link android.os.PowerWhitelistManager.TempAllowListType} */ - private ArrayMap<IBinder, Pair<Long, Integer>> mWhitelistDuration; + private ArrayMap<IBinder, TempAllowListDuration> mAllowlistDuration; private RemoteCallbackList<IResultReceiver> mCancelCallbacks; private ArraySet<IBinder> mAllowBgActivityStartsForActivitySender = new ArraySet<>(); private ArraySet<IBinder> mAllowBgActivityStartsForBroadcastSender = new ArraySet<>(); @@ -214,6 +215,21 @@ public final class PendingIntentRecord extends IIntentSender.Stub { } } + static final class TempAllowListDuration { + long duration; + int type; + @ReasonCode int reasonCode; + @Nullable String reason; + + TempAllowListDuration(long _duration, int _type, @ReasonCode int _reasonCode, + String _reason) { + duration = _duration; + type = _type; + reasonCode = _reasonCode; + reason = _reason; + } + } + PendingIntentRecord(PendingIntentController _controller, Key _k, int _u) { controller = _controller; key = _k; @@ -221,18 +237,19 @@ public final class PendingIntentRecord extends IIntentSender.Stub { ref = new WeakReference<>(this); } - void setWhitelistDurationLocked(IBinder whitelistToken, long duration, int type) { + void setAllowlistDurationLocked(IBinder allowlistToken, long duration, int type, + @ReasonCode int reasonCode, @Nullable String reason) { if (duration > 0) { - if (mWhitelistDuration == null) { - mWhitelistDuration = new ArrayMap<>(); + if (mAllowlistDuration == null) { + mAllowlistDuration = new ArrayMap<>(); } - mWhitelistDuration.put(whitelistToken, new Pair(duration, type)); - } else if (mWhitelistDuration != null) { - mWhitelistDuration.remove(whitelistToken); - if (mWhitelistDuration.size() <= 0) { - mWhitelistDuration = null; + mAllowlistDuration.put(allowlistToken, + new TempAllowListDuration(duration, type, reasonCode, reason)); + } else if (mAllowlistDuration != null) { + mAllowlistDuration.remove(allowlistToken); + if (mAllowlistDuration.size() <= 0) { + mAllowlistDuration = null; } - } this.stringName = null; } @@ -280,25 +297,25 @@ public final class PendingIntentRecord extends IIntentSender.Stub { return listeners; } - public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, + public void send(int code, Intent intent, String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { - sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver, + sendInner(code, intent, resolvedType, allowlistToken, finishedReceiver, requiredPermission, null, null, 0, 0, 0, options); } - public int sendWithResult(int code, Intent intent, String resolvedType, IBinder whitelistToken, + public int sendWithResult(int code, Intent intent, String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { - return sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver, + return sendInner(code, intent, resolvedType, allowlistToken, finishedReceiver, requiredPermission, null, null, 0, 0, 0, options); } - public int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken, + public int sendInner(int code, Intent intent, String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) { if (intent != null) intent.setDefusable(true); if (options != null) options.setDefusable(true); - Pair<Long, Integer> duration = null; + TempAllowListDuration duration = null; Intent finalIntent = null; Intent[] allIntents = null; String[] allResolvedTypes = null; @@ -347,8 +364,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub { mergedOptions.setCallerOptions(opts); } - if (mWhitelistDuration != null) { - duration = mWhitelistDuration.get(whitelistToken); + if (mAllowlistDuration != null) { + duration = mAllowlistDuration.get(allowlistToken); } if (key.type == ActivityManager.INTENT_SENDER_ACTIVITY @@ -377,7 +394,9 @@ public final class PendingIntentRecord extends IIntentSender.Stub { try { if (duration != null) { StringBuilder tag = new StringBuilder(64); - tag.append("pendingintent:"); + tag.append("setPendingIntentAllowlistDuration,reason:"); + tag.append(duration.reason == null ? "" : duration.reason); + tag.append(",pendingintent:"); UserHandle.formatUid(tag, callingUid); tag.append(":"); if (finalIntent.getAction() != null) { @@ -387,8 +406,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub { } else if (finalIntent.getData() != null) { tag.append(finalIntent.getData().toSafeString()); } - controller.mAmInternal.tempWhitelistForPendingIntent(callingPid, callingUid, - uid, duration.first, duration.second, tag.toString()); + controller.mAmInternal.tempAllowlistForPendingIntent(callingPid, callingUid, + uid, duration.duration, duration.type, duration.reasonCode, tag.toString()); } boolean sendFinish = finishedReceiver != null; @@ -417,7 +436,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub { allIntents, allResolvedTypes, resultTo, mergedOptions, userId, false /* validateIncomingUser */, this /* originatingPendingIntent */, - mAllowBgActivityStartsForActivitySender.contains(whitelistToken)); + mAllowBgActivityStartsForActivitySender.contains( + allowlistToken)); } else { res = controller.mAtmInternal.startActivityInPackage(uid, callingPid, callingUid, key.packageName, key.featureId, finalIntent, @@ -426,7 +446,7 @@ public final class PendingIntentRecord extends IIntentSender.Stub { false /* validateIncomingUser */, this /* originatingPendingIntent */, mAllowBgActivityStartsForActivitySender.contains( - whitelistToken)); + allowlistToken)); } } catch (RuntimeException e) { Slog.w(TAG, "Unable to send startActivity intent", e); @@ -439,8 +459,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub { case ActivityManager.INTENT_SENDER_BROADCAST: try { final boolean allowedByToken = - mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken); - final IBinder bgStartsToken = (allowedByToken) ? whitelistToken : null; + mAllowBgActivityStartsForBroadcastSender.contains(allowlistToken); + final IBinder bgStartsToken = (allowedByToken) ? allowlistToken : null; // If a completion callback has been requested, require // that the broadcast be delivered synchronously @@ -460,8 +480,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub { case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE: try { final boolean allowedByToken = - mAllowBgActivityStartsForServiceSender.contains(whitelistToken); - final IBinder bgStartsToken = (allowedByToken) ? whitelistToken : null; + mAllowBgActivityStartsForServiceSender.contains(allowlistToken); + final IBinder bgStartsToken = (allowedByToken) ? allowlistToken : null; controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType, key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE, @@ -533,18 +553,23 @@ public final class PendingIntentRecord extends IIntentSender.Stub { pw.print(prefix); pw.print("sent="); pw.print(sent); pw.print(" canceled="); pw.println(canceled); } - if (mWhitelistDuration != null) { + if (mAllowlistDuration != null) { pw.print(prefix); - pw.print("whitelistDuration="); - for (int i = 0; i < mWhitelistDuration.size(); i++) { + pw.print("allowlistDuration="); + for (int i = 0; i < mAllowlistDuration.size(); i++) { if (i != 0) { pw.print(", "); } - pw.print(Integer.toHexString(System.identityHashCode(mWhitelistDuration.keyAt(i)))); + TempAllowListDuration entry = mAllowlistDuration.valueAt(i); + pw.print(Integer.toHexString(System.identityHashCode(mAllowlistDuration.keyAt(i)))); pw.print(":"); - TimeUtils.formatDuration(mWhitelistDuration.valueAt(i).first, pw); + TimeUtils.formatDuration(entry.duration, pw); pw.print("/"); - pw.print(mWhitelistDuration.valueAt(i).second); + pw.print(entry.type); + pw.print("/"); + pw.print(PowerWhitelistManager.reasonCodeToString(entry.reasonCode)); + pw.print("/"); + pw.print(entry.reason); } pw.println(); } @@ -572,18 +597,23 @@ public final class PendingIntentRecord extends IIntentSender.Stub { } sb.append(' '); sb.append(key.typeName()); - if (mWhitelistDuration != null) { - sb.append( " (whitelist: "); - for (int i = 0; i < mWhitelistDuration.size(); i++) { + if (mAllowlistDuration != null) { + sb.append(" (allowlist: "); + for (int i = 0; i < mAllowlistDuration.size(); i++) { if (i != 0) { sb.append(","); } + TempAllowListDuration entry = mAllowlistDuration.valueAt(i); sb.append(Integer.toHexString(System.identityHashCode( - mWhitelistDuration.keyAt(i)))); + mAllowlistDuration.keyAt(i)))); sb.append(":"); - TimeUtils.formatDuration(mWhitelistDuration.valueAt(i).first, sb); + TimeUtils.formatDuration(entry.duration, sb); + sb.append("/"); + sb.append(entry.type); + sb.append("/"); + sb.append(PowerWhitelistManager.reasonCodeToString(entry.reasonCode)); sb.append("/"); - sb.append(mWhitelistDuration.valueAt(i).second); + sb.append(entry.reason); } sb.append(")"); } diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java index 60b246726593..984fe409b086 100644 --- a/services/core/java/com/android/server/am/PreBootBroadcaster.java +++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java @@ -17,6 +17,8 @@ package com.android.server.am; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; +import static android.os.PowerWhitelistManager.REASON_PRE_BOOT_COMPLETED; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; @@ -117,9 +119,9 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub { duration = amInternal.getBootTimeTempAllowListDuration(); } final BroadcastOptions bOptions = BroadcastOptions.makeBasic(); - bOptions.setTemporaryAppWhitelistDuration( - BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED, - duration); + bOptions.setTemporaryAppAllowlist(duration, + TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + REASON_PRE_BOOT_COMPLETED, ""); synchronized (mService) { mService.broadcastIntentLocked(null, null, null, mIntent, null, this, 0, null, null, null, AppOpsManager.OP_NONE, bOptions.toBundle(), true, diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java index 165312352990..3258f8af0da2 100644 --- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java @@ -22,6 +22,7 @@ import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.am.ProcessRecord.TAG; import android.app.ActivityManager; +import android.app.AnrController; import android.app.ApplicationErrorReport; import android.app.ApplicationExitInfo; import android.content.ComponentName; @@ -418,10 +419,16 @@ class ProcessErrorStateRecord { // Retrieve max ANR delay from AnrControllers without the mService lock since the // controllers might in turn call into apps - long anrDialogDelayMs = mService.mActivityTaskManager.getMaxAnrDelayMillis(aInfo); - if (aInfo != null && aInfo.packageName != null && anrDialogDelayMs > 0) { - Slog.i(TAG, "Delaying ANR dialog for " + aInfo.packageName + " for " + anrDialogDelayMs - + "ms"); + AnrController anrController = mService.mActivityTaskManager.getAnrController(aInfo); + long anrDialogDelayMs = 0; + if (anrController != null) { + String packageName = aInfo.packageName; + int uid = aInfo.uid; + anrDialogDelayMs = anrController.getAnrDelayMillis(packageName, uid); + // Might execute an async binder call to a system app to show an interim + // ANR progress UI + anrController.onAnrDelayStarted(packageName, uid); + Slog.i(TAG, "ANR delay of " + anrDialogDelayMs + "ms started for " + packageName); } synchronized (mService) { @@ -440,6 +447,7 @@ class ProcessErrorStateRecord { // Set the app's notResponding state, and look up the errorReportReceiver makeAppNotRespondingLSP(activityShortComponentName, annotation != null ? "ANR " + annotation : "ANR", info.toString()); + mDialogController.setAnrController(anrController); } // Notify package manager service to possibly update package state diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 172f7073852a..38330fe770fb 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.ActivityThread.PROC_START_SEQ_IDENT; @@ -57,6 +58,7 @@ import static com.android.server.am.AppProfiler.TAG_PSS; import android.annotation.NonNull; import android.app.ActivityManager; +import android.app.ActivityManager.ProcessCapability; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppProtoEnums; @@ -228,6 +230,11 @@ public final class ProcessList { // not so perceptible that it affects the user immediately if killed. static final int PERCEPTIBLE_LOW_APP_ADJ = 250; + // This is a process hosting services that are not perceptible to the user but the + // client (system) binding to it requested to treat it as if it is perceptible and avoid killing + // it if possible. + static final int PERCEPTIBLE_MEDIUM_APP_ADJ = 225; + // This is a process only hosting components that are perceptible to the // user, and we really want to avoid killing them, but they are not // immediately visible. An example is background music playback. @@ -1027,6 +1034,9 @@ public final class ProcessList { } else if (setAdj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) { return buildOomTag("prcl ", "prcl", null, setAdj, ProcessList.PERCEPTIBLE_LOW_APP_ADJ, compact); + } else if (setAdj >= ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ) { + return buildOomTag("prcm ", "prcm", null, setAdj, + ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ, compact); } else if (setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) { return buildOomTag("prcp ", "prcp", null, setAdj, ProcessList.PERCEPTIBLE_APP_ADJ, compact); @@ -1054,76 +1064,7 @@ public final class ProcessList { } public static String makeProcStateString(int curProcState) { - String procState; - switch (curProcState) { - case ActivityManager.PROCESS_STATE_PERSISTENT: - procState = "PER "; - break; - case ActivityManager.PROCESS_STATE_PERSISTENT_UI: - procState = "PERU"; - break; - case ActivityManager.PROCESS_STATE_TOP: - procState = "TOP "; - break; - case ActivityManager.PROCESS_STATE_BOUND_TOP: - procState = "BTOP"; - break; - case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE: - procState = "FGS "; - break; - case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE: - procState = "BFGS"; - break; - case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: - procState = "IMPF"; - break; - case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: - procState = "IMPB"; - break; - case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND: - procState = "TRNB"; - break; - case ActivityManager.PROCESS_STATE_BACKUP: - procState = "BKUP"; - break; - case ActivityManager.PROCESS_STATE_SERVICE: - procState = "SVC "; - break; - case ActivityManager.PROCESS_STATE_RECEIVER: - procState = "RCVR"; - break; - case ActivityManager.PROCESS_STATE_TOP_SLEEPING: - procState = "TPSL"; - break; - case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: - procState = "HVY "; - break; - case ActivityManager.PROCESS_STATE_HOME: - procState = "HOME"; - break; - case ActivityManager.PROCESS_STATE_LAST_ACTIVITY: - procState = "LAST"; - break; - case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: - procState = "CAC "; - break; - case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: - procState = "CACC"; - break; - case ActivityManager.PROCESS_STATE_CACHED_RECENT: - procState = "CRE "; - break; - case ActivityManager.PROCESS_STATE_CACHED_EMPTY: - procState = "CEM "; - break; - case ActivityManager.PROCESS_STATE_NONEXISTENT: - procState = "NONE"; - break; - default: - procState = "??"; - break; - } - return procState; + return ActivityManager.procStateToString(curProcState); } public static int makeProcStateProtoEnum(int curProcState) { @@ -1828,7 +1769,8 @@ public final class ProcessList { */ @GuardedBy("mService") boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord, - int zygotePolicyFlags, boolean disableHiddenApiChecks, String abiOverride) { + int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks, + String abiOverride) { if (app.isPendingStart()) { return true; } @@ -1974,6 +1916,10 @@ public final class ProcessList { throw new IllegalStateException("Invalid API policy: " + policy); } runtimeFlags |= policyBits; + + if (disableTestApiChecks) { + runtimeFlags |= Zygote.DISABLE_TEST_API_ENFORCEMENT_POLICY; + } } String useAppImageCache = SystemProperties.get( @@ -2437,7 +2383,8 @@ public final class ProcessList { boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord, int zygotePolicyFlags, String abiOverride) { return startProcessLocked(app, hostingRecord, zygotePolicyFlags, - false /* disableHiddenApiChecks */, abiOverride); + false /* disableHiddenApiChecks */, false /* disableTestApiChecks */, + abiOverride); } @GuardedBy("mService") @@ -4458,6 +4405,7 @@ public final class ProcessList { printOomLevel(pw, "FOREGROUND_APP_ADJ", FOREGROUND_APP_ADJ); printOomLevel(pw, "VISIBLE_APP_ADJ", VISIBLE_APP_ADJ); printOomLevel(pw, "PERCEPTIBLE_APP_ADJ", PERCEPTIBLE_APP_ADJ); + printOomLevel(pw, "PERCEPTIBLE_MEDIUM_APP_ADJ", PERCEPTIBLE_MEDIUM_APP_ADJ); printOomLevel(pw, "PERCEPTIBLE_LOW_APP_ADJ", PERCEPTIBLE_LOW_APP_ADJ); printOomLevel(pw, "BACKUP_APP_ADJ", BACKUP_APP_ADJ); printOomLevel(pw, "HEAVY_WEIGHT_APP_ADJ", HEAVY_WEIGHT_APP_ADJ); @@ -4721,13 +4669,26 @@ public final class ProcessList { } } - /** Returns the uid's process state or PROCESS_STATE_NONEXISTENT if not running */ + /** + * Returns the uid's process state or {@link ActivityManager#PROCESS_STATE_NONEXISTENT} + * if not running + */ @GuardedBy(anyOf = {"mService", "mProcLock"}) int getUidProcStateLOSP(int uid) { UidRecord uidRec = mActiveUids.get(uid); return uidRec == null ? PROCESS_STATE_NONEXISTENT : uidRec.getCurProcState(); } + /** + * Returns the uid's process capability or {@link ActivityManager#PROCESS_CAPABILITY_NONE} + * if not running + */ + @GuardedBy(anyOf = {"mService", "mProcLock"}) + @ProcessCapability int getUidProcessCapabilityLOSP(int uid) { + UidRecord uidRec = mActiveUids.get(uid); + return uidRec == null ? PROCESS_CAPABILITY_NONE : uidRec.getCurCapability(); + } + /** Returns the UidRecord for the given uid, if it exists. */ @GuardedBy(anyOf = {"mService", "mProcLock"}) UidRecord getUidRecordLOSP(int uid) { @@ -4768,11 +4729,13 @@ public final class ProcessList { int getBlockStateForUid(UidRecord uidRec) { // Denotes whether uid's process state is currently allowed network access. final boolean isAllowed = - isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState()) + isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getCurProcState(), + uidRec.getCurCapability()) || isProcStateAllowedWhileOnRestrictBackground(uidRec.getCurProcState()); // Denotes whether uid's process state was previously allowed network access. final boolean wasAllowed = - isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getSetProcState()) + isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.getSetProcState(), + uidRec.getSetCapability()) || isProcStateAllowedWhileOnRestrictBackground(uidRec.getSetProcState()); // When the uid is coming to foreground, AMS should inform the app thread that it should @@ -4810,8 +4773,9 @@ public final class ProcessList { if (!UserHandle.isApp(uidRec.getUid()) || !uidRec.hasInternetPermission) { continue; } - // If process state is not changed, then there's nothing to do. - if (uidRec.getSetProcState() == uidRec.getCurProcState()) { + // If process state and capabilities are not changed, then there's nothing to do. + if (uidRec.getSetProcState() == uidRec.getCurProcState() + && uidRec.getSetCapability() == uidRec.getCurCapability()) { continue; } final int blockState = getBlockStateForUid(uidRec); diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 499fbcb0642d..6d783fc63901 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -23,22 +23,23 @@ import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_BACKGROUND_FGS_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_COMPANION_DEVICE_MANAGER; +import static android.os.PowerWhitelistManager.REASON_DENIED; +import static android.os.PowerWhitelistManager.REASON_DEVICE_OWNER; +import static android.os.PowerWhitelistManager.REASON_PROFILE_OWNER; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED; +import static android.os.PowerWhitelistManager.REASON_SYSTEM_UID; +import static android.os.PowerWhitelistManager.ReasonCode; +import static android.os.PowerWhitelistManager.getReasonCodeFromProcState; +import static android.os.PowerWhitelistManager.reasonCodeToString; import static android.os.Process.NFC_UID; import static android.os.Process.ROOT_UID; import static android.os.Process.SHELL_UID; import static android.os.Process.SYSTEM_UID; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_COMPANION_APP; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_PROC_STATE; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION; -import static com.android.server.am.ActiveServices.FGS_FEATURE_ALLOWED_BY_SYSTEM_UID; -import static com.android.server.am.ActiveServices.FGS_FEATURE_DENIED; -import static com.android.server.am.ActiveServices.fgsCodeToString; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; import static com.android.server.am.ProcessRecord.TAG; @@ -329,7 +330,7 @@ final class ProcessStateRecord { * Does the process has permission to start FGS from background. */ @GuardedBy("mService") - private @ActiveServices.FgsFeatureRetCode int mAllowStartFgsByPermission; + private @ReasonCode int mAllowStartFgsByPermission = REASON_DENIED; /** * Can this process start FGS from background? @@ -337,7 +338,7 @@ final class ProcessStateRecord { * another process through service binding. */ @GuardedBy("mService") - private @ActiveServices.FgsFeatureRetCode int mAllowStartFgs; + private @ReasonCode int mAllowStartFgs = REASON_DENIED; /** * Debugging: primary thing impacting oom_adj. @@ -1152,44 +1153,47 @@ final class ProcessStateRecord { } @GuardedBy("mService") + int getAllowStartFgsState() { + return mAllowStartFgsState; + } + + @GuardedBy("mService") boolean isAllowedStartFgsState() { return mAllowStartFgsState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE; } @GuardedBy("mService") void setAllowStartFgsByPermission() { - int ret = FGS_FEATURE_DENIED; - if (ret == FGS_FEATURE_DENIED) { - boolean isSystem = false; - final int uid = UserHandle.getAppId(mApp.info.uid); - switch (uid) { - case ROOT_UID: - case SYSTEM_UID: - case NFC_UID: - case SHELL_UID: - isSystem = true; - break; - default: - isSystem = false; - break; - } + int ret = REASON_DENIED; + boolean isSystem = false; + final int uid = UserHandle.getAppId(mApp.info.uid); + switch (uid) { + case ROOT_UID: + case SYSTEM_UID: + case NFC_UID: + case SHELL_UID: + isSystem = true; + break; + default: + isSystem = false; + break; + } - if (isSystem) { - ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_UID; - } + if (isSystem) { + ret = REASON_SYSTEM_UID; } - if (ret == FGS_FEATURE_DENIED) { + if (ret == REASON_DENIED) { if (ActivityManager.checkComponentPermission(START_ACTIVITIES_FROM_BACKGROUND, mApp.info.uid, -1, true) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION; + ret = REASON_BACKGROUND_ACTIVITY_PERMISSION; } else if (ActivityManager.checkComponentPermission( START_FOREGROUND_SERVICES_FROM_BACKGROUND, mApp.info.uid, -1, true) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION; + ret = REASON_BACKGROUND_FGS_PERMISSION; } else if (ActivityManager.checkComponentPermission(SYSTEM_ALERT_WINDOW, mApp.info.uid, -1, true) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION; + ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION; } } mAllowStartFgs = mAllowStartFgsByPermission = ret; @@ -1197,59 +1201,65 @@ final class ProcessStateRecord { @GuardedBy("mService") void setAllowStartFgs() { - if (mAllowStartFgs != FGS_FEATURE_DENIED) { + if (mAllowStartFgs != REASON_DENIED) { return; } - if (mAllowStartFgs == FGS_FEATURE_DENIED) { + if (mAllowStartFgs == REASON_DENIED) { if (isAllowedStartFgsState()) { - mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_PROC_STATE; + mAllowStartFgs = getReasonCodeFromProcState(mAllowStartFgsState); } } - if (mAllowStartFgs == FGS_FEATURE_DENIED) { + if (mAllowStartFgs == REASON_DENIED) { // Is the calling UID a device owner app? if (mService.mInternal != null) { if (mService.mInternal.isDeviceOwner(mApp.info.uid)) { - mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER; + mAllowStartFgs = REASON_DEVICE_OWNER; } } } - if (mAllowStartFgs == FGS_FEATURE_DENIED) { + if (mAllowStartFgs == REASON_DENIED) { if (mService.mInternal != null) { final boolean isCompanionApp = mService.mInternal.isAssociatedCompanionApp( UserHandle.getUserId(mApp.info.uid), mApp.info.uid); if (isCompanionApp) { - mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_COMPANION_APP; + mAllowStartFgs = REASON_COMPANION_DEVICE_MANAGER; } } } - if (mAllowStartFgs == FGS_FEATURE_DENIED) { + if (mAllowStartFgs == REASON_DENIED) { // Is the calling UID a profile owner app? if (mService.mInternal != null) { if (mService.mInternal.isProfileOwner(mApp.info.uid)) { - mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_PROFILE_OWNER; + mAllowStartFgs = REASON_PROFILE_OWNER; } } } - if (mAllowStartFgs == FGS_FEATURE_DENIED) { + if (mAllowStartFgs == REASON_DENIED) { // uid is on DeviceIdleController's user/system allowlist // or AMS's FgsStartTempAllowList. - if (mService.isAllowlistedForFgsStartLOSP(mApp.info.uid)) { - mAllowStartFgs = FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST; + FgsStartTempAllowList.TempFgsAllowListEntry entry = + mService.isAllowlistedForFgsStartLOSP(mApp.info.uid); + if (entry != null) { + if (entry == ActivityManagerService.FAKE_TEMP_ALLOWLIST_ENTRY) { + mAllowStartFgs = REASON_SYSTEM_ALLOW_LISTED; + } else { + mAllowStartFgs = entry.mReasonCode; + } } } } @GuardedBy("mService") - void setAllowStartFgs(@ActiveServices.FgsFeatureRetCode int allowStartFgs) { + void setAllowStartFgs(@ReasonCode int allowStartFgs) { mAllowStartFgs = allowStartFgs; } @GuardedBy("mService") - @ActiveServices.FgsFeatureRetCode int getAllowedStartFgs() { + @ReasonCode int getAllowedStartFgs() { return mAllowStartFgs; } @@ -1291,9 +1301,9 @@ final class ProcessStateRecord { pw.println(); pw.print(prefix); pw.print("allowStartFgsState="); pw.println(mAllowStartFgsState); - if (mAllowStartFgs != FGS_FEATURE_DENIED) { + if (mAllowStartFgs != REASON_DENIED) { pw.print(prefix); pw.print("allowStartFgs="); - pw.println(fgsCodeToString(mAllowStartFgs)); + pw.println(reasonCodeToString(mAllowStartFgs)); } if (mHasShownUi || mApp.mProfile.hasPendingUiClean()) { pw.print(prefix); pw.print("hasShownUi="); pw.print(mHasShownUi); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 485087d29422..3ab95d131fad 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -18,6 +18,7 @@ package com.android.server.am; import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; +import static android.os.PowerWhitelistManager.REASON_DENIED; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -36,6 +37,7 @@ import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.IBinder; +import android.os.PowerWhitelistManager; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; @@ -101,7 +103,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN ProcessRecord isolatedProc; // keep track of isolated process, if requested ServiceState tracker; // tracking service execution, may be null ServiceState restartTracker; // tracking service restart - boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT? + boolean allowlistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT? boolean delayed; // are we waiting to start this service in the background? boolean fgRequired; // is the service required to go foreground after starting? boolean fgWaiting; // is a timeout for going foreground already scheduled? @@ -156,11 +158,14 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // the most recent package that start/bind this service. String mRecentCallingPackage; + // the most recent uid that start/bind this service. + int mRecentCallingUid; // allow the service becomes foreground service? Service started from background may not be // allowed to become a foreground service. - @ActiveServices.FgsFeatureRetCode int mAllowStartForeground; + @PowerWhitelistManager.ReasonCode int mAllowStartForeground = REASON_DENIED; String mInfoAllowStartForeground; + FgsStartTempAllowList.TempFgsAllowListEntry mInfoTempFgsAllowListReason; boolean mLoggedInfoAllowStartForeground; String stringName; // caching of toString @@ -309,7 +314,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (isolatedProc != null) { isolatedProc.dumpDebug(proto, ServiceRecordProto.ISOLATED_PROC); } - proto.write(ServiceRecordProto.WHITELIST_MANAGER, whitelistManager); + proto.write(ServiceRecordProto.WHITELIST_MANAGER, allowlistManager); proto.write(ServiceRecordProto.DELAYED, delayed); if (isForeground || foregroundId != 0) { long fgToken = proto.start(ServiceRecordProto.FOREGROUND); @@ -410,8 +415,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (isolatedProc != null) { pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc); } - if (whitelistManager) { - pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager); + if (allowlistManager) { + pw.print(prefix); pw.print("allowlistManager="); pw.println(allowlistManager); } if (mIsAllowedBgActivityStartsByBinding) { pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByBinding="); @@ -429,6 +434,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.println(mAllowWhileInUsePermissionInFgs); pw.print(prefix); pw.print("recentCallingPackage="); pw.println(mRecentCallingPackage); + pw.print(prefix); pw.print("recentCallingUid="); + pw.println(mRecentCallingUid); pw.print(prefix); pw.print("allowStartForeground="); pw.println(mAllowStartForeground); pw.print(prefix); pw.print("infoAllowStartForeground="); @@ -894,13 +901,13 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN return false; } - public void updateWhitelistManager() { - whitelistManager = false; + public void updateAllowlistManager() { + allowlistManager = false; for (int conni=connections.size()-1; conni>=0; conni--) { ArrayList<ConnectionRecord> cr = connections.valueAt(conni); for (int i=0; i<cr.size(); i++) { if ((cr.get(i).flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { - whitelistManager = true; + allowlistManager = true; return; } } diff --git a/services/core/java/com/android/server/am/StrictModeViolationDialog.java b/services/core/java/com/android/server/am/StrictModeViolationDialog.java index 4cc1fc1b7d09..9dddd658575b 100644 --- a/services/core/java/com/android/server/am/StrictModeViolationDialog.java +++ b/services/core/java/com/android/server/am/StrictModeViolationDialog.java @@ -82,6 +82,11 @@ final class StrictModeViolationDialog extends BaseErrorDialog { DISMISS_TIMEOUT); } + @Override + protected void closeDialog() { + mHandler.obtainMessage(ACTION_OK).sendToTarget(); + } + private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { synchronized (mService.mProcLock) { diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING index 4061df4f3f62..03eddc9634d7 100644 --- a/services/core/java/com/android/server/am/TEST_MAPPING +++ b/services/core/java/com/android/server/am/TEST_MAPPING @@ -44,6 +44,23 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] + }, + { + "file_patterns": ["Battery[^/]*\\.java", "MeasuredEnergy[^/]*\\.java"], + "name": "FrameworksCoreTests", + "options": [ + { "include-filter": "com.android.internal.os.BatteryStatsTests" }, + { "exclude-annotation": "com.android.internal.os.SkipPresubmit" } + ] + }, + { + "file_patterns": ["Battery[^/]*\\.java", "MeasuredEnergy[^/]*\\.java"], + "name": "FrameworksServicesTests", + "options": [ + { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }, + { "include-filter": "com.android.server.am.MeasuredEnergySnapshotTest" }, + { "include-filter": "com.android.server.am.BatteryExternalStatsWorkerTest" } + ] } ], "postsubmit": [ diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 6dd78e77aafb..caf2510e5b1c 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -27,6 +27,9 @@ import static android.app.ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_ import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE; +import static android.os.PowerWhitelistManager.REASON_BOOT_COMPLETED; +import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.os.Process.SHELL_UID; import static android.os.Process.SYSTEM_UID; @@ -74,6 +77,7 @@ import android.os.IRemoteCallback; import android.os.IUserManager; import android.os.Looper; import android.os.Message; +import android.os.PowerWhitelistManager; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -522,7 +526,8 @@ class UserController implements Handler.Callback { mInjector.broadcastIntent(intent, null, resultTo, 0, null, null, new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, AppOpsManager.OP_NONE, - getTemporaryAppWhitelistBroadcastOptions().toBundle(), true, + getTemporaryAppAllowlistBroadcastOptions(REASON_LOCKED_BOOT_COMPLETED) + .toBundle(), true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), userId); } @@ -770,9 +775,8 @@ class UserController implements Handler.Callback { }, 0, null, null, new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, AppOpsManager.OP_NONE, - getTemporaryAppWhitelistBroadcastOptions().toBundle(), true, - false, MY_PID, SYSTEM_UID, - callingUid, callingPid, userId); + getTemporaryAppAllowlistBroadcastOptions(REASON_BOOT_COMPLETED).toBundle(), + true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, userId); }); } @@ -2811,7 +2815,8 @@ class UserController implements Handler.Callback { } } - private BroadcastOptions getTemporaryAppWhitelistBroadcastOptions() { + private BroadcastOptions getTemporaryAppAllowlistBroadcastOptions( + @PowerWhitelistManager.ReasonCode int reasonCode) { long duration = 10_000; final ActivityManagerInternal amInternal = LocalServices.getService(ActivityManagerInternal.class); @@ -2819,9 +2824,8 @@ class UserController implements Handler.Callback { duration = amInternal.getBootTimeTempAllowListDuration(); } final BroadcastOptions bOptions = BroadcastOptions.makeBasic(); - bOptions.setTemporaryAppWhitelistDuration( - BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED, - duration); + bOptions.setTemporaryAppAllowlist(duration, + TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, reasonCode, ""); return bOptions; } diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index e97f0b47380a..32ae87898085 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -23,9 +23,12 @@ import static android.content.Intent.EXTRA_REPLACING; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION; +import static com.android.server.apphibernation.AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityThread; import android.app.IActivityManager; import android.apphibernation.IAppHibernationService; import android.content.BroadcastReceiver; @@ -45,6 +48,8 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.provider.DeviceConfig; +import android.provider.DeviceConfig.Properties; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -52,10 +57,13 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.SystemService; import java.io.File; import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -93,6 +101,9 @@ public final class AppHibernationService extends SystemService { private final HibernationStateDiskStore<GlobalLevelState> mGlobalLevelHibernationDiskStore; private final Injector mInjector; + @VisibleForTesting + boolean mIsServiceEnabled; + /** * Initializes the system service. * <p> @@ -139,6 +150,13 @@ public final class AppHibernationService extends SystemService { initializeGlobalHibernationStates(states); } } + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + mIsServiceEnabled = isAppHibernationEnabled(); + DeviceConfig.addOnPropertiesChangedListener( + NAMESPACE_APP_HIBERNATION, + ActivityThread.currentApplication().getMainExecutor(), + this::onDeviceConfigChanged); + } } /** @@ -149,6 +167,10 @@ public final class AppHibernationService extends SystemService { * @return true if package is hibernating for the user */ boolean isHibernatingForUser(String packageName, int userId) { + if (!checkHibernationEnabled("isHibernatingForUser")) { + return false; + } + userId = handleIncomingUser(userId, "isHibernating"); if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.e(TAG, "Attempt to get hibernation state of stopped or nonexistent user " @@ -174,6 +196,9 @@ public final class AppHibernationService extends SystemService { * @param packageName package to check */ boolean isHibernatingGlobally(String packageName) { + if (!checkHibernationEnabled("isHibernatingGlobally")) { + return false; + } synchronized (mLock) { GlobalLevelState state = mGlobalHibernationStates.get(packageName); if (state == null) { @@ -192,6 +217,9 @@ public final class AppHibernationService extends SystemService { * @param isHibernating new hibernation state */ void setHibernatingForUser(String packageName, int userId, boolean isHibernating) { + if (!checkHibernationEnabled("setHibernatingForUser")) { + return; + } userId = handleIncomingUser(userId, "setHibernating"); if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.w(TAG, "Attempt to set hibernation state for a stopped or nonexistent user " @@ -229,6 +257,9 @@ public final class AppHibernationService extends SystemService { * @param isHibernating new hibernation state */ void setHibernatingGlobally(String packageName, boolean isHibernating) { + if (!checkHibernationEnabled("setHibernatingGlobally")) { + return; + } synchronized (mLock) { GlobalLevelState state = mGlobalHibernationStates.get(packageName); if (state == null) { @@ -421,6 +452,9 @@ public final class AppHibernationService extends SystemService { private void onPackageAdded(@NonNull String packageName, int userId) { synchronized (mLock) { + if (!mUserStates.contains(userId)) { + return; + } UserLevelState userState = new UserLevelState(); userState.packageName = packageName; mUserStates.get(userId).put(packageName, userState); @@ -434,6 +468,9 @@ public final class AppHibernationService extends SystemService { private void onPackageRemoved(@NonNull String packageName, int userId) { synchronized (mLock) { + if (!mUserStates.contains(userId)) { + return; + } mUserStates.get(userId).remove(packageName); } } @@ -444,6 +481,15 @@ public final class AppHibernationService extends SystemService { } } + private void onDeviceConfigChanged(Properties properties) { + for (String key : properties.getKeyset()) { + if (TextUtils.equals(KEY_APP_HIBERNATION_ENABLED, key)) { + mIsServiceEnabled = isAppHibernationEnabled(); + break; + } + } + } + /** * Private helper method to get the real user id and enforce permission checks. * @@ -461,6 +507,44 @@ public final class AppHibernationService extends SystemService { } } + private boolean checkHibernationEnabled(String methodName) { + if (!mIsServiceEnabled) { + Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName)); + } + return mIsServiceEnabled; + } + + private void dump(PrintWriter pw) { + // Check usage stats permission since hibernation indirectly informs usage. + if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return; + + IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " "); + + synchronized (mLock) { + final int userCount = mUserStates.size(); + for (int i = 0; i < userCount; i++) { + final int userId = mUserStates.keyAt(i); + idpw.print("User Level Hibernation States, "); + idpw.printPair("user", userId); + idpw.println(); + Map<String, UserLevelState> stateMap = mUserStates.get(i); + idpw.increaseIndent(); + for (UserLevelState state : stateMap.values()) { + idpw.print(state); + idpw.println(); + } + idpw.decreaseIndent(); + } + idpw.println(); + idpw.print("Global Level Hibernation States"); + idpw.println(); + for (GlobalLevelState state : mGlobalHibernationStates.values()) { + idpw.print(state); + idpw.println(); + } + } + } + private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this); static final class AppHibernationServiceStub extends IAppHibernationService.Stub { @@ -497,6 +581,12 @@ public final class AppHibernationService extends SystemService { new AppHibernationShellCommand(mService).exec(this, in, out, err, args, callback, resultReceiver); } + + @Override + protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout, + @Nullable String[] args) { + mService.dump(fout); + } } // Broadcast receiver for package add/removal events @@ -536,7 +626,7 @@ public final class AppHibernationService extends SystemService { public static boolean isAppHibernationEnabled() { return DeviceConfig.getBoolean( NAMESPACE_APP_HIBERNATION, - AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED, + KEY_APP_HIBERNATION_ENABLED, false /* defaultValue */); } diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelState.java b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java index 4f756756c2ab..baa84b7070ff 100644 --- a/services/core/java/com/android/server/apphibernation/GlobalLevelState.java +++ b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java @@ -22,4 +22,12 @@ package com.android.server.apphibernation; final class GlobalLevelState { public String packageName; public boolean hibernated; + + @Override + public String toString() { + return "GlobalLevelState{" + + "packageName='" + packageName + '\'' + + ", hibernated=" + hibernated + + '}'; + } } diff --git a/services/core/java/com/android/server/apphibernation/UserLevelState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java index c66dad87c891..272d3d1122a1 100644 --- a/services/core/java/com/android/server/apphibernation/UserLevelState.java +++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java @@ -22,4 +22,12 @@ package com.android.server.apphibernation; final class UserLevelState { public String packageName; public boolean hibernated; + + @Override + public String toString() { + return "UserLevelState{" + + "packageName='" + packageName + '\'' + + ", hibernated=" + hibernated + + '}'; + } } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index c92abd48fdc2..44dcc205a9d0 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -339,7 +339,10 @@ public class AppOpsService extends IAppOpsService.Stub { SparseIntArray mProfileOwners; @GuardedBy("this") - private CheckOpsDelegate mCheckOpsDelegate; + private CheckOpsDelegate mAppOpsPolicy; + + @GuardedBy("this") + private CheckOpsDelegateDispatcher mCheckOpsDelegateDispatcher; /** * Reverse lookup for {@link AppOpsManager#opToSwitch(int)}. Initialized once and never @@ -1770,6 +1773,17 @@ public class AppOpsService extends IAppOpsService.Stub { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); } + /** + * Sets a policy for handling app ops. + * + * @param appOpsPolicy The policy. + */ + public void setAppOpsPolicy(@Nullable CheckOpsDelegate appOpsPolicy) { + synchronized (AppOpsService.this) { + mAppOpsPolicy = appOpsPolicy; + } + } + public void packageRemoved(int uid, String packageName) { synchronized (this) { UidState uidState = mUidStates.get(uid); @@ -2868,13 +2882,19 @@ public class AppOpsService extends IAppOpsService.Stub { public CheckOpsDelegate getAppOpsServiceDelegate() { synchronized (this) { - return mCheckOpsDelegate; + return (mCheckOpsDelegateDispatcher != null) + ? mCheckOpsDelegateDispatcher.getCheckOpsDelegate() + : null; } } public void setAppOpsServiceDelegate(CheckOpsDelegate delegate) { synchronized (this) { - mCheckOpsDelegate = delegate; + if (delegate != null) { + mCheckOpsDelegateDispatcher = new CheckOpsDelegateDispatcher(delegate); + } else { + mCheckOpsDelegateDispatcher = null; + } } } @@ -2889,19 +2909,28 @@ public class AppOpsService extends IAppOpsService.Stub { } private int checkOperationInternal(int code, int uid, String packageName, boolean raw) { - final CheckOpsDelegate delegate; - synchronized (this) { - delegate = mCheckOpsDelegate; + final CheckOpsDelegate policy; + final CheckOpsDelegateDispatcher delegateDispatcher; + synchronized (AppOpsService.this) { + policy = mAppOpsPolicy; + delegateDispatcher = mCheckOpsDelegateDispatcher; } - if (delegate == null) { - return checkOperationImpl(code, uid, packageName, raw); + if (policy != null) { + if (delegateDispatcher != null) { + return policy.checkOperation(code, uid, packageName, raw, + delegateDispatcher::checkOperationImpl); + } else { + return policy.checkOperation(code, uid, packageName, raw, + AppOpsService.this::checkOperationImpl); + } + } else if (delegateDispatcher != null) { + delegateDispatcher.getCheckOpsDelegate().checkOperation(code, uid, + packageName, raw, AppOpsService.this::checkOperationImpl); } - return delegate.checkOperation(code, uid, packageName, raw, - AppOpsService.this::checkOperationImpl); + return checkOperationImpl(code, uid, packageName, raw); } - private int checkOperationImpl(int code, int uid, String packageName, - boolean raw) { + private int checkOperationImpl(int code, int uid, String packageName, boolean raw) { verifyIncomingOp(code); verifyIncomingPackage(packageName, UserHandle.getUserId(uid)); @@ -2956,15 +2985,25 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int checkAudioOperation(int code, int usage, int uid, String packageName) { - final CheckOpsDelegate delegate; - synchronized (this) { - delegate = mCheckOpsDelegate; + final CheckOpsDelegate policy; + final CheckOpsDelegateDispatcher delegateDispatcher; + synchronized (AppOpsService.this) { + policy = mAppOpsPolicy; + delegateDispatcher = mCheckOpsDelegateDispatcher; } - if (delegate == null) { - return checkAudioOperationImpl(code, usage, uid, packageName); + if (policy != null) { + if (delegateDispatcher != null) { + return policy.checkAudioOperation(code, usage, uid, packageName, + delegateDispatcher::checkAudioOperationImpl); + } else { + return policy.checkAudioOperation(code, usage, uid, packageName, + AppOpsService.this::checkAudioOperationImpl); + } + } else if (delegateDispatcher != null) { + delegateDispatcher.getCheckOpsDelegate().checkAudioOperation(code, usage, + uid, packageName, AppOpsService.this::checkAudioOperationImpl); } - return delegate.checkAudioOperation(code, usage, uid, packageName, - AppOpsService.this::checkAudioOperationImpl); + return checkAudioOperationImpl(code, usage, uid, packageName); } private int checkAudioOperationImpl(int code, int usage, int uid, String packageName) { @@ -3078,17 +3117,29 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int noteOperation(int code, int uid, String packageName, String attributionTag, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage) { - final CheckOpsDelegate delegate; - synchronized (this) { - delegate = mCheckOpsDelegate; - } - if (delegate == null) { - return noteOperationImpl(code, uid, packageName, attributionTag, - shouldCollectAsyncNotedOp, message, shouldCollectMessage); + final CheckOpsDelegate policy; + final CheckOpsDelegateDispatcher delegateDispatcher; + synchronized (AppOpsService.this) { + policy = mAppOpsPolicy; + delegateDispatcher = mCheckOpsDelegateDispatcher; + } + if (policy != null) { + if (delegateDispatcher != null) { + return policy.noteOperation(code, uid, packageName, attributionTag, + shouldCollectAsyncNotedOp, message, shouldCollectMessage, + delegateDispatcher::noteOperationImpl); + } else { + return policy.noteOperation(code, uid, packageName, attributionTag, + shouldCollectAsyncNotedOp, message, shouldCollectMessage, + AppOpsService.this::noteOperationImpl); + } + } else if (delegateDispatcher != null) { + delegateDispatcher.getCheckOpsDelegate().noteOperation(code, uid, packageName, + attributionTag, shouldCollectAsyncNotedOp, message, shouldCollectMessage, + AppOpsService.this::noteOperationImpl); } - return delegate.noteOperation(code, uid, packageName, attributionTag, - shouldCollectAsyncNotedOp, message, shouldCollectMessage, - AppOpsService.this::noteOperationImpl); + return noteOperationImpl(code, uid, packageName, attributionTag, + shouldCollectAsyncNotedOp, message, shouldCollectMessage); } private int noteOperationImpl(int code, int uid, @Nullable String packageName, @@ -3124,7 +3175,7 @@ public class AppOpsService extends IAppOpsService.Stub { final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, true /* edit */); if (ops == null) { - scheduleOpNotedIfNeededLocked(code, uid, packageName, flags, + scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_IGNORED); if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid + " package " + packageName); @@ -3142,7 +3193,7 @@ public class AppOpsService extends IAppOpsService.Stub { final UidState uidState = ops.uidState; if (isOpRestrictedLocked(uid, code, packageName, bypass)) { attributedOp.rejected(uidState.state, flags); - scheduleOpNotedIfNeededLocked(code, uid, packageName, flags, + scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_IGNORED); return AppOpsManager.MODE_IGNORED; } @@ -3155,7 +3206,8 @@ public class AppOpsService extends IAppOpsService.Stub { + switchCode + " (" + code + ") uid " + uid + " package " + packageName); attributedOp.rejected(uidState.state, flags); - scheduleOpNotedIfNeededLocked(code, uid, packageName, flags, uidMode); + scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags, + uidMode); return uidMode; } } else { @@ -3167,7 +3219,8 @@ public class AppOpsService extends IAppOpsService.Stub { + switchCode + " (" + code + ") uid " + uid + " package " + packageName); attributedOp.rejected(uidState.state, flags); - scheduleOpNotedIfNeededLocked(code, uid, packageName, flags, mode); + scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags, + mode); return mode; } } @@ -3177,7 +3230,7 @@ public class AppOpsService extends IAppOpsService.Stub { + packageName + (attributionTag == null ? "" : "." + attributionTag)); } - scheduleOpNotedIfNeededLocked(code, uid, packageName, flags, + scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_ALLOWED); attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag, uidState.state, flags); @@ -3580,7 +3633,7 @@ public class AppOpsService extends IAppOpsService.Stub { final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, true /* edit */); if (ops == null) { if (!dryRun) { - scheduleOpStartedIfNeededLocked(code, uid, packageName, + scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_IGNORED); } if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid @@ -3590,7 +3643,7 @@ public class AppOpsService extends IAppOpsService.Stub { final Op op = getOpLocked(ops, code, uid, true); if (isOpRestrictedLocked(uid, code, packageName, bypass)) { if (!dryRun) { - scheduleOpStartedIfNeededLocked(code, uid, packageName, + scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_IGNORED); } return AppOpsManager.MODE_IGNORED; @@ -3611,7 +3664,8 @@ public class AppOpsService extends IAppOpsService.Stub { } if (!dryRun) { attributedOp.rejected(uidState.state, flags); - scheduleOpStartedIfNeededLocked(code, uid, packageName, flags, uidMode); + scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, + flags, uidMode); } return uidMode; } @@ -3626,7 +3680,8 @@ public class AppOpsService extends IAppOpsService.Stub { + packageName); if (!dryRun) { attributedOp.rejected(uidState.state, flags); - scheduleOpStartedIfNeededLocked(code, uid, packageName, flags, mode); + scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, + flags, mode); } return mode; } @@ -3634,7 +3689,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid + " package " + packageName); if (!dryRun) { - scheduleOpStartedIfNeededLocked(code, uid, packageName, flags, + scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_ALLOWED); try { attributedOp.started(clientId, proxyUid, proxyPackageName, proxyAttributionTag, @@ -3774,7 +3829,7 @@ public class AppOpsService extends IAppOpsService.Stub { } private void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName, - @OpFlags int flags, @Mode int result) { + String attributionTag, @OpFlags int flags, @Mode int result) { ArraySet<StartedCallback> dispatchedCallbacks = null; final int callbackListCount = mStartedWatchers.size(); for (int i = 0; i < callbackListCount; i++) { @@ -3799,18 +3854,21 @@ public class AppOpsService extends IAppOpsService.Stub { mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpStarted, - this, dispatchedCallbacks, code, uid, pkgName, flags, result)); + this, dispatchedCallbacks, code, uid, pkgName, attributionTag, flags, + result)); } private void notifyOpStarted(ArraySet<StartedCallback> callbacks, - int code, int uid, String packageName, @OpFlags int flags, @Mode int result) { + int code, int uid, String packageName, String attributionTag, @OpFlags int flags, + @Mode int result) { final long identity = Binder.clearCallingIdentity(); try { final int callbackCount = callbacks.size(); for (int i = 0; i < callbackCount; i++) { final StartedCallback callback = callbacks.valueAt(i); try { - callback.mCallback.opStarted(code, uid, packageName, flags, result); + callback.mCallback.opStarted(code, uid, packageName, attributionTag, flags, + result); } catch (RemoteException e) { /* do nothing */ } @@ -3821,7 +3879,7 @@ public class AppOpsService extends IAppOpsService.Stub { } private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName, - @OpFlags int flags, @Mode int result) { + String attributionTag, @OpFlags int flags, @Mode int result) { ArraySet<NotedCallback> dispatchedCallbacks = null; final int callbackListCount = mNotedWatchers.size(); for (int i = 0; i < callbackListCount; i++) { @@ -3842,11 +3900,13 @@ public class AppOpsService extends IAppOpsService.Stub { } mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpChecked, - this, dispatchedCallbacks, code, uid, packageName, flags, result)); + this, dispatchedCallbacks, code, uid, packageName, attributionTag, flags, + result)); } private void notifyOpChecked(ArraySet<NotedCallback> callbacks, - int code, int uid, String packageName, @OpFlags int flags, @Mode int result) { + int code, int uid, String packageName, String attributionTag, @OpFlags int flags, + @Mode int result) { // There are features watching for checks in our process. The callbacks in // these features may require permissions our remote caller does not have. final long identity = Binder.clearCallingIdentity(); @@ -3855,7 +3915,8 @@ public class AppOpsService extends IAppOpsService.Stub { for (int i = 0; i < callbackCount; i++) { final NotedCallback callback = callbacks.valueAt(i); try { - callback.mCallback.opNoted(code, uid, packageName, flags, result); + callback.mCallback.opNoted(code, uid, packageName, attributionTag, flags, + result); } catch (RemoteException e) { /* do nothing */ } @@ -6579,7 +6640,6 @@ public class AppOpsService extends IAppOpsService.Stub { } } - /** * Async task for writing note op stack trace, op code, package name and version to file * More specifically, writes all the collected ops from {@link #mNoteOpCallerStacktraces} @@ -6716,4 +6776,34 @@ public class AppOpsService extends IAppOpsService.Stub { } } } + + private final class CheckOpsDelegateDispatcher { + private final @NonNull CheckOpsDelegate mCheckOpsDelegate; + + CheckOpsDelegateDispatcher(@NonNull CheckOpsDelegate checkOpsDelegate) { + mCheckOpsDelegate = checkOpsDelegate; + } + + public @NonNull CheckOpsDelegate getCheckOpsDelegate() { + return mCheckOpsDelegate; + } + + public int checkOperationImpl(int code, int uid, String packageName, boolean raw) { + return mCheckOpsDelegate.checkOperation(code, uid, packageName, raw, + AppOpsService.this::checkOperationImpl); + } + + public int checkAudioOperationImpl(int code, int usage, int uid, String packageName) { + return mCheckOpsDelegate.checkAudioOperation(code, usage, uid, packageName, + AppOpsService.this::checkAudioOperationImpl); + } + + public int noteOperationImpl(int code, int uid, @Nullable String packageName, + @Nullable String featureId, boolean shouldCollectAsyncNotedOp, + @Nullable String message, boolean shouldCollectMessage) { + return mCheckOpsDelegate.noteOperation(code, uid, packageName, featureId, + shouldCollectAsyncNotedOp, message, shouldCollectMessage, + AppOpsService.this::noteOperationImpl); + } + } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 6f625a745ef6..f5b94177a2d9 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -178,6 +178,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BooleanSupplier; import java.util.stream.Collectors; @@ -330,7 +331,8 @@ public class AudioService extends IAudioService.Stub private SettingsObserver mSettingsObserver; - private int mMode = AudioSystem.MODE_NORMAL; + private AtomicInteger mMode = new AtomicInteger(AudioSystem.MODE_NORMAL); + // protects mRingerMode private final Object mSettingsLock = new Object(); @@ -724,7 +726,7 @@ public class AudioService extends IAudioService.Stub // caches the value returned by AudioSystem.isMicrophoneMuted() private boolean mMicMuteFromSystemCached; - private boolean mFastScrollSoundEffectsEnabled; + private boolean mNavigationRepeatSoundEffectsEnabled; private boolean mHomeSoundEffectEnabled; @GuardedBy("mSettingsLock") @@ -2323,15 +2325,15 @@ public class AudioService extends IAudioService.Stub VOL_ADJUST_NORMAL); } - public void setFastScrollSoundEffectsEnabled(boolean enabled) { - mFastScrollSoundEffectsEnabled = enabled; + public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) { + mNavigationRepeatSoundEffectsEnabled = enabled; } /** * @return true if the fast scroll sound effects are enabled */ - public boolean areFastScrollSoundEffectsEnabled() { - return mFastScrollSoundEffectsEnabled; + public boolean areNavigationRepeatSoundEffectsEnabled() { + return mNavigationRepeatSoundEffectsEnabled; } public void setHomeSoundEffectEnabled(boolean enabled) { @@ -2996,7 +2998,7 @@ public class AudioService extends IAudioService.Stub } /*package*/ int getHearingAidStreamType() { - return getHearingAidStreamType(getMode()); + return getHearingAidStreamType(mMode.get()); } private int getHearingAidStreamType(int mode) { @@ -3136,7 +3138,8 @@ public class AudioService extends IAudioService.Stub private void dumpAudioMode(PrintWriter pw) { pw.println("\nAudio mode: "); - pw.println("- Current mode = " + AudioSystem.modeToString(getMode())); + pw.println("- Requested mode = " + AudioSystem.modeToString(getMode())); + pw.println("- Actual mode = " + AudioSystem.modeToString(mMode.get())); pw.println("- Mode owner: "); SetModeDeathHandler hdlr = getAudioModeOwnerHandler(); if (hdlr != null) { @@ -4477,10 +4480,10 @@ public class AudioService extends IAudioService.Stub pid = currentModeHandler.getPid(); } if (DEBUG_MODE) { - Log.v(TAG, "onUpdateAudioMode() mode: " + mode + ", mMode: " + mMode - + " requestedMode: " + requestedMode); + Log.v(TAG, "onUpdateAudioMode() new mode: " + mode + ", current mode: " + + mMode.get() + " requested mode: " + requestedMode); } - if (mode != mMode) { + if (mode != mMode.get()) { final long identity = Binder.clearCallingIdentity(); int status = mAudioSystem.setPhoneState(mode, uid); Binder.restoreCallingIdentity(identity); @@ -4488,8 +4491,7 @@ public class AudioService extends IAudioService.Stub if (DEBUG_MODE) { Log.v(TAG, "onUpdateAudioMode: mode successfully set to " + mode); } - int previousMode = mMode; - mMode = mode; + int previousMode = mMode.getAndSet(mode); // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL mModeLogger.log(new PhoneStateEvent(requesterPackage, requesterPid, requestedMode, pid, mode)); @@ -5413,8 +5415,10 @@ public class AudioService extends IAudioService.Stub IsInCall = telecomManager.isInCall(); Binder.restoreCallingIdentity(ident); - return (IsInCall || getMode() == AudioManager.MODE_IN_COMMUNICATION || - getMode() == AudioManager.MODE_IN_CALL); + int mode = mMode.get(); + return (IsInCall + || mode == AudioManager.MODE_IN_COMMUNICATION + || mode == AudioManager.MODE_IN_CALL); } /** @@ -8566,6 +8570,7 @@ public class AudioService extends IAudioService.Stub public void postDisplaySafeVolumeWarning(int flags) { if (mController == null) return; + flags = flags | AudioManager.FLAG_SHOW_UI; try { mController.displaySafeVolumeWarning(flags); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/audio/SoundEffectsHelper.java b/services/core/java/com/android/server/audio/SoundEffectsHelper.java index c14bb3ee5df1..7031e02af05a 100644 --- a/services/core/java/com/android/server/audio/SoundEffectsHelper.java +++ b/services/core/java/com/android/server/audio/SoundEffectsHelper.java @@ -52,6 +52,7 @@ import java.util.Map; * used by AudioService. As its methods are called on the message handler thread * of AudioService, the actual work is offloaded to a dedicated thread. * This helps keeping AudioService responsive. + * * @hide */ class SoundEffectsHelper { @@ -89,15 +90,18 @@ class SoundEffectsHelper { final String mFileName; int mSampleId; boolean mLoaded; // for effects in SoundPool + Resource(String fileName) { mFileName = fileName; mSampleId = EFFECT_NOT_IN_SOUND_POOL; } + void unload() { mSampleId = EFFECT_NOT_IN_SOUND_POOL; mLoaded = false; } } + // All the fields below are accessed by the worker thread exclusively private final List<Resource> mResources = new ArrayList<Resource>(); private final int[] mEffects = new int[AudioManager.NUM_SOUND_EFFECTS]; // indexes in mResources @@ -116,9 +120,9 @@ class SoundEffectsHelper { } /** - * Unloads samples from the sound pool. - * This method can be called to free some memory when - * sound effects are disabled. + * Unloads samples from the sound pool. + * This method can be called to free some memory when + * sound effects are disabled. */ /*package*/ void unloadSoundEffects() { sendMsg(MSG_UNLOAD_EFFECTS, 0, 0, null, 0); @@ -385,12 +389,12 @@ class SoundEffectsHelper { } } - boolean fastScrollSoundEffectsParsed = allFastScrollSoundsParsed(parserCounter); + boolean navigationRepeatFxParsed = allNavigationRepeatSoundsParsed(parserCounter); boolean homeSoundParsed = parserCounter.getOrDefault(AudioManager.FX_HOME, 0) > 0; - if (fastScrollSoundEffectsParsed || homeSoundParsed) { + if (navigationRepeatFxParsed || homeSoundParsed) { AudioManager audioManager = mContext.getSystemService(AudioManager.class); - if (audioManager != null && fastScrollSoundEffectsParsed) { - audioManager.setFastScrollSoundEffectsEnabled(true); + if (audioManager != null && navigationRepeatFxParsed) { + audioManager.setNavigationRepeatSoundEffectsEnabled(true); } if (audioManager != null && homeSoundParsed) { audioManager.setHomeSoundEffectEnabled(true); @@ -410,13 +414,13 @@ class SoundEffectsHelper { } } - private boolean allFastScrollSoundsParsed(Map<Integer, Integer> parserCounter) { + private boolean allNavigationRepeatSoundsParsed(Map<Integer, Integer> parserCounter) { int numFastScrollSoundEffectsParsed = - parserCounter.getOrDefault(AudioManager.FX_FAST_SCROLL_1, 0) - + parserCounter.getOrDefault(AudioManager.FX_FAST_SCROLL_2, 0) - + parserCounter.getOrDefault(AudioManager.FX_FAST_SCROLL_3, 0) - + parserCounter.getOrDefault(AudioManager.FX_FAST_SCROLL_4, 0); - return numFastScrollSoundEffectsParsed == AudioManager.NUM_FAST_SCROLL_SOUND_EFFECTS; + parserCounter.getOrDefault(AudioManager.FX_FOCUS_NAVIGATION_REPEAT_1, 0) + + parserCounter.getOrDefault(AudioManager.FX_FOCUS_NAVIGATION_REPEAT_2, 0) + + parserCounter.getOrDefault(AudioManager.FX_FOCUS_NAVIGATION_REPEAT_3, 0) + + parserCounter.getOrDefault(AudioManager.FX_FOCUS_NAVIGATION_REPEAT_4, 0); + return numFastScrollSoundEffectsParsed == AudioManager.NUM_NAVIGATION_REPEAT_SOUND_EFFECTS; } private int findOrAddResourceByFileName(String fileName) { diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java index 16f82af93856..8197edc97a05 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java @@ -96,4 +96,9 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier, public int getProtoEnum() { return BiometricsProto.CM_REMOVE; } + + @Override + public boolean interruptsPrecedingClients() { + return true; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java new file mode 100644 index 000000000000..f35a5208f6ed --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java @@ -0,0 +1,82 @@ +/* + * 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.biometrics.sensors.face; + +import android.annotation.NonNull; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.UserHandle; + +import com.android.internal.R; + +public class ReEnrollNotificationUtils { + + private static final String NOTIFICATION_TAG = "FaceService"; + private static final int NOTIFICATION_ID = 1; + + public static void showReEnrollmentNotification(@NonNull Context context) { + final NotificationManager notificationManager = + context.getSystemService(NotificationManager.class); + + final String name = + context.getString(R.string.face_recalibrate_notification_name); + final String title = + context.getString(R.string.face_recalibrate_notification_title); + final String content = + context.getString(R.string.face_recalibrate_notification_content); + + final Intent intent = new Intent("android.settings.FACE_SETTINGS"); + intent.setPackage("com.android.settings"); + + final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, + 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */, + null /* options */, UserHandle.CURRENT); + + final String channelName = "FaceEnrollNotificationChannel"; + + final NotificationChannel channel = new NotificationChannel(channelName, name, + NotificationManager.IMPORTANCE_HIGH); + final Notification notification = new Notification.Builder(context, channelName) + .setSmallIcon(R.drawable.ic_lock) + .setContentTitle(title) + .setContentText(content) + .setSubText(name) + .setOnlyAlertOnce(true) + .setLocalOnly(true) + .setAutoCancel(true) + .setCategory(Notification.CATEGORY_SYSTEM) + .setContentIntent(pendingIntent) + .setVisibility(Notification.VISIBILITY_SECRET) + .build(); + + notificationManager.createNotificationChannel(channel); + notificationManager.notifyAsUser(NOTIFICATION_TAG, + NOTIFICATION_ID, notification, + UserHandle.CURRENT); + } + + public static void cancelNotification(@NonNull Context context) { + final NotificationManager notificationManager = + context.getSystemService(NotificationManager.class); + notificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID, UserHandle.CURRENT); + } + +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index 8f554028ebfd..089cf1e4cee8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -41,6 +41,7 @@ import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutConsumer; import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils; import com.android.server.biometrics.sensors.face.UsageStats; import java.util.ArrayList; @@ -163,6 +164,9 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements vibrateError(); } break; + case BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL: + ReEnrollNotificationUtils.showReEnrollmentNotification(getContext()); + break; default: break; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index 898d81b0c8c4..0eb51fdba159 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -41,6 +41,7 @@ import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.EnrollClient; import com.android.server.biometrics.sensors.face.FaceUtils; +import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils; import java.io.IOException; import java.util.ArrayList; @@ -85,6 +86,13 @@ public class FaceEnrollClient extends EnrollClient<ISession> { } @Override + public void start(@NonNull Callback callback) { + super.start(callback); + + ReEnrollNotificationUtils.cancelNotification(getContext()); + } + + @Override public void destroy() { try { AidlNativeHandleUtils.close(mPreviewSurface); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java index ff65c931dd78..afa5bd22e081 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestHal.java @@ -22,6 +22,7 @@ import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.ISession; import android.hardware.biometrics.face.ISessionCallback; import android.hardware.biometrics.face.SensorProps; +import android.hardware.biometrics.face.SessionState; import android.hardware.common.NativeHandle; import android.hardware.keymaster.HardwareAuthToken; import android.os.RemoteException; @@ -131,6 +132,15 @@ public class TestHal extends IFace.Stub { Slog.w(TAG, "resetLockout, cookie: " + cookie); cb.onLockoutCleared(); } + + @Override + public void close(int cookie) throws RemoteException { + cb.onStateChanged(cookie, SessionState.CLOSED); + } }; } + + @Override + public void reset() { + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index ee8823e041bc..1b9bd7fd0cea 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -19,7 +19,6 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; -import android.app.NotificationManager; import android.app.SynchronousUserSwitchObserver; import android.app.UserSwitchObserver; import android.content.Context; @@ -71,6 +70,7 @@ import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.RemovalConsumer; import com.android.server.biometrics.sensors.face.FaceUtils; import com.android.server.biometrics.sensors.face.LockoutHalImpl; +import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils; import com.android.server.biometrics.sensors.face.ServiceProvider; import com.android.server.biometrics.sensors.face.UsageStats; @@ -96,8 +96,6 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { private static final String TAG = "Face10"; private static final int ENROLL_TIMEOUT_SEC = 75; - static final String NOTIFICATION_TAG = "FaceService"; - static final int NOTIFICATION_ID = 1; private boolean mTestHalEnabled; @@ -109,7 +107,6 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @NonNull private final LockoutHalImpl mLockoutTracker; @NonNull private final UsageStats mUsageStats; - @NonNull private final NotificationManager mNotificationManager; @NonNull private final Map<Integer, Long> mAuthenticatorIds; @Nullable private IBiometricsFace mDaemon; @NonNull private final HalResultController mHalResultController; @@ -343,7 +340,6 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mUsageStats = new UsageStats(context); mAuthenticatorIds = new HashMap<>(); mLazyDaemon = Face10.this::getDaemon; - mNotificationManager = mContext.getSystemService(NotificationManager.class); mLockoutTracker = new LockoutHalImpl(); mLockoutResetDispatcher = lockoutResetDispatcher; mHalResultController = new HalResultController(sensorId, context, mHandler, mScheduler, @@ -607,8 +603,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); - mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID, - UserHandle.CURRENT); + ReEnrollNotificationUtils.cancelNotification(mContext); final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java index a4b3ac57a4df..3ca51d32797e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java @@ -17,12 +17,7 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.Context; -import android.content.Intent; import android.content.res.Resources; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; @@ -32,7 +27,6 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.face.FaceManager; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Slog; import com.android.internal.R; @@ -40,6 +34,7 @@ import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils; import com.android.server.biometrics.sensors.face.UsageStats; import java.util.ArrayList; @@ -52,7 +47,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { private static final String TAG = "FaceAuthenticationClient"; - private final NotificationManager mNotificationManager; + private final UsageStats mUsageStats; private final int[] mBiometricPromptIgnoreList; @@ -72,7 +67,6 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { owner, cookie, requireConfirmation, sensorId, isStrongBiometric, BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */, lockoutTracker, isKeyguard); - mNotificationManager = context.getSystemService(NotificationManager.class); mUsageStats = usageStats; final Resources resources = getContext().getResources(); @@ -188,41 +182,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { mLastAcquire = acquireInfo; if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) { - final String name = - getContext().getString(R.string.face_recalibrate_notification_name); - final String title = - getContext().getString(R.string.face_recalibrate_notification_title); - final String content = - getContext().getString(R.string.face_recalibrate_notification_content); - - final Intent intent = new Intent("android.settings.FACE_SETTINGS"); - intent.setPackage("com.android.settings"); - - final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(getContext(), - 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */, - null /* options */, UserHandle.CURRENT); - - final String channelName = "FaceEnrollNotificationChannel"; - - NotificationChannel channel = new NotificationChannel(channelName, name, - NotificationManager.IMPORTANCE_HIGH); - Notification notification = new Notification.Builder(getContext(), channelName) - .setSmallIcon(R.drawable.ic_lock) - .setContentTitle(title) - .setContentText(content) - .setSubText(name) - .setOnlyAlertOnce(true) - .setLocalOnly(true) - .setAutoCancel(true) - .setCategory(Notification.CATEGORY_SYSTEM) - .setContentIntent(pendingIntent) - .setVisibility(Notification.VISIBILITY_SECRET) - .build(); - - mNotificationManager.createNotificationChannel(channel); - mNotificationManager.notifyAsUser(Face10.NOTIFICATION_TAG, - Face10.NOTIFICATION_ID, notification, - UserHandle.CURRENT); + ReEnrollNotificationUtils.showReEnrollmentNotification(getContext()); } final boolean shouldSend = shouldSend(acquireInfo, vendorCode); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java index 8ed24b6f9d48..9db2fcfd1b5b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestHal.java @@ -22,6 +22,7 @@ import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.biometrics.fingerprint.ISessionCallback; import android.hardware.biometrics.fingerprint.SensorProps; +import android.hardware.biometrics.fingerprint.SessionState; import android.hardware.keymaster.HardwareAuthToken; import android.os.RemoteException; import android.util.Slog; @@ -119,6 +120,11 @@ public class TestHal extends IFingerprint.Stub { } @Override + public void close(int cookie) throws RemoteException { + cb.onStateChanged(cookie, SessionState.CLOSED); + } + + @Override public void onPointerDown(int pointerId, int x, int y, float minor, float major) { Slog.w(TAG, "onPointerDown"); } @@ -134,4 +140,9 @@ public class TestHal extends IFingerprint.Stub { } }; } + + @Override + public void reset() { + } } + diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 507600783aa4..2de709ebe71d 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -61,7 +61,6 @@ import android.widget.Toast; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.UiThread; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; @@ -207,7 +206,7 @@ public class ClipboardService extends SystemService { new ClipData.Item(contents)); synchronized(mClipboards) { setPrimaryClipInternal(getClipboard(0), clip, - android.os.Process.SYSTEM_UID); + android.os.Process.SYSTEM_UID, null); } } }); @@ -247,6 +246,8 @@ public class ClipboardService extends SystemService { ClipData primaryClip; /** UID that set {@link #primaryClip}. */ int primaryClipUid = android.os.Process.NOBODY_UID; + /** Package of the app that set {@link #primaryClip}. */ + String mPrimaryClipPackage; final HashSet<String> activePermissionOwners = new HashSet<String>(); @@ -365,7 +366,7 @@ public class ClipboardService extends SystemService { return; } checkDataOwnerLocked(clip, intendingUid); - setPrimaryClipInternal(clip, intendingUid); + setPrimaryClipInternal(clip, intendingUid, callingPackage); } } @@ -378,7 +379,7 @@ public class ClipboardService extends SystemService { intendingUid, intendingUserId)) { return; } - setPrimaryClipInternal(null, intendingUid); + setPrimaryClipInternal(null, intendingUid, callingPackage); } } @@ -509,6 +510,11 @@ public class ClipboardService extends SystemService { } void setPrimaryClipInternal(@Nullable ClipData clip, int uid) { + setPrimaryClipInternal(clip, uid, null); + } + + private void setPrimaryClipInternal( + @Nullable ClipData clip, int uid, @Nullable String sourcePackage) { // Push clipboard to host, if any if (mHostClipboardMonitor != null) { if (clip == null) { @@ -524,7 +530,7 @@ public class ClipboardService extends SystemService { // Update this user final int userId = UserHandle.getUserId(uid); - setPrimaryClipInternal(getClipboard(userId), clip, uid); + setPrimaryClipInternal(getClipboard(userId), clip, uid, sourcePackage); // Update related users List<UserInfo> related = getRelatedProfiles(userId); @@ -558,7 +564,8 @@ public class ClipboardService extends SystemService { final boolean canCopyIntoProfile = !hasRestriction( UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id); if (canCopyIntoProfile) { - setPrimaryClipInternal(getClipboard(id), clip, uid); + setPrimaryClipInternal( + getClipboard(id), clip, uid, sourcePackage); } } } @@ -568,6 +575,11 @@ public class ClipboardService extends SystemService { void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip, int uid) { + setPrimaryClipInternal(clipboard, clip, uid, null); + } + + private void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip, + int uid, @Nullable String sourcePackage) { revokeUris(clipboard); clipboard.activePermissionOwners.clear(); if (clip == null && clipboard.primaryClip == null) { @@ -576,8 +588,10 @@ public class ClipboardService extends SystemService { clipboard.primaryClip = clip; if (clip != null) { clipboard.primaryClipUid = uid; + clipboard.mPrimaryClipPackage = sourcePackage; } else { clipboard.primaryClipUid = android.os.Process.NOBODY_UID; + clipboard.mPrimaryClipPackage = null; } if (clip != null) { final ClipDescription description = clip.getDescription(); @@ -861,29 +875,34 @@ public class ClipboardService extends SystemService { && mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) { return; } - // Load the labels for the calling app and the app that set the clipboard content. - final long ident = Binder.clearCallingIdentity(); + + // Retrieve the app label of the source of the clip data + CharSequence sourceAppLabel = null; + if (clipboard.mPrimaryClipPackage != null) { + try { + sourceAppLabel = mPm.getApplicationLabel( + mPm.getApplicationInfo(clipboard.mPrimaryClipPackage, 0)); + } catch (PackageManager.NameNotFoundException e) { + // leave label as null + } + } + try { - final IPackageManager pm = AppGlobals.getPackageManager(); + CharSequence callingAppLabel = mPm.getApplicationLabel( + mPm.getApplicationInfo(callingPackage, 0)); String message; - final CharSequence callingLabel = mPm.getApplicationLabel( - pm.getApplicationInfo(callingPackage, 0, userId)); - final String[] packagesForUid = pm.getPackagesForUid(clipboard.primaryClipUid); - if (packagesForUid != null && packagesForUid.length > 0) { - final CharSequence clipLabel = mPm.getApplicationLabel( - pm.getApplicationInfo(packagesForUid[0], 0, - UserHandle.getUserId(clipboard.primaryClipUid))); - message = callingLabel + " pasted from " + clipLabel; + if (sourceAppLabel != null) { + message = callingAppLabel + " pasted from " + sourceAppLabel; } else { - message = callingLabel + " pasted from clipboard"; + message = callingAppLabel + " pasted from clipboard"; } Slog.i(TAG, message); - Toast.makeText(getContext(), UiThread.get().getLooper(), message, Toast.LENGTH_SHORT) - .show(); - } catch (RemoteException e) { - /* ignore */ - } finally { - Binder.restoreCallingIdentity(ident); + Binder.withCleanCallingIdentity(() -> + Toast.makeText(getContext(), UiThread.get().getLooper(), message, + Toast.LENGTH_SHORT) + .show()); + } catch (PackageManager.NameNotFoundException e) { + // do nothing } } } diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index 422991e082a9..66a652053857 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -50,6 +50,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -595,18 +596,24 @@ final class CompatConfig { * Rechecks all the existing overrides for a package. */ void recheckOverrides(String packageName) { + // Local cache of compat changes. Holding a lock on mChanges for the whole duration of the + // method will cause a deadlock. + List<CompatChange> changes; synchronized (mChanges) { - boolean shouldInvalidateCache = false; + changes = new ArrayList<>(mChanges.size()); for (int idx = 0; idx < mChanges.size(); ++idx) { - CompatChange c = mChanges.valueAt(idx); - OverrideAllowedState allowedState = - mOverrideValidator.getOverrideAllowedState(c.getId(), packageName); - shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext); - } - if (shouldInvalidateCache) { - invalidateCache(); + changes.add(mChanges.valueAt(idx)); } } + boolean shouldInvalidateCache = false; + for (CompatChange c: changes) { + OverrideAllowedState allowedState = + mOverrideValidator.getOverrideAllowedState(c.getId(), packageName); + shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext); + } + if (shouldInvalidateCache) { + invalidateCache(); + } } void registerContentObserver() { diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java index 21ef356c962c..4ecc7594a79c 100644 --- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java +++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java @@ -26,6 +26,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.net.NetworkTemplate.NETWORK_TYPE_ALL; +import static android.net.NetworkTemplate.OEM_MANAGED_ALL; import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -226,7 +227,7 @@ public class MultipathPolicyTracker { mNetworkTemplate = new NetworkTemplate( NetworkTemplate.MATCH_MOBILE, subscriberId, new String[] { subscriberId }, null, NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL, - NetworkStats.DEFAULT_NETWORK_NO, NETWORK_TYPE_ALL); + NetworkStats.DEFAULT_NETWORK_NO, NETWORK_TYPE_ALL, OEM_MANAGED_ALL); mUsageCallback = new UsageCallback() { @Override public void onThresholdReached(int networkType, String subscriberId) { @@ -274,7 +275,8 @@ public class MultipathPolicyTracker { null /* networkId, unused for matching mobile networks */, !nc.hasCapability(NET_CAPABILITY_NOT_ROAMING), !nc.hasCapability(NET_CAPABILITY_NOT_METERED), - false /* defaultNetwork, templates should have DEFAULT_NETWORK_ALL */); + false /* defaultNetwork, templates should have DEFAULT_NETWORK_ALL */, + OEM_MANAGED_ALL); } private long getRemainingDailyBudget(long limitBytes, diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index 46c49e7fc28c..641287f0f435 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -16,6 +16,8 @@ package com.android.server.connectivity; +import static com.android.net.module.util.CollectionUtils.contains; + import android.annotation.NonNull; import android.net.ConnectivityManager; import android.net.IDnsResolver; @@ -33,7 +35,6 @@ import android.os.ServiceSpecificException; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; import com.android.net.module.util.NetworkStackConstants; import com.android.server.net.BaseNetworkObserver; @@ -117,8 +118,8 @@ public class Nat464Xlat extends BaseNetworkObserver { @VisibleForTesting protected static boolean requiresClat(NetworkAgentInfo nai) { // TODO: migrate to NetworkCapabilities.TRANSPORT_*. - final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType()); - final boolean connected = ArrayUtils.contains(NETWORK_STATES, nai.networkInfo.getState()); + final boolean supported = contains(NETWORK_TYPES, nai.networkInfo.getType()); + final boolean connected = contains(NETWORK_STATES, nai.networkInfo.getState()); // Only run clat on networks that have a global IPv6 address and don't have a native IPv4 // address. diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index c05e25367d03..4cf527415d7e 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -899,7 +899,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { ? networkAgentConfig.subscriberId : null; return new NetworkState(new NetworkInfo(networkInfo), new LinkProperties(linkProperties), - new NetworkCapabilities(networkCapabilities), network, subscriberId, null); + new NetworkCapabilities(networkCapabilities), network, subscriberId); } } diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index 8bf188696c27..9411e33434d8 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -28,6 +28,8 @@ import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; +import static com.android.net.module.util.CollectionUtils.toIntArray; + import android.annotation.NonNull; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -40,23 +42,21 @@ import android.net.UidRange; import android.os.Build; import android.os.RemoteException; import android.os.ServiceSpecificException; +import android.os.SystemConfigManager; import android.os.UserHandle; import android.os.UserManager; import android.system.OsConstants; -import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; +import com.android.net.module.util.CollectionUtils; import com.android.server.LocalServices; -import com.android.server.SystemConfig; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -80,6 +80,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse private final PackageManager mPackageManager; private final UserManager mUserManager; + private final SystemConfigManager mSystemConfigManager; private final INetd mNetd; private final Dependencies mDeps; @@ -123,6 +124,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse @NonNull final Dependencies deps) { mPackageManager = context.getPackageManager(); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + mSystemConfigManager = context.getSystemService(SystemConfigManager.class); mNetd = netd; mDeps = deps; } @@ -174,20 +176,18 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse mUsers.addAll(mUserManager.getUserHandles(true /* excludeDying */)); - final SparseArray<ArraySet<String>> systemPermission = - SystemConfig.getInstance().getSystemPermissions(); - for (int i = 0; i < systemPermission.size(); i++) { - ArraySet<String> perms = systemPermission.valueAt(i); - int uid = systemPermission.keyAt(i); - int netdPermission = 0; - // Get the uids of native services that have UPDATE_DEVICE_STATS or INTERNET permission. - if (perms != null) { - netdPermission |= perms.contains(UPDATE_DEVICE_STATS) - ? INetd.PERMISSION_UPDATE_DEVICE_STATS : 0; - netdPermission |= perms.contains(INTERNET) - ? INetd.PERMISSION_INTERNET : 0; + final SparseArray<String> netdPermToSystemPerm = new SparseArray<>(); + netdPermToSystemPerm.put(INetd.PERMISSION_INTERNET, INTERNET); + netdPermToSystemPerm.put(INetd.PERMISSION_UPDATE_DEVICE_STATS, UPDATE_DEVICE_STATS); + for (int i = 0; i < netdPermToSystemPerm.size(); i++) { + final int netdPermission = netdPermToSystemPerm.keyAt(i); + final String systemPermission = netdPermToSystemPerm.valueAt(i); + final int[] hasPermissionUids = + mSystemConfigManager.getSystemPermissionUids(systemPermission); + for (int j = 0; j < hasPermissionUids.length; j++) { + final int uid = hasPermissionUids[j]; + netdPermsUids.put(uid, netdPermsUids.get(uid) | netdPermission); } - netdPermsUids.put(uid, netdPermsUids.get(uid) | netdPermission); } log("Users: " + mUsers.size() + ", Apps: " + mApps.size()); update(mUsers, mApps, true); @@ -204,7 +204,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) { return false; } - final int index = ArrayUtils.indexOf(app.requestedPermissions, permission); + final int index = CollectionUtils.indexOf(app.requestedPermissions, permission); if (index < 0 || index >= app.requestedPermissionsFlags.length) return false; return (app.requestedPermissionsFlags[index] & REQUESTED_PERMISSION_GRANTED) != 0; } @@ -246,15 +246,6 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse return mApps.containsKey(uid); } - private int[] toIntArray(Collection<Integer> list) { - int[] array = new int[list.size()]; - int i = 0; - for (Integer item : list) { - array[i++] = item; - } - return array; - } - private void update(Set<UserHandle> users, Map<Integer, Boolean> apps, boolean add) { List<Integer> network = new ArrayList<>(); List<Integer> system = new ArrayList<>(); @@ -662,23 +653,23 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse if (allPermissionAppIds.size() != 0) { mNetd.trafficSetNetPermForUids( INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS, - ArrayUtils.convertToIntArray(allPermissionAppIds)); + toIntArray(allPermissionAppIds)); } if (internetPermissionAppIds.size() != 0) { mNetd.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET, - ArrayUtils.convertToIntArray(internetPermissionAppIds)); + toIntArray(internetPermissionAppIds)); } if (updateStatsPermissionAppIds.size() != 0) { mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS, - ArrayUtils.convertToIntArray(updateStatsPermissionAppIds)); + toIntArray(updateStatsPermissionAppIds)); } if (noPermissionAppIds.size() != 0) { mNetd.trafficSetNetPermForUids(INetd.PERMISSION_NONE, - ArrayUtils.convertToIntArray(noPermissionAppIds)); + toIntArray(noPermissionAppIds)); } if (uninstalledAppIds.size() != 0) { mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UNINSTALLED, - ArrayUtils.convertToIntArray(uninstalledAppIds)); + toIntArray(uninstalledAppIds)); } } catch (RemoteException e) { Log.e(TAG, "Pass appId list of special permission failed." + e); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index a769e88f77d7..67f495a455fb 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -22,6 +22,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.RouteInfo.RTN_THROW; import static android.net.RouteInfo.RTN_UNREACHABLE; import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN; +import static android.os.PowerWhitelistManager.REASON_VPN; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; @@ -113,6 +114,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; +import com.android.net.module.util.NetdUtils; import com.android.net.module.util.NetworkStackConstants; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; @@ -824,7 +826,8 @@ public class Vpn { // a short time, so we can bootstrap the VPN service. DeviceIdleInternal idleController = mDeps.getDeviceIdleInternal(); idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage, - VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS, mUserId, false, "vpn"); + VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS, mUserId, false, REASON_VPN, + "vpn"); // Start the VPN service declared in the app's manifest. Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE); @@ -1509,7 +1512,7 @@ public class Vpn { if (start != -1) ranges.add(new UidRange(start, stop)); } else if (disallowedApplications != null) { // Add all ranges for user skipping UIDs for disallowedApplications. - final UidRange userRange = UidRange.createForUser(userId); + final UidRange userRange = UidRange.createForUser(UserHandle.of(userId)); int start = userRange.start; for (int uid : getAppsUids(disallowedApplications, userId)) { if (uid == start) { @@ -1522,7 +1525,7 @@ public class Vpn { if (start <= userRange.stop) ranges.add(new UidRange(start, userRange.stop)); } else { // Add all UIDs for the user. - ranges.add(UidRange.createForUser(userId)); + ranges.add(UidRange.createForUser(UserHandle.of(userId))); } } @@ -1531,7 +1534,7 @@ public class Vpn { private static List<UidRange> uidRangesForUser(int userId, Set<UidRange> existingRanges) { // UidRange#createForUser returns the entire range of UIDs available to a macro-user. // This is something like 0-99999 ; {@see UserHandle#PER_USER_RANGE} - final UidRange userRange = UidRange.createForUser(userId); + final UidRange userRange = UidRange.createForUser(UserHandle.of(userId)); final List<UidRange> ranges = new ArrayList<>(); for (UidRange range : existingRanges) { if (userRange.containsRange(range)) { @@ -2528,7 +2531,7 @@ public class Vpn { address /* unused */, address /* unused */, network); - mNms.setInterfaceUp(mTunnelIface.getInterfaceName()); + NetdUtils.setInterfaceUp(mNetd, mTunnelIface.getInterfaceName()); mSession = mIkev2SessionCreator.createIkeSession( mContext, diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index fe89903c02d7..ae0e001e8417 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -16,6 +16,8 @@ package com.android.server.content; +import static android.os.PowerWhitelistManager.REASON_SYNC_MANAGER; + import static com.android.server.content.SyncLogger.logSafe; import android.accounts.Account; @@ -264,6 +266,10 @@ public class SyncManager { private final SyncLogger mLogger; + // NOTE: this is a temporary allow-list for testing purposes; it will be removed before release. + private final String[] mEjSyncAllowedPackages = new String[]{ + "com.google.android.google", "com.android.frameworks.servicestests"}; + private boolean isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs) { for (JobInfo job: pendingJobs) { if (job.getId() == jobId) { @@ -983,6 +989,14 @@ public class SyncManager { } } + final boolean scheduleAsEj = + extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false); + // NOTE: this is a temporary check for internal testing - to be removed before release. + if (scheduleAsEj && !ArrayUtils.contains(mEjSyncAllowedPackages, callingPackage)) { + throw new IllegalArgumentException( + callingPackage + " is not allowed to schedule a sync as an EJ yet."); + } + for (AccountAndUser account : accounts) { // If userId is specified, do not sync accounts of other users if (userId >= UserHandle.USER_SYSTEM && account.userId >= UserHandle.USER_SYSTEM @@ -1490,6 +1504,12 @@ public class SyncManager { + logSafe(syncOperation.target)); backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); + } else { + // if an EJ is being backed-off but doesn't have SYNC_EXTRAS_IGNORE_BACKOFF set, + // reschedule it as a regular job + if (syncOperation.isScheduledAsExpeditedJob()) { + syncOperation.scheduleEjAsRegularJob = true; + } } long now = SystemClock.elapsedRealtime(); long backoffDelay = backoff.first == SyncStorageEngine.NOT_IN_BACKOFF_MODE ? 0 @@ -1640,6 +1660,10 @@ public class SyncManager { b.setRequiresCharging(true); } + if (syncOperation.isScheduledAsExpeditedJob() && !syncOperation.scheduleEjAsRegularJob) { + b.setExpedited(true); + } + if (syncOperation.syncExemptionFlag == ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP) { DeviceIdleInternal dic = @@ -1649,7 +1673,7 @@ public class SyncManager { syncOperation.owningPackage, mConstants.getKeyExemptionTempWhitelistDurationInSeconds() * 1000, UserHandle.getUserId(syncOperation.owningUid), - /* sync=*/ false, "sync by top app"); + /* sync=*/ false, REASON_SYNC_MANAGER, "sync by top app"); } } @@ -3951,6 +3975,9 @@ public class SyncManager { if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) { return true; } + if (key.equals(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB)) { + return true; + } if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) { return true; } diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java index 478763531abc..c8654d1b36ee 100644 --- a/services/core/java/com/android/server/content/SyncOperation.java +++ b/services/core/java/com/android/server/content/SyncOperation.java @@ -103,6 +103,13 @@ public class SyncOperation { /** Stores the number of times this sync operation failed and had to be retried. */ int retries; + /** + * Indicates if a sync that was originally scheduled as an EJ is being re-scheduled as a + * regular job. Specifically, this will be {@code true} if a sync is being backed-off but + * {@link ContentResolver#SYNC_EXTRAS_IGNORE_BACKOFF} is not set. + */ + boolean scheduleEjAsRegularJob; + /** jobId of the JobScheduler job corresponding to this sync */ public int jobId; @@ -408,6 +415,12 @@ public class SyncOperation { if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) { sb.append(" EXPEDITED"); } + if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false)) { + sb.append(" EXPEDITED-JOB"); + if (scheduleEjAsRegularJob) { + sb.append("(scheduled-as-regular)"); + } + } switch (syncExemptionFlag) { case ContentResolver.SYNC_EXEMPTION_NONE: break; @@ -537,6 +550,11 @@ public class SyncOperation { return mImmutableExtras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, false); } + boolean isScheduledAsExpeditedJob() { + return mImmutableExtras.getBoolean( + ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false); + } + boolean isAppStandbyExempted() { return syncExemptionFlag != ContentResolver.SYNC_EXEMPTION_NONE; } diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index b3a6f263953f..835b4688dd4e 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -25,6 +25,7 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.hardware.devicestate.DeviceStateInfo; import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.IDeviceStateManager; import android.hardware.devicestate.IDeviceStateManagerCallback; @@ -47,6 +48,7 @@ import com.android.server.policy.DeviceStatePolicyImpl; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Optional; /** @@ -76,6 +78,9 @@ import java.util.Optional; public final class DeviceStateManagerService extends SystemService { private static final String TAG = "DeviceStateManagerService"; private static final boolean DEBUG = false; + // The device state to use as a placeholder before callback from the DeviceStateProvider occurs. + private static final DeviceState UNSPECIFIED_DEVICE_STATE = + new DeviceState(MINIMUM_DEVICE_STATE, "UNSPECIFIED"); private final Object mLock = new Object(); @NonNull @@ -87,11 +92,11 @@ public final class DeviceStateManagerService extends SystemService { @GuardedBy("mLock") private SparseArray<DeviceState> mDeviceStates = new SparseArray<>(); - // The current committed device state. The default of UNSET will be replaced by - // the current state after the initial callback from the DeviceStateProvider. + // The current committed device state. The default of UNSPECIFIED_DEVICE_STATE will be replaced + // by the current state after the initial callback from the DeviceStateProvider. @GuardedBy("mLock") @NonNull - private DeviceState mCommittedState = new DeviceState(MINIMUM_DEVICE_STATE, "UNSET"); + private DeviceState mCommittedState = UNSPECIFIED_DEVICE_STATE; // The device state that is currently awaiting callback from the policy to be committed. @GuardedBy("mLock") @NonNull @@ -103,7 +108,7 @@ public final class DeviceStateManagerService extends SystemService { // The device state that is set by the device state provider. @GuardedBy("mLock") @NonNull - private Optional<DeviceState> mBaseState = Optional.empty(); + private DeviceState mBaseState = UNSPECIFIED_DEVICE_STATE; // List of processes registered to receive notifications about changes to device state and // request status indexed by process id. @@ -166,7 +171,7 @@ public final class DeviceStateManagerService extends SystemService { * @see #getOverrideState() */ @NonNull - Optional<DeviceState> getBaseState() { + DeviceState getBaseState() { synchronized (mLock) { return mBaseState; } @@ -203,33 +208,47 @@ public final class DeviceStateManagerService extends SystemService { /** Returns the list of currently supported device state identifiers. */ private int[] getSupportedStateIdentifiers() { synchronized (mLock) { - int[] supportedStates = new int[mDeviceStates.size()]; - for (int i = 0; i < supportedStates.length; i++) { - supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier(); - } - return supportedStates; + return getSupportedStateIdentifiersLocked(); } } + /** Returns the list of currently supported device state identifiers. */ + private int[] getSupportedStateIdentifiersLocked() { + int[] supportedStates = new int[mDeviceStates.size()]; + for (int i = 0; i < supportedStates.length; i++) { + supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier(); + } + return supportedStates; + } + + @NonNull + private DeviceStateInfo getDeviceStateInfoLocked() { + final int[] supportedStates = getSupportedStateIdentifiersLocked(); + final int baseState = mBaseState.getIdentifier(); + final int currentState = mCommittedState.getIdentifier(); + + return new DeviceStateInfo(supportedStates, baseState, currentState); + } + @VisibleForTesting IDeviceStateManager getBinderService() { return mBinderService; } private void updateSupportedStates(DeviceState[] supportedDeviceStates) { + boolean updatedPendingState; synchronized (mLock) { + final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked(); + mDeviceStates.clear(); for (int i = 0; i < supportedDeviceStates.length; i++) { DeviceState state = supportedDeviceStates[i]; mDeviceStates.put(state.getIdentifier(), state); } - if (mBaseState.isPresent() - && !isSupportedStateLocked(mBaseState.get().getIdentifier())) { - // The current base state is no longer valid. We'll clear it here, though - // we won't actually update the current state until a callback comes from the - // provider with the most recent state. - mBaseState = Optional.empty(); + final int[] newStateIdentifiers = getSupportedStateIdentifiersLocked(); + if (Arrays.equals(oldStateIdentifiers, newStateIdentifiers)) { + return; } final int requestSize = mRequestRecords.size(); @@ -240,7 +259,14 @@ public final class DeviceStateManagerService extends SystemService { } } - updatePendingStateLocked(); + updatedPendingState = updatePendingStateLocked(); + } + + if (!updatedPendingState) { + // If the change in the supported states didn't result in a change of the pending state + // commitPendingState() will never be called and the callbacks will never be notified + // of the change. + notifyDeviceStateInfoChanged(); } notifyRequestsOfStatusChangeIfNeeded(); @@ -265,23 +291,25 @@ public final class DeviceStateManagerService extends SystemService { } /** - * Requests to set the base state. The request may not be honored under certain conditions, for - * example if the provided state is not supported. + * Sets the base state. + * + * @throws IllegalArgumentException if the {@code identifier} is not a supported state. * * @see #isSupportedStateLocked(int) */ private void setBaseState(int identifier) { + boolean updatedPendingState; synchronized (mLock) { - if (mBaseState.isPresent() && mBaseState.get().getIdentifier() == identifier) { - // Base state hasn't changed. Nothing to do. - return; - } - - final Optional<DeviceState> baseState = getStateLocked(identifier); - if (!baseState.isPresent()) { + final Optional<DeviceState> baseStateOptional = getStateLocked(identifier); + if (!baseStateOptional.isPresent()) { throw new IllegalArgumentException("Base state is not supported"); } + final DeviceState baseState = baseStateOptional.get(); + if (mBaseState.equals(baseState)) { + // Base state hasn't changed. Nothing to do. + return; + } mBaseState = baseState; final int requestSize = mRequestRecords.size(); @@ -292,7 +320,14 @@ public final class DeviceStateManagerService extends SystemService { } } - updatePendingStateLocked(); + updatedPendingState = updatePendingStateLocked(); + } + + if (!updatedPendingState) { + // If the change in base state didn't result in a change of the pending state + // commitPendingState() will never be called and the callbacks will never be notified + // of the change. + notifyDeviceStateInfoChanged(); } notifyRequestsOfStatusChangeIfNeeded(); @@ -303,32 +338,39 @@ public final class DeviceStateManagerService extends SystemService { * Tries to update the current pending state with the current requested state. Must call * {@link #notifyPolicyIfNeeded()} to actually notify the policy that the state is being * changed. + * + * @return {@code true} if the pending state has changed as a result of this call, {@code false} + * otherwise. */ - private void updatePendingStateLocked() { + private boolean updatePendingStateLocked() { if (mPendingState.isPresent()) { // Have pending state, can not configure a new state until the state is committed. - return; + return false; } final DeviceState stateToConfigure; if (!mRequestRecords.isEmpty()) { stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState; + } else if (isSupportedStateLocked(mBaseState.getIdentifier())) { + // Base state could have recently become unsupported after a change in supported states. + stateToConfigure = mBaseState; } else { - stateToConfigure = mBaseState.orElse(null); + stateToConfigure = null; } if (stateToConfigure == null) { // No currently requested state. - return; + return false; } - if (stateToConfigure == mCommittedState) { + if (stateToConfigure.equals(mCommittedState)) { // The state requesting to be committed already matches the current committed state. - return; + return false; } mPendingState = Optional.of(stateToConfigure); mIsPolicyWaitingForState = true; + return true; } /** @@ -374,13 +416,11 @@ public final class DeviceStateManagerService extends SystemService { */ private void commitPendingState() { // Update the current state. - int newState; synchronized (mLock) { if (DEBUG) { Slog.d(TAG, "Committing state: " + mPendingState); } mCommittedState = mPendingState.get(); - newState = mCommittedState.getIdentifier(); if (!mRequestRecords.isEmpty()) { final OverrideRequestRecord topRequest = @@ -393,7 +433,7 @@ public final class DeviceStateManagerService extends SystemService { } // Notify callbacks of a change. - notifyDeviceStateChanged(newState); + notifyDeviceStateInfoChanged(); // Notify the top request that it's active. notifyRequestsOfStatusChangeIfNeeded(); @@ -402,14 +442,15 @@ public final class DeviceStateManagerService extends SystemService { notifyPolicyIfNeeded(); } - private void notifyDeviceStateChanged(int deviceState) { + private void notifyDeviceStateInfoChanged() { if (Thread.holdsLock(mLock)) { throw new IllegalStateException( "Attempting to notify callbacks with service lock held."); } - // Grab the lock and copy the process records. + // Grab the lock and copy the process records and the current info. ArrayList<ProcessRecord> registeredProcesses; + DeviceStateInfo info; synchronized (mLock) { if (mProcessRecords.size() == 0) { return; @@ -419,11 +460,13 @@ public final class DeviceStateManagerService extends SystemService { for (int i = 0; i < mProcessRecords.size(); i++) { registeredProcesses.add(mProcessRecords.valueAt(i)); } + + info = getDeviceStateInfoLocked(); } // After releasing the lock, send the notifications out. for (int i = 0; i < registeredProcesses.size(); i++) { - registeredProcesses.get(i).notifyDeviceStateAsync(deviceState); + registeredProcesses.get(i).notifyDeviceStateInfoAsync(info); } } @@ -454,28 +497,20 @@ public final class DeviceStateManagerService extends SystemService { } private void registerProcess(int pid, IDeviceStateManagerCallback callback) { - int currentState; - ProcessRecord record; - // Grab the lock to register the callback and get the current state. synchronized (mLock) { if (mProcessRecords.contains(pid)) { throw new SecurityException("The calling process has already registered an" + " IDeviceStateManagerCallback."); } - record = new ProcessRecord(callback, pid); + ProcessRecord record = new ProcessRecord(callback, pid); try { callback.asBinder().linkToDeath(record, 0); } catch (RemoteException ex) { throw new RuntimeException(ex); } - mProcessRecords.put(pid, record); - currentState = mCommittedState.getIdentifier(); } - - // Notify the callback of the state at registration. - record.notifyDeviceStateAsync(currentState); } private void handleProcessDied(ProcessRecord processRecord) { @@ -626,9 +661,9 @@ public final class DeviceStateManagerService extends SystemService { handleProcessDied(this); } - public void notifyDeviceStateAsync(int devicestate) { + public void notifyDeviceStateInfoAsync(@NonNull DeviceStateInfo info) { try { - mCallback.onDeviceStateChanged(devicestate); + mCallback.onDeviceStateInfoChanged(info); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.", ex); @@ -752,6 +787,13 @@ public final class DeviceStateManagerService extends SystemService { /** Implementation of {@link IDeviceStateManager} published as a binder service. */ private final class BinderService extends IDeviceStateManager.Stub { @Override // Binder call + public DeviceStateInfo getDeviceStateInfo() { + synchronized (mLock) { + return getDeviceStateInfoLocked(); + } + } + + @Override // Binder call public void registerCallback(IDeviceStateManagerCallback callback) { if (callback == null) { throw new IllegalArgumentException("Device state callback must not be null."); @@ -767,16 +809,6 @@ public final class DeviceStateManagerService extends SystemService { } @Override // Binder call - public int[] getSupportedDeviceStates() { - final long token = Binder.clearCallingIdentity(); - try { - return getSupportedStateIdentifiers(); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override // Binder call public void requestState(IBinder token, int state, int flags) { getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE, "Permission required to request device state."); diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java index 6cc55a6c4774..f3466006bd30 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java @@ -64,13 +64,13 @@ public class DeviceStateManagerShellCommand extends ShellCommand { private void printState(PrintWriter pw) { DeviceState committedState = mService.getCommittedState(); - Optional<DeviceState> baseState = mService.getBaseState(); + DeviceState baseState = mService.getBaseState(); Optional<DeviceState> overrideState = mService.getOverrideState(); pw.println("Committed state: " + committedState); if (overrideState.isPresent()) { pw.println("----------------------"); - pw.println("Base state: " + baseState.orElse(null)); + pw.println("Base state: " + baseState); pw.println("Override state: " + overrideState.get()); } } diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 225da7ad87b3..a2b9b966dd46 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -354,6 +354,10 @@ class AutomaticBrightnessController { } } + public void stop() { + setLightSensorEnabled(false); + } + public boolean hasUserDataPoints() { return mBrightnessMapper.hasUserDataPoints(); } diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index a62f67a743ad..30cbf2745638 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -61,7 +61,10 @@ public abstract class BrightnessMappingStrategy { private static final Plog PLOG = Plog.createSystemPlog(TAG); @Nullable - public static BrightnessMappingStrategy create(Resources resources) { + public static BrightnessMappingStrategy create(Resources resources, + DisplayDeviceConfig displayDeviceConfig) { + + // Display independent values float[] luxLevels = getLuxLevels(resources.getIntArray( com.android.internal.R.array.config_autoBrightnessLevels)); int[] brightnessLevelsBacklight = resources.getIntArray( @@ -71,32 +74,22 @@ public abstract class BrightnessMappingStrategy { float autoBrightnessAdjustmentMaxGamma = resources.getFraction( com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 1, 1); - - float[] nitsRange = getFloatArray(resources.obtainTypedArray( - com.android.internal.R.array.config_screenBrightnessNits)); - int[] backlightRange = resources.getIntArray( - com.android.internal.R.array.config_screenBrightnessBacklight); - long shortTermModelTimeout = resources.getInteger( com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout); - if (isValidMapping(nitsRange, backlightRange) + // Display dependent values - used for physical mapping strategy nits -> brightness + final float[] nitsRange = displayDeviceConfig.getNits(); + final float[] brightnessRange = displayDeviceConfig.getBrightness(); + + if (isValidMapping(nitsRange, brightnessRange) && isValidMapping(luxLevels, brightnessLevelsNits)) { - int minimumBacklight = resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessSettingMinimum); - int maximumBacklight = resources.getInteger( - com.android.internal.R.integer.config_screenBrightnessSettingMaximum); - if (backlightRange[0] > minimumBacklight - || backlightRange[backlightRange.length - 1] < maximumBacklight) { - Slog.w(TAG, "Screen brightness mapping does not cover whole range of available " + - "backlight values, autobrightness functionality may be impaired."); - } + BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder( luxLevels, brightnessLevelsNits); builder.setShortTermModelTimeoutMillis(shortTermModelTimeout); builder.setShortTermModelLowerLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO); builder.setShortTermModelUpperLuxMultiplier(SHORT_TERM_MODEL_THRESHOLD_RATIO); - return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange, + return new PhysicalMappingStrategy(builder.build(), nitsRange, brightnessRange, autoBrightnessAdjustmentMaxGamma); } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) { return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight, @@ -264,11 +257,11 @@ public abstract class BrightnessMappingStrategy { public abstract boolean setAutoBrightnessAdjustment(float adjustment); /** - * Converts the provided backlight value to nits if possible. + * Converts the provided brightness value to nits if possible. * - * Returns -1.0f if there's no available mapping for the backlight to nits. + * Returns -1.0f if there's no available mapping for the brightness to nits. */ - public abstract float convertToNits(int backlight); + public abstract float convertToNits(float brightness); /** * Adds a user interaction data point to the brightness mapping. @@ -603,7 +596,7 @@ public abstract class BrightnessMappingStrategy { } @Override - public float convertToNits(int backlight) { + public float convertToNits(float brightness) { return -1.0f; } @@ -701,37 +694,39 @@ public abstract class BrightnessMappingStrategy { // in nits. private Spline mBrightnessSpline; - // A spline mapping from nits to the corresponding backlight value, normalized to the range + // A spline mapping from nits to the corresponding brightness value, normalized to the range // [0, 1.0]. - private Spline mNitsToBacklightSpline; + private Spline mNitsToBrightnessSpline; + + // A spline mapping from the system brightness value, normalized to the range [0, 1.0], to + // a brightness in nits. + private Spline mBrightnessToNitsSpline; // The default brightness configuration. private final BrightnessConfiguration mDefaultConfig; - // A spline mapping from the device's backlight value, normalized to the range [0, 1.0], to - // a brightness in nits. - private Spline mBacklightToNitsSpline; - - private float[] mNits; - private int[] mBacklight; + private final float[] mNits; + private final float[] mBrightness; private boolean mBrightnessRangeAdjustmentApplied; - private float mMaxGamma; + private final float mMaxGamma; private float mAutoBrightnessAdjustment; private float mUserLux; private float mUserBrightness; public PhysicalMappingStrategy(BrightnessConfiguration config, float[] nits, - int[] backlight, float maxGamma) { - Preconditions.checkArgument(nits.length != 0 && backlight.length != 0, - "Nits and backlight arrays must not be empty!"); - Preconditions.checkArgument(nits.length == backlight.length, - "Nits and backlight arrays must be the same length!"); + float[] brightness, float maxGamma) { + + Preconditions.checkArgument(nits.length != 0 && brightness.length != 0, + "Nits and brightness arrays must not be empty!"); + + Preconditions.checkArgument(nits.length == brightness.length, + "Nits and brightness arrays must be the same length!"); Objects.requireNonNull(config); Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits"); - Preconditions.checkArrayElementsInRange(backlight, - PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON, "backlight"); + Preconditions.checkArrayElementsInRange(brightness, + PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, "brightness"); mMaxGamma = maxGamma; mAutoBrightnessAdjustment = 0; @@ -739,7 +734,7 @@ public abstract class BrightnessMappingStrategy { mUserBrightness = -1; mNits = nits; - mBacklight = backlight; + mBrightness = brightness; computeNitsBrightnessSplines(mNits); mDefaultConfig = config; @@ -784,15 +779,15 @@ public abstract class BrightnessMappingStrategy { public float getBrightness(float lux, String packageName, @ApplicationInfo.Category int category) { float nits = mBrightnessSpline.interpolate(lux); - float backlight = mNitsToBacklightSpline.interpolate(nits); + float brightness = mNitsToBrightnessSpline.interpolate(nits); // Correct the brightness according to the current application and its category, but - // only if no user data point is set (as this will oevrride the user setting). + // only if no user data point is set (as this will override the user setting). if (mUserLux == -1) { - backlight = correctBrightness(backlight, packageName, category); + brightness = correctBrightness(brightness, packageName, category); } else if (mLoggingEnabled) { Slog.d(TAG, "user point set, correction not applied"); } - return backlight; + return brightness; } @Override @@ -817,8 +812,8 @@ public abstract class BrightnessMappingStrategy { } @Override - public float convertToNits(int backlight) { - return mBacklightToNitsSpline.interpolate(normalizeAbsoluteBrightness(backlight)); + public float convertToNits(float brightness) { + return mBrightnessToNitsSpline.interpolate(brightness); } @Override @@ -884,7 +879,8 @@ public abstract class BrightnessMappingStrategy { pw.println("PhysicalMappingStrategy"); pw.println(" mConfig=" + mConfig); pw.println(" mBrightnessSpline=" + mBrightnessSpline); - pw.println(" mNitsToBacklightSpline=" + mNitsToBacklightSpline); + pw.println(" mNitsToBrightnessSpline=" + mNitsToBrightnessSpline); + pw.println(" mBrightnessToNitsSpline=" + mBrightnessToNitsSpline); pw.println(" mMaxGamma=" + mMaxGamma); pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); pw.println(" mUserLux=" + mUserLux); @@ -894,31 +890,25 @@ public abstract class BrightnessMappingStrategy { } private void computeNitsBrightnessSplines(float[] nits) { - final int len = nits.length; - float[] normalizedBacklight = new float[len]; - for (int i = 0; i < len; i++) { - normalizedBacklight[i] = normalizeAbsoluteBrightness(mBacklight[i]); - } - - mNitsToBacklightSpline = Spline.createSpline(nits, normalizedBacklight); - mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits); + mNitsToBrightnessSpline = Spline.createSpline(nits, mBrightness); + mBrightnessToNitsSpline = Spline.createSpline(mBrightness, nits); } private void computeSpline() { Pair<float[], float[]> defaultCurve = mConfig.getCurve(); float[] defaultLux = defaultCurve.first; float[] defaultNits = defaultCurve.second; - float[] defaultBacklight = new float[defaultNits.length]; - for (int i = 0; i < defaultBacklight.length; i++) { - defaultBacklight[i] = mNitsToBacklightSpline.interpolate(defaultNits[i]); + float[] defaultBrightness = new float[defaultNits.length]; + for (int i = 0; i < defaultBrightness.length; i++) { + defaultBrightness[i] = mNitsToBrightnessSpline.interpolate(defaultNits[i]); } - Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBacklight, mUserLux, + Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBrightness, mUserLux, mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma); float[] lux = curve.first; - float[] backlight = curve.second; - float[] nits = new float[backlight.length]; + float[] brightness = curve.second; + float[] nits = new float[brightness.length]; for (int i = 0; i < nits.length; i++) { - nits[i] = mBacklightToNitsSpline.interpolate(backlight[i]); + nits[i] = mBrightnessToNitsSpline.interpolate(brightness[i]); } mBrightnessSpline = Spline.createSpline(lux, nits); } @@ -926,7 +916,7 @@ public abstract class BrightnessMappingStrategy { private float getUnadjustedBrightness(float lux) { Pair<float[], float[]> curve = mConfig.getCurve(); Spline spline = Spline.createSpline(curve.first, curve.second); - return mNitsToBacklightSpline.interpolate(spline.interpolate(lux)); + return mNitsToBrightnessSpline.interpolate(spline.interpolate(lux)); } private float correctBrightness(float brightness, String packageName, int category) { diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index a186e334c5c0..06010f51e231 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -243,7 +243,6 @@ public class BrightnessTracker { } /** Stop listening for events */ - @VisibleForTesting void stop() { if (DEBUG) { Slog.d(TAG, "Stop"); diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index 5e1df27167c2..f2126987ff19 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -165,7 +165,7 @@ final class ColorFade { "Failed to take screenshot because internal display is disconnected"); return false; } - boolean isWideColor = SurfaceControl.getActiveColorMode(token) + boolean isWideColor = SurfaceControl.getDynamicDisplayInfo(token).activeColorMode == Display.COLOR_MODE_DISPLAY_P3; // Set mPrepared here so if initialization fails, resources can be cleaned up. @@ -817,6 +817,12 @@ final class ColorFade { } DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId); + if (displayInfo == null) { + // displayInfo can be null if the associated display has been removed. There + // is a delay between the display being removed and ColorFade being dismissed. + return; + } + switch (displayInfo.rotation) { case Surface.ROTATION_0: t.setPosition(mSurfaceControl, 0, 0); diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index cd17cfef2726..b3070b7cf1ba 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -16,6 +16,7 @@ package com.android.server.display; +import android.content.Context; import android.graphics.Rect; import android.hardware.display.DisplayViewport; import android.os.IBinder; @@ -38,12 +39,14 @@ abstract class DisplayDevice { private final IBinder mDisplayToken; private final String mUniqueId; + protected DisplayDeviceConfig mDisplayDeviceConfig; // The display device does not manage these properties itself, they are set by // the display manager service. The display device shouldn't really be looking at these. private int mCurrentLayerStack = -1; private int mCurrentOrientation = -1; private Rect mCurrentLayerStackRect; private Rect mCurrentDisplayRect; + private final Context mContext; // The display device owns its surface, but it should only set it // within a transaction from performTraversalLocked. @@ -53,10 +56,13 @@ abstract class DisplayDevice { // Do not use for any other purpose. DisplayDeviceInfo mDebugLastLoggedDeviceInfo; - public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId) { + public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken, String uniqueId, + Context context) { mDisplayAdapter = displayAdapter; mDisplayToken = displayToken; mUniqueId = uniqueId; + mDisplayDeviceConfig = null; + mContext = context; } /** @@ -74,7 +80,10 @@ abstract class DisplayDevice { * @return The DisplayDeviceConfig; {@code null} if not overridden. */ public DisplayDeviceConfig getDisplayDeviceConfig() { - return null; + if (mDisplayDeviceConfig == null) { + mDisplayDeviceConfig = loadDisplayDeviceConfig(); + } + return mDisplayDeviceConfig; } /** @@ -292,4 +301,8 @@ abstract class DisplayDevice { pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect); pw.println("mCurrentSurface=" + mCurrentSurface); } + + private DisplayDeviceConfig loadDisplayDeviceConfig() { + return DisplayDeviceConfig.create(mContext, false); + } } diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 1b25427adf71..0071b2f558c4 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -18,9 +18,12 @@ package com.android.server.display; import android.annotation.NonNull; import android.content.Context; +import android.content.res.Resources; import android.os.Environment; import android.os.PowerManager; +import android.util.MathUtils; import android.util.Slog; +import android.util.Spline; import android.view.DisplayAddress; import com.android.internal.R; @@ -72,15 +75,31 @@ public class DisplayDeviceConfig { private final Context mContext; + // Nits and backlight values that are loaded from either the display device config file, or + // config.xml. These are the raw values and just used for the dumpsys + private float[] mRawNits; + private float[] mRawBacklight; + + // These arrays are calculated from the raw arrays, but clamped to contain values equal to and + // between mBacklightMinimum and mBacklightMaximum. These three arrays should all be the same + // length + // Nits array that is used to store the entire range of nits values that the device supports private float[] mNits; + // Backlight array holds the values that the HAL uses to display the corresponding nits values + private float[] mBacklight; + // Purely an array that covers the ranges of values 0.0 - 1.0, indicating the system brightness + // for the corresponding values above private float[] mBrightness; - private float mBrightnessMinimum = Float.NaN; - private float mBrightnessMaximum = Float.NaN; + + private float mBacklightMinimum = Float.NaN; + private float mBacklightMaximum = Float.NaN; private float mBrightnessDefault = Float.NaN; private float mBrightnessRampFastDecrease = Float.NaN; private float mBrightnessRampFastIncrease = Float.NaN; private float mBrightnessRampSlowDecrease = Float.NaN; private float mBrightnessRampSlowIncrease = Float.NaN; + private Spline mBrightnessToBacklightSpline; + private Spline mBacklightToBrightnessSpline; private List<String> mQuirks; private boolean mIsHighBrightnessModeEnabled = false; private HighBrightnessModeData mHbmData; @@ -120,7 +139,20 @@ public class DisplayDeviceConfig { // If no config can be loaded from any ddc xml at all, // prepare a whole config using the global config.xml. // Guaranteed not null - if (isDefaultDisplay) { + return create(context, isDefaultDisplay); + } + + /** + * Creates an instance using global values since no display device config xml exists. + * Uses values from config or PowerManager. + * + * @param context + * @param useConfigXml + * @return A configuration instance. + */ + public static DisplayDeviceConfig create(Context context, boolean useConfigXml) { + DisplayDeviceConfig config; + if (useConfigXml) { config = getConfigFromGlobalXml(context); } else { config = getConfigFromPmValues(context); @@ -154,7 +186,7 @@ public class DisplayDeviceConfig { } /** - * Return the brightness mapping nits array if one is defined in the configuration file. + * Return the brightness mapping nits array. * * @return The brightness mapping nits array. */ @@ -163,22 +195,40 @@ public class DisplayDeviceConfig { } /** - * Return the brightness mapping value array if one is defined in the configuration file. + * Return the brightness mapping backlight array. * - * @return The brightness mapping value array. + * @return The backlight mapping value array. */ - public float[] getBrightness() { - return mBrightness; + public float[] getBacklight() { + return mBacklight; } - public float getBrightnessMinimum() { - return mBrightnessMinimum; + /** + * Calculates the backlight value, as recognised by the HAL, from the brightness value + * given that the rest of the system deals with. + * + * @param brightness value on the framework scale of 0-1 + * @return backlight value on the HAL scale of 0-1 + */ + public float getBacklightFromBrightness(float brightness) { + return mBrightnessToBacklightSpline.interpolate(brightness); } - public float getBrightnessMaximum() { - return mBrightnessMaximum; + /** + * Return an array of equal length to backlight and nits, that covers the entire system + * brightness range of 0.0-1.0. + * + * @return brightness array + */ + public float[] getBrightness() { + return mBrightness; } + /** + * Return the default brightness on a scale of 0.0f - 1.0f + * + * @return default brightness + */ public float getBrightnessDefault() { return mBrightnessDefault; } @@ -224,10 +274,15 @@ public class DisplayDeviceConfig { @Override public String toString() { String str = "DisplayDeviceConfig{" - + "mBrightness=" + Arrays.toString(mBrightness) + + "mBacklight=" + Arrays.toString(mBacklight) + ", mNits=" + Arrays.toString(mNits) - + ", mBrightnessMinimum=" + mBrightnessMinimum - + ", mBrightnessMaximum=" + mBrightnessMaximum + + ", mRawBacklight=" + Arrays.toString(mRawBacklight) + + ", mRawNits=" + Arrays.toString(mRawNits) + + ", mBrightness=" + Arrays.toString(mBrightness) + + ", mBrightnessToBacklightSpline=" + mBrightnessToBacklightSpline + + ", mBacklightToBrightnessSpline=" + mBacklightToBrightnessSpline + + ", mBacklightMinimum=" + mBacklightMinimum + + ", mBacklightMaximum=" + mBacklightMaximum + ", mBrightnessDefault=" + mBrightnessDefault + ", mQuirks=" + mQuirks + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled @@ -240,10 +295,6 @@ public class DisplayDeviceConfig { return str; } - private float getMaxBrightness() { - return mBrightness[mBrightness.length - 1]; - } - private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory, String suffixFormat, long idNumber) { @@ -251,7 +302,6 @@ public class DisplayDeviceConfig { final String filename = String.format(CONFIG_FILE_FORMAT, suffix); final File filePath = Environment.buildPath( baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename); - if (filePath.exists()) { final DisplayDeviceConfig config = new DisplayDeviceConfig(context); config.initFromFile(filePath); @@ -286,9 +336,9 @@ public class DisplayDeviceConfig { try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) { final DisplayConfiguration config = XmlParser.read(in); if (config != null) { - loadBrightnessMap(config); loadBrightnessDefaultFromDdcXml(config); loadBrightnessConstraintsFromConfigXml(); + loadBrightnessMap(config); loadHighBrightnessModeData(config); loadQuirks(config); loadBrightnessRamps(config); @@ -305,13 +355,20 @@ public class DisplayDeviceConfig { // If no ddc exists, use config.xml loadBrightnessDefaultFromConfigXml(); loadBrightnessConstraintsFromConfigXml(); + loadBrightnessMapFromConfigXml(); loadBrightnessRampsFromConfigXml(); } private void initFromPmValues() { - mBrightnessMinimum = PowerManager.BRIGHTNESS_MIN; - mBrightnessMaximum = PowerManager.BRIGHTNESS_MAX; + // Set all to basic values + mBacklightMinimum = PowerManager.BRIGHTNESS_MIN; + mBacklightMaximum = PowerManager.BRIGHTNESS_MAX; mBrightnessDefault = BRIGHTNESS_DEFAULT; + mBrightnessRampFastDecrease = PowerManager.BRIGHTNESS_MAX; + mBrightnessRampFastIncrease = PowerManager.BRIGHTNESS_MAX; + mBrightnessRampSlowDecrease = PowerManager.BRIGHTNESS_MAX; + mBrightnessRampSlowIncrease = PowerManager.BRIGHTNESS_MAX; + setSimpleMappingStrategyValues(); } private void loadBrightnessDefaultFromDdcXml(DisplayConfiguration config) { @@ -351,24 +408,27 @@ public class DisplayDeviceConfig { final float max = mContext.getResources().getFloat(com.android.internal.R.dimen .config_screenBrightnessSettingMaximumFloat); if (min == INVALID_BRIGHTNESS_IN_CONFIG || max == INVALID_BRIGHTNESS_IN_CONFIG) { - mBrightnessMinimum = BrightnessSynchronizer.brightnessIntToFloat( + mBacklightMinimum = BrightnessSynchronizer.brightnessIntToFloat( mContext.getResources().getInteger(com.android.internal.R.integer .config_screenBrightnessSettingMinimum)); - mBrightnessMaximum = BrightnessSynchronizer.brightnessIntToFloat( + mBacklightMaximum = BrightnessSynchronizer.brightnessIntToFloat( mContext.getResources().getInteger(com.android.internal.R.integer .config_screenBrightnessSettingMaximum)); } else { - mBrightnessMinimum = min; - mBrightnessMaximum = max; + mBacklightMinimum = min; + mBacklightMaximum = max; } } private void loadBrightnessMap(DisplayConfiguration config) { final NitsMap map = config.getScreenBrightnessMap(); - // Map may not exist in config file + // Map may not exist in display device config if (map == null) { + loadBrightnessMapFromConfigXml(); return; } + + // Use the (preferred) display device config mapping final List<Point> points = map.getPoint(); final int size = points.size(); @@ -395,8 +455,123 @@ public class DisplayDeviceConfig { } ++i; } - mNits = nits; - mBrightness = backlight; + mRawNits = nits; + mRawBacklight = backlight; + constrainNitsAndBacklightArrays(); + } + + private void loadBrightnessMapFromConfigXml() { + // Use the config.xml mapping + final Resources res = mContext.getResources(); + final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray( + com.android.internal.R.array.config_screenBrightnessNits)); + final int[] sysBrightness = res.getIntArray( + com.android.internal.R.array.config_screenBrightnessBacklight); + final float[] sysBrightnessFloat = new float[sysBrightness.length]; + + for (int i = 0; i < sysBrightness.length; i++) { + sysBrightnessFloat[i] = BrightnessSynchronizer.brightnessIntToFloat( + sysBrightness[i]); + } + + // These arrays are allowed to be empty, we set null values so that + // BrightnessMappingStrategy will create a SimpleMappingStrategy instead. + if (sysBrightnessFloat.length == 0 || sysNits.length == 0) { + setSimpleMappingStrategyValues(); + return; + } + + mRawNits = sysNits; + mRawBacklight = sysBrightnessFloat; + constrainNitsAndBacklightArrays(); + } + + private void setSimpleMappingStrategyValues() { + // No translation from backlight to brightness should occur if we are using a + // SimpleMappingStrategy (ie they should be the same) so the splines are + // set to be linear, between 0.0 and 1.0 + mNits = null; + mBacklight = null; + float[] simpleMappingStrategyArray = new float[]{0.0f, 1.0f}; + mBrightnessToBacklightSpline = Spline.createSpline(simpleMappingStrategyArray, + simpleMappingStrategyArray); + mBacklightToBrightnessSpline = Spline.createSpline(simpleMappingStrategyArray, + simpleMappingStrategyArray); + } + + /** + * Change the nits and backlight arrays, so that they cover only the allowed backlight values + * Use the brightness minimum and maximum values to clamp these arrays. + */ + private void constrainNitsAndBacklightArrays() { + if (mRawBacklight[0] > mBacklightMinimum + || mRawBacklight[mRawBacklight.length - 1] < mBacklightMaximum + || mBacklightMinimum > mBacklightMaximum) { + throw new IllegalStateException("Min or max values are invalid" + + "; raw min=" + mRawBacklight[0] + + "; raw max=" + mRawBacklight[mRawBacklight.length - 1] + + "; backlight min=" + mBacklightMinimum + + "; backlight max=" + mBacklightMaximum); + } + + float[] newNits = new float[mRawBacklight.length]; + float[] newBacklight = new float[mRawBacklight.length]; + // Find the starting index of the clamped arrays. This may be less than the min so + // we'll need to clamp this value still when actually doing the remapping. + int newStart = 0; + for (int i = 0; i < mRawBacklight.length - 1; i++) { + if (mRawBacklight[i + 1] > mBacklightMinimum) { + newStart = i; + break; + } + } + + boolean isLastValue = false; + int newIndex = 0; + for (int i = newStart; i < mRawBacklight.length && !isLastValue; i++) { + newIndex = i - newStart; + final float newBacklightVal; + final float newNitsVal; + isLastValue = mRawBacklight[i] > mBacklightMaximum + || i >= mRawBacklight.length - 1; + // Clamp beginning and end to valid backlight values. + if (newIndex == 0) { + newBacklightVal = MathUtils.max(mRawBacklight[i], mBacklightMinimum); + newNitsVal = rawBacklightToNits(i, newBacklightVal); + } else if (isLastValue) { + newBacklightVal = MathUtils.min(mRawBacklight[i], mBacklightMaximum); + newNitsVal = rawBacklightToNits(i - 1, newBacklightVal); + } else { + newBacklightVal = mRawBacklight[i]; + newNitsVal = mRawNits[i]; + } + newBacklight[newIndex] = newBacklightVal; + newNits[newIndex] = newNitsVal; + } + mBacklight = Arrays.copyOf(newBacklight, newIndex + 1); + mNits = Arrays.copyOf(newNits, newIndex + 1); + createBacklightConversionSplines(); + } + + private float rawBacklightToNits(int i, float backlight) { + return MathUtils.map(mRawBacklight[i], mRawBacklight[i + 1], + mRawNits[i], mRawNits[i + 1], backlight); + } + + // This method creates a brightness spline that is of equal length with proportional increments + // to the backlight spline. The values of this array range from 0.0f to 1.0f instead of the + // potential constrained range that the backlight array covers + // These splines are used to convert from the system brightness value to the HAL backlight + // value + private void createBacklightConversionSplines() { + mBrightness = new float[mBacklight.length]; + for (int i = 0; i < mBrightness.length; i++) { + mBrightness[i] = MathUtils.map(mBacklight[0], + mBacklight[mBacklight.length - 1], + PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, mBacklight[i]); + } + mBrightnessToBacklightSpline = Spline.createSpline(mBrightness, mBacklight); + mBacklightToBrightnessSpline = Spline.createSpline(mBacklight, mBrightness); } private void loadQuirks(DisplayConfiguration config) { @@ -412,12 +587,14 @@ public class DisplayDeviceConfig { mIsHighBrightnessModeEnabled = hbm.getEnabled(); mHbmData = new HighBrightnessModeData(); mHbmData.minimumLux = hbm.getMinimumLux_all().floatValue(); - mHbmData.transitionPoint = hbm.getTransitionPoint_all().floatValue(); - if (mHbmData.transitionPoint >= getMaxBrightness()) { + float transitionPointBacklightScale = hbm.getTransitionPoint_all().floatValue(); + if (transitionPointBacklightScale >= mBacklightMaximum) { throw new IllegalArgumentException("HBM transition point invalid. " + mHbmData.transitionPoint + " is not less than " - + getMaxBrightness()); + + mBacklightMaximum); } + mHbmData.transitionPoint = + mBacklightToBrightnessSpline.interpolate(transitionPointBacklightScale); final HbmTiming hbmTiming = hbm.getTiming_all(); mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000; mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000; diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index 501533d535d3..40cee66a6791 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -466,7 +466,7 @@ final class DisplayDeviceInfo { sb.append(", supportedModes ").append(Arrays.toString(supportedModes)); sb.append(", colorMode ").append(colorMode); sb.append(", supportedColorModes ").append(Arrays.toString(supportedColorModes)); - sb.append(", HdrCapabilities ").append(hdrCapabilities); + sb.append(", hdrCapabilities ").append(hdrCapabilities); sb.append(", allmSupported ").append(allmSupported); sb.append(", gameContentTypeSupported ").append(gameContentTypeSupported); sb.append(", density ").append(densityDpi); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 6a229417316d..52149ee3a4dd 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -249,30 +249,48 @@ public final class DisplayManagerService extends SystemService { /** {@link DisplayBlanker} used by all {@link DisplayPowerController}s. */ private final DisplayBlanker mDisplayBlanker = new DisplayBlanker() { + // Synchronized to avoid race conditions when updating multiple display states. @Override - public void requestDisplayState(int displayId, int state, float brightness) { - // TODO (b/168210494): Stop applying default display state to all displays. - if (displayId != Display.DEFAULT_DISPLAY) { - return; - } - final int[] displayIds; + public synchronized void requestDisplayState(int displayId, int state, float brightness) { + boolean allInactive = true; + boolean allOff = true; + final boolean stateChanged; synchronized (mSyncRoot) { - displayIds = mLogicalDisplayMapper.getDisplayIdsLocked(); + final int index = mDisplayStates.indexOfKey(displayId); + if (index > -1) { + final int currentState = mDisplayStates.valueAt(index); + stateChanged = state != currentState; + if (stateChanged) { + final int size = mDisplayStates.size(); + for (int i = 0; i < size; i++) { + final int displayState = i == index ? state : mDisplayStates.valueAt(i); + if (displayState != Display.STATE_OFF) { + allOff = false; + } + if (Display.isActiveState(displayState)) { + allInactive = false; + } + if (!allOff && !allInactive) { + break; + } + } + } + } else { + stateChanged = false; + } } // The order of operations is important for legacy reasons. if (state == Display.STATE_OFF) { - for (int id : displayIds) { - requestDisplayStateInternal(id, state, brightness); - } + requestDisplayStateInternal(displayId, state, brightness); } - mDisplayPowerCallbacks.onDisplayStateChange(state); + if (stateChanged) { + mDisplayPowerCallbacks.onDisplayStateChange(allInactive, allOff); + } if (state != Display.STATE_OFF) { - for (int id : displayIds) { - requestDisplayStateInternal(id, state, brightness); - } + requestDisplayStateInternal(displayId, state, brightness); } } }; @@ -514,7 +532,7 @@ public final class DisplayManagerService extends SystemService { DeviceStateManager deviceStateManager = mContext.getSystemService(DeviceStateManager.class); - deviceStateManager.addDeviceStateListener(new HandlerExecutor(mHandler), + deviceStateManager.registerCallback(new HandlerExecutor(mHandler), new DeviceStateListener()); scheduleTraversalLocked(false); @@ -1160,7 +1178,7 @@ public final class DisplayManagerService extends SystemService { private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) { final int displayId = display.getDisplayIdLocked(); - mDisplayPowerControllers.delete(displayId); + mDisplayPowerControllers.removeReturnOld(displayId).stop(); mDisplayStates.delete(displayId); mDisplayBrightnesses.delete(displayId); DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); @@ -2758,8 +2776,22 @@ public final class DisplayManagerService extends SystemService { public boolean requestPowerState(int groupId, DisplayPowerRequest request, boolean waitForNegativeProximity) { synchronized (mSyncRoot) { - return mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY) - .requestPowerState(request, waitForNegativeProximity); + final DisplayGroup displayGroup = mLogicalDisplayMapper.getDisplayGroupLocked( + groupId); + if (displayGroup == null) { + return true; + } + + final int size = displayGroup.getSizeLocked(); + boolean ready = true; + for (int i = 0; i < size; i++) { + final DisplayPowerController displayPowerController = + mDisplayPowerControllers.get(displayGroup.getIdLocked(i)); + ready &= displayPowerController.requestPowerState(request, + waitForNegativeProximity); + } + + return ready; } } @@ -2772,13 +2804,6 @@ public final class DisplayManagerService extends SystemService { } @Override - public int getDisplayGroupId(int displayId) { - synchronized (mSyncRoot) { - return mLogicalDisplayMapper.getDisplayGroupIdLocked(displayId); - } - } - - @Override public void registerDisplayGroupListener(DisplayGroupListener listener) { mDisplayGroupListeners.add(listener); } @@ -2949,9 +2974,9 @@ public final class DisplayManagerService extends SystemService { /** * Listens to changes in device state and reports the state to LogicalDisplayMapper. */ - class DeviceStateListener implements DeviceStateManager.DeviceStateListener { + class DeviceStateListener implements DeviceStateManager.DeviceStateCallback { @Override - public void onDeviceStateChanged(int deviceState) { + public void onStateChanged(int deviceState) { synchronized (mSyncRoot) { mLogicalDisplayMapper.setDeviceStateLocked(deviceState); } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 9320f5027ce0..011732682ace 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -121,6 +121,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6; private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7; private static final int MSG_IGNORE_PROXIMITY = 8; + private static final int MSG_STOP = 9; private static final int PROXIMITY_UNKNOWN = -1; private static final int PROXIMITY_NEGATIVE = 0; @@ -189,12 +190,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The dim screen brightness. private final float mScreenBrightnessDimConfig; - // The minimum allowed brightness. - private final float mScreenBrightnessRangeMinimum; - - // The maximum allowed brightness. - private final float mScreenBrightnessRangeMaximum; - private final float mScreenBrightnessDefault; // The minimum allowed brightness while in VR. @@ -351,6 +346,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Nullable private final DisplayWhiteBalanceController mDisplayWhiteBalanceController; + @Nullable private final ColorDisplayServiceInternal mCdsi; private final float[] mNitsRange; @@ -409,6 +405,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private ObjectAnimator mColorFadeOffAnimator; private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator; + // True if this DisplayPowerController has been stopped and should no longer be running. + private boolean mStopped; + /** * Creates the display power controller. */ @@ -438,8 +437,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final Resources resources = context.getResources(); - final float screenBrightnessSettingMinimumFloat = clampAbsoluteBrightness( - pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM)); // DOZE AND DIM SETTINGS mScreenBrightnessDozeConfig = clampAbsoluteBrightness( @@ -448,10 +445,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM)); // NORMAL SCREEN SETTINGS - mScreenBrightnessRangeMinimum = - Math.min(screenBrightnessSettingMinimumFloat, mScreenBrightnessDimConfig); - mScreenBrightnessRangeMaximum = clampAbsoluteBrightness( - pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM)); mScreenBrightnessDefault = clampAbsoluteBrightness( mLogicalDisplay.getDisplayInfoLocked().brightnessDefault); @@ -476,18 +469,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call DisplayDeviceConfig displayDeviceConfig = logicalDisplay .getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig(); - // TODO: (b/178183143) Ensure that the ddc is not null - if (displayDeviceConfig != null) { - mBrightnessRampRateFastDecrease = displayDeviceConfig.getBrightnessRampFastDecrease(); - mBrightnessRampRateFastIncrease = displayDeviceConfig.getBrightnessRampFastIncrease(); - mBrightnessRampRateSlowDecrease = displayDeviceConfig.getBrightnessRampSlowDecrease(); - mBrightnessRampRateSlowIncrease = displayDeviceConfig.getBrightnessRampSlowIncrease(); - } else { - mBrightnessRampRateFastDecrease = 1.0f; - mBrightnessRampRateFastIncrease = 1.0f; - mBrightnessRampRateSlowDecrease = 1.0f; - mBrightnessRampRateSlowIncrease = 1.0f; - } + mBrightnessRampRateFastDecrease = displayDeviceConfig.getBrightnessRampFastDecrease(); + mBrightnessRampRateFastIncrease = displayDeviceConfig.getBrightnessRampFastIncrease(); + mBrightnessRampRateSlowDecrease = displayDeviceConfig.getBrightnessRampSlowDecrease(); + mBrightnessRampRateSlowIncrease = displayDeviceConfig.getBrightnessRampSlowIncrease(); mSkipScreenOnBrightnessRamp = resources.getBoolean( com.android.internal.R.bool.config_skipScreenOnBrightnessRamp); @@ -540,12 +525,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call com.android.internal.R.string.config_displayLightSensorType); Sensor lightSensor = findDisplayLightSensor(lightSensorType); - mBrightnessMapper = BrightnessMappingStrategy.create(resources); + final DisplayDeviceConfig ddc = + logicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig(); + mBrightnessMapper = BrightnessMappingStrategy.create(resources, ddc); if (mBrightnessMapper != null) { mAutomaticBrightnessController = new AutomaticBrightnessController(this, handler.getLooper(), sensorManager, lightSensor, mBrightnessMapper, - lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum, - mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate, + lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, screenBrightnessThresholds, logicalDisplay, context); @@ -583,14 +570,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null; DisplayWhiteBalanceController displayWhiteBalanceController = null; - try { - displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler); - displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler, - mSensorManager, resources); - displayWhiteBalanceSettings.setCallbacks(this); - displayWhiteBalanceController.setCallbacks(this); - } catch (Exception e) { - Slog.e(TAG, "failed to set up display white-balance: " + e); + if (mDisplayId == Display.DEFAULT_DISPLAY) { + try { + displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler); + displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler, + mSensorManager, resources); + displayWhiteBalanceSettings.setCallbacks(this); + displayWhiteBalanceController.setCallbacks(this); + } catch (Exception e) { + Slog.e(TAG, "failed to set up display white-balance: " + e); + } } mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings; mDisplayWhiteBalanceController = displayWhiteBalanceController; @@ -602,20 +591,24 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mNitsRange = BrightnessMappingStrategy.getFloatArray(context.getResources() .obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits)); } - mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class); - boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() { - @Override - public void onReduceBrightColorsActivationChanged(boolean activated) { - applyReduceBrightColorsSplineAdjustment(); - } + if (mDisplayId == Display.DEFAULT_DISPLAY) { + mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class); + boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() { + @Override + public void onReduceBrightColorsActivationChanged(boolean activated) { + applyReduceBrightColorsSplineAdjustment(); + } - @Override - public void onReduceBrightColorsStrengthChanged(int strength) { + @Override + public void onReduceBrightColorsStrengthChanged(int strength) { + applyReduceBrightColorsSplineAdjustment(); + } + }); + if (active) { applyReduceBrightColorsSplineAdjustment(); } - }); - if (active) { - applyReduceBrightColorsSplineAdjustment(); + } else { + mCdsi = null; } } @@ -713,6 +706,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } synchronized (mLock) { + if (mStopped) { + return true; + } + boolean changed = false; if (waitForNegativeProximity @@ -731,11 +728,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (changed) { mDisplayReadyLocked = false; - } - - if (changed && !mPendingRequestChangedLocked) { - mPendingRequestChangedLocked = true; - sendUpdatePowerStateLocked(); + if (!mPendingRequestChangedLocked) { + mPendingRequestChangedLocked = true; + sendUpdatePowerStateLocked(); + } } return mDisplayReadyLocked; @@ -758,6 +754,34 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // TODO: b/175821789 - Support high brightness on multiple (folding) displays } + /** + * Unregisters all listeners and interrupts all running threads; halting future work. + * + * This method should be called when the DisplayPowerController is no longer in use; i.e. when + * the {@link #mDisplayId display} has been removed. + */ + public void stop() { + synchronized (mLock) { + mStopped = true; + Message msg = mHandler.obtainMessage(MSG_STOP); + mHandler.sendMessage(msg); + + if (mDisplayWhiteBalanceController != null) { + mDisplayWhiteBalanceController.setEnabled(false); + } + + if (mAutomaticBrightnessController != null) { + mAutomaticBrightnessController.stop(); + } + + if (mBrightnessTracker != null) { + mBrightnessTracker.stop(); + } + + mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); + } + } + private void sendUpdatePowerState() { synchronized (mLock) { sendUpdatePowerStateLocked(); @@ -765,7 +789,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private void sendUpdatePowerStateLocked() { - if (!mPendingUpdatePowerStateLocked) { + if (!mStopped && !mPendingUpdatePowerStateLocked) { mPendingUpdatePowerStateLocked = true; Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE); mHandler.sendMessage(msg); @@ -788,7 +812,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mColorFadeOffAnimator.addListener(mAnimatorListener); } - mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>( + mScreenBrightnessRampAnimator = new RampAnimator<>( mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT); mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener); @@ -796,9 +820,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call noteScreenBrightness(mPowerState.getScreenBrightness()); // Initialize all of the brightness tracking state - final float brightness = convertToNits(BrightnessSynchronizer.brightnessFloatToInt( - mPowerState.getScreenBrightness())); - if (brightness >= 0.0f) { + final float brightness = convertToNits(mPowerState.getScreenBrightness()); + if (brightness >= PowerManager.BRIGHTNESS_MIN) { mBrightnessTracker.start(brightness); } @@ -829,12 +852,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } }; - private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() { - @Override - public void onAnimationEnd() { - sendUpdatePowerState(); + private final RampAnimator.Listener mRampAnimatorListener = this::sendUpdatePowerState; + + /** Clean up all resources that are accessed via the {@link #mHandler} thread. */ + private void cleanupHandlerThreadAfterStop() { + setProximitySensorEnabled(false); + mHandler.removeCallbacksAndMessages(null); + if (mPowerState != null) { + mPowerState.stop(); + mPowerState = null; } - }; + } private void updatePowerState() { // Update the power state request. @@ -844,6 +872,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call int brightnessAdjustmentFlags = 0; mBrightnessReasonTemp.set(null); synchronized (mLock) { + if (mStopped) { + return; + } mPendingUpdatePowerStateLocked = false; if (mPendingRequestLocked == null) { return; // wait until first actual power request @@ -1103,10 +1134,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Apply dimming by at least some minimum amount when user activity // timeout is about to expire. if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) { - if (brightnessState > mScreenBrightnessRangeMinimum) { + if (brightnessState > PowerManager.BRIGHTNESS_MIN) { brightnessState = Math.max(Math.min(brightnessState - SCREEN_DIM_MINIMUM_REDUCTION_FLOAT, - mScreenBrightnessDimConfig), mScreenBrightnessRangeMinimum); + mScreenBrightnessDimConfig), PowerManager.BRIGHTNESS_MIN); mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED); } if (!mAppliedDimming) { @@ -1120,12 +1151,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor // as long as it is above the minimum threshold. if (mPowerRequest.lowPowerMode) { - if (brightnessState > mScreenBrightnessRangeMinimum) { + if (brightnessState > PowerManager.BRIGHTNESS_MIN) { final float brightnessFactor = Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1); final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor); - brightnessState = Math.max(lowPowerBrightnessFloat, - mScreenBrightnessRangeMinimum); + brightnessState = Math.max(lowPowerBrightnessFloat, PowerManager.BRIGHTNESS_MIN); mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER); } if (!mAppliedLowPower) { @@ -1210,9 +1240,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // slider event so notify as if the system changed the brightness. userInitiatedChange = false; } - notifyBrightnessChanged( - BrightnessSynchronizer.brightnessFloatToInt(brightnessState), - userInitiatedChange, hadUserBrightnessPoint); + notifyBrightnessChanged(brightnessState, userInitiatedChange, + hadUserBrightnessPoint); } } @@ -1439,17 +1468,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private float clampScreenBrightness(float value) { if (Float.isNaN(value)) { - return mScreenBrightnessRangeMinimum; + return PowerManager.BRIGHTNESS_MIN; } return MathUtils.constrain( - value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum); + value, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); } // Checks whether the brightness is within the valid brightness range, not including the off or // invalid states. private boolean isValidBrightnessValue(float brightnessState) { - return brightnessState >= mScreenBrightnessRangeMinimum - && brightnessState <= mScreenBrightnessRangeMaximum; + return brightnessState >= PowerManager.BRIGHTNESS_MIN + && brightnessState <= PowerManager.BRIGHTNESS_MAX; } private void animateScreenBrightness(float target, float rate) { @@ -1826,7 +1855,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return true; } - private void notifyBrightnessChanged(int brightness, boolean userInitiated, + private void notifyBrightnessChanged(float brightness, boolean userInitiated, boolean hadUserDataPoint) { final float brightnessInNits = convertToNits(brightness); if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f @@ -1843,9 +1872,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - private float convertToNits(int backlight) { + private float convertToNits(float brightness) { if (mBrightnessMapper != null) { - return mBrightnessMapper.convertToNits(backlight); + return mBrightnessMapper.convertToNits(brightness); } else { return -1.0f; } @@ -1924,12 +1953,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(); pw.println("Display Power Controller Configuration:"); - pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum); - pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum); pw.println(" mScreenBrightnessRangeDefault=" + mScreenBrightnessDefault); pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig); pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig); - pw.println(" mScreenBrightnessDefault=" + mScreenBrightnessDefault); pw.println(" mScreenBrightnessForVrRangeMinimum=" + mScreenBrightnessForVrRangeMinimum); pw.println(" mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum); pw.println(" mScreenBrightnessForVrDefault=" + mScreenBrightnessForVrDefault); @@ -2061,10 +2087,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - private static int clampAbsoluteBrightness(int value) { - return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); - } - private static float clampAbsoluteBrightness(float value) { return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); @@ -2144,6 +2166,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call case MSG_IGNORE_PROXIMITY: ignoreProximitySensorUntilChangedInternal(); break; + + case MSG_STOP: + cleanupHandlerThreadAfterStop(); + break; } } } diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 1d20d878fb81..77aff5b03dd2 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -72,6 +72,8 @@ final class DisplayPowerState { private Runnable mCleanListener; + private volatile boolean mStopped; + DisplayPowerState( DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) { mHandler = new Handler(true /*async*/); @@ -264,9 +266,24 @@ final class DisplayPowerState { } } + /** + * Interrupts all running threads; halting future work. + * + * This method should be called when the DisplayPowerState is no longer in use; i.e. when + * the {@link #mDisplayId display} has been removed. + */ + public void stop() { + mStopped = true; + mPhotonicModulator.interrupt(); + dismissColorFade(); + mCleanListener = null; + mHandler.removeCallbacksAndMessages(null); + } + public void dump(PrintWriter pw) { pw.println(); pw.println("Display Power State:"); + pw.println(" mStopped=" + mStopped); pw.println(" mScreenState=" + Display.stateToString(mScreenState)); pw.println(" mScreenBrightness=" + mScreenBrightness); pw.println(" mScreenReady=" + mScreenReady); @@ -428,7 +445,11 @@ final class DisplayPowerState { if (!stateChanged && !backlightChanged) { try { mLock.wait(); - } catch (InterruptedException ex) { } + } catch (InterruptedException ex) { + if (mStopped) { + return; + } + } continue; } mActualState = state; diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 3b66236c9f0f..3709963b7caa 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -30,7 +30,6 @@ import android.os.Trace; import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; -import android.util.Spline; import android.view.Display; import android.view.DisplayAddress; import android.view.DisplayCutout; @@ -38,8 +37,8 @@ import android.view.DisplayEventReceiver; import android.view.RoundedCorners; import android.view.SurfaceControl; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; -import com.android.internal.os.BackgroundThread; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.lights.LightsManager; @@ -72,6 +71,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final Injector mInjector; + private final SurfaceControlProxy mSurfaceControlProxy; // Called with SyncRoot lock held. public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, @@ -79,10 +79,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { this(syncRoot, context, handler, listener, new Injector()); } + @VisibleForTesting LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, Injector injector) { super(syncRoot, context, handler, listener, TAG); mInjector = injector; + mSurfaceControlProxy = mInjector.getSurfaceControlProxy(); } @Override @@ -92,58 +94,57 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInjector.setDisplayEventListenerLocked(getHandler().getLooper(), new LocalDisplayEventListener()); - for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) { + for (long physicalDisplayId : mSurfaceControlProxy.getPhysicalDisplayIds()) { tryConnectDisplayLocked(physicalDisplayId); } } private void tryConnectDisplayLocked(long physicalDisplayId) { - final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(physicalDisplayId); + final IBinder displayToken = + mSurfaceControlProxy.getPhysicalDisplayToken(physicalDisplayId); if (displayToken != null) { - SurfaceControl.DisplayInfo info = SurfaceControl.getDisplayInfo(displayToken); - if (info == null) { - Slog.w(TAG, "No valid info found for display device " + physicalDisplayId); + SurfaceControl.StaticDisplayInfo staticInfo = + mSurfaceControlProxy.getStaticDisplayInfo(displayToken); + if (staticInfo == null) { + Slog.w(TAG, "No valid static info found for display device " + physicalDisplayId); return; } - SurfaceControl.DisplayMode[] displayModes = - SurfaceControl.getDisplayModes(displayToken); - if (displayModes == null) { + SurfaceControl.DynamicDisplayInfo dynamicInfo = + mSurfaceControlProxy.getDynamicDisplayInfo(displayToken); + if (dynamicInfo == null) { + Slog.w(TAG, "No valid dynamic info found for display device " + physicalDisplayId); + return; + } + if (dynamicInfo.supportedDisplayModes == null) { // There are no valid modes for this device, so we can't use it Slog.w(TAG, "No valid modes found for display device " + physicalDisplayId); return; } - int activeDisplayMode = SurfaceControl.getActiveDisplayMode(displayToken); - if (activeDisplayMode < 0) { + if (dynamicInfo.activeDisplayModeId < 0) { // There is no active mode, and for now we don't have the // policy to set one. - Slog.w(TAG, "No active mode found for display device " + physicalDisplayId); + Slog.w(TAG, "No valid active mode found for display device " + physicalDisplayId); return; } - int activeColorMode = SurfaceControl.getActiveColorMode(displayToken); - if (activeColorMode < 0) { + if (dynamicInfo.activeColorMode < 0) { // We failed to get the active color mode. We don't bail out here since on the next // configuration pass we'll go ahead and set it to whatever it was set to last (or // COLOR_MODE_NATIVE if this is the first configuration). - Slog.w(TAG, "Unable to get active color mode for display device " + - physicalDisplayId); - activeColorMode = Display.COLOR_MODE_INVALID; - } - SurfaceControl.DesiredDisplayModeSpecs modeSpecsSpecs = - SurfaceControl.getDesiredDisplayModeSpecs(displayToken); - int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken); - Display.HdrCapabilities hdrCapabilities = - SurfaceControl.getHdrCapabilities(displayToken); + Slog.w(TAG, "No valid active color mode for display device " + physicalDisplayId); + dynamicInfo.activeColorMode = Display.COLOR_MODE_INVALID; + } + SurfaceControl.DesiredDisplayModeSpecs modeSpecs = + mSurfaceControlProxy.getDesiredDisplayModeSpecs(displayToken); LocalDisplayDevice device = mDevices.get(physicalDisplayId); if (device == null) { // Display was added. final boolean isDefaultDisplay = mDevices.size() == 0; - device = new LocalDisplayDevice(displayToken, physicalDisplayId, info, displayModes, - activeDisplayMode, modeSpecsSpecs, colorModes, activeColorMode, - hdrCapabilities, isDefaultDisplay); + device = new LocalDisplayDevice(displayToken, physicalDisplayId, staticInfo, + dynamicInfo, modeSpecs, isDefaultDisplay); mDevices.put(physicalDisplayId, device); sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); - } else if (device.updateDisplayPropertiesLocked(info, displayModes, activeDisplayMode, - modeSpecsSpecs, colorModes, activeColorMode, hdrCapabilities)) { + } else if (device.updateDisplayPropertiesLocked(staticInfo, dynamicInfo, + modeSpecs)) { sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED); } } else { @@ -195,7 +196,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs = new DisplayModeDirector.DesiredDisplayModeSpecs(); private boolean mDisplayModeSpecsInvalid; - private int mActiveDisplayModeId; private int mActiveColorMode; private Display.HdrCapabilities mHdrCapabilities; private boolean mAllmSupported; @@ -204,33 +204,29 @@ final class LocalDisplayAdapter extends DisplayAdapter { private boolean mGameContentTypeRequested; private boolean mSidekickActive; private SidekickInternal mSidekickInternal; - private SurfaceControl.DisplayInfo mDisplayInfo; - private SurfaceControl.DisplayMode[] mDisplayModes; - private Spline mSystemBrightnessToNits; - private Spline mNitsToHalBrightness; + private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo; + // The supported display modes according in SurfaceFlinger + private SurfaceControl.DisplayMode[] mSfDisplayModes; + // The active display mode in SurfaceFlinger + private SurfaceControl.DisplayMode mActiveSfDisplayMode; private DisplayDeviceConfig mDisplayDeviceConfig; private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides = new DisplayEventReceiver.FrameRateOverride[0]; LocalDisplayDevice(IBinder displayToken, long physicalDisplayId, - SurfaceControl.DisplayInfo info, SurfaceControl.DisplayMode[] displayModes, - int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs, - int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities, - boolean isDefaultDisplay) { - super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId); + SurfaceControl.StaticDisplayInfo staticDisplayInfo, + SurfaceControl.DynamicDisplayInfo dynamicInfo, + SurfaceControl.DesiredDisplayModeSpecs modeSpecs, boolean isDefaultDisplay) { + super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId, + getContext()); mPhysicalDisplayId = physicalDisplayId; mIsDefaultDisplay = isDefaultDisplay; - updateDisplayPropertiesLocked(info, displayModes, activeDisplayModeId, modeSpecs, - colorModes, activeColorMode, hdrCapabilities); + updateDisplayPropertiesLocked(staticDisplayInfo, dynamicInfo, modeSpecs); mSidekickInternal = LocalServices.getService(SidekickInternal.class); - mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay); - mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken); - mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken); + mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay, + mSurfaceControlProxy); mDisplayDeviceConfig = null; - // Defer configuration file loading - BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage( - LocalDisplayDevice::loadDisplayConfiguration, this)); } @Override @@ -241,15 +237,17 @@ final class LocalDisplayAdapter extends DisplayAdapter { /** * Returns true if there is a change. **/ - public boolean updateDisplayPropertiesLocked(SurfaceControl.DisplayInfo info, - SurfaceControl.DisplayMode[] displayModes, - int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs, - int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities) { + public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo, + SurfaceControl.DynamicDisplayInfo dynamicInfo, + SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { boolean changed = updateDisplayModesLocked( - displayModes, activeDisplayModeId, modeSpecs); - changed |= updateDisplayInfo(info); - changed |= updateColorModesLocked(colorModes, activeColorMode); - changed |= updateHdrCapabilitiesLocked(hdrCapabilities); + dynamicInfo.supportedDisplayModes, dynamicInfo.activeDisplayModeId, modeSpecs); + changed |= updateStaticInfo(staticInfo); + changed |= updateColorModesLocked(dynamicInfo.supportedColorModes, + dynamicInfo.activeColorMode); + changed |= updateHdrCapabilitiesLocked(dynamicInfo.hdrCapabilities); + changed |= updateAllmSupport(dynamicInfo.autoLowLatencyModeSupported); + changed |= updateGameContentTypeSupport(dynamicInfo.gameContentTypeSupported); if (changed) { mHavePendingChanges = true; @@ -260,8 +258,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { public boolean updateDisplayModesLocked( SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { - mDisplayModes = Arrays.copyOf(displayModes, displayModes.length); - mActiveDisplayModeId = activeDisplayModeId; + mSfDisplayModes = Arrays.copyOf(displayModes, displayModes.length); + mActiveSfDisplayMode = getModeById(displayModes, activeDisplayModeId); + // Build an updated list of all existing modes. ArrayList<DisplayModeRecord> records = new ArrayList<>(); boolean modesAdded = false; @@ -282,7 +281,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { // First, check to see if we've already added a matching mode. Since not all // configuration options are exposed via Display.Mode, it's possible that we have - // multiple DisplayModess that would generate the same Display.Mode. + // multiple DisplayModes that would generate the same Display.Mode. boolean existingMode = false; for (DisplayModeRecord record : records) { if (record.hasMatchingMode(mode) @@ -312,9 +311,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { // Get the currently active mode DisplayModeRecord activeRecord = null; - for (int i = 0; i < records.size(); i++) { - DisplayModeRecord record = records.get(i); - if (record.hasMatchingMode(displayModes[activeDisplayModeId])) { + for (DisplayModeRecord record : records) { + if (record.hasMatchingMode(mActiveSfDisplayMode)) { activeRecord = record; break; } @@ -340,19 +338,18 @@ final class LocalDisplayAdapter extends DisplayAdapter { // If we can't map the defaultMode index to a mode, then the physical display // modes must have changed, and the code below for handling changes to the // list of available modes will take care of updating display mode specs. - if (activeBaseMode != NO_DISPLAY_MODE_ID) { - if (mDisplayModeSpecs.baseModeId != activeBaseMode - || mDisplayModeSpecs.primaryRefreshRateRange.min - != modeSpecs.primaryRefreshRateMin - || mDisplayModeSpecs.primaryRefreshRateRange.max - != modeSpecs.primaryRefreshRateMax - || mDisplayModeSpecs.appRequestRefreshRateRange.min - != modeSpecs.appRequestRefreshRateMin - || mDisplayModeSpecs.appRequestRefreshRateRange.max - != modeSpecs.appRequestRefreshRateMax) { - mDisplayModeSpecsInvalid = true; - sendTraversalRequestLocked(); - } + if (activeBaseMode == NO_DISPLAY_MODE_ID + || mDisplayModeSpecs.baseModeId != activeBaseMode + || mDisplayModeSpecs.primaryRefreshRateRange.min + != modeSpecs.primaryRefreshRateMin + || mDisplayModeSpecs.primaryRefreshRateRange.max + != modeSpecs.primaryRefreshRateMax + || mDisplayModeSpecs.appRequestRefreshRateRange.min + != modeSpecs.appRequestRefreshRateMin + || mDisplayModeSpecs.appRequestRefreshRateRange.max + != modeSpecs.appRequestRefreshRateMax) { + mDisplayModeSpecsInvalid = true; + sendTraversalRequestLocked(); } } @@ -370,17 +367,17 @@ final class LocalDisplayAdapter extends DisplayAdapter { // For a new display, we need to initialize the default mode ID. if (mDefaultModeId == NO_DISPLAY_MODE_ID) { mDefaultModeId = activeRecord.mMode.getModeId(); - mDefaultModeGroup = displayModes[activeDisplayModeId].group; + mDefaultModeGroup = mActiveSfDisplayMode.group; } else if (modesAdded && activeModeChanged) { Slog.d(TAG, "New display modes are added and the active mode has changed, " + "use active mode as default mode."); mDefaultModeId = activeRecord.mMode.getModeId(); - mDefaultModeGroup = displayModes[activeDisplayModeId].group; + mDefaultModeGroup = mActiveSfDisplayMode.group; } else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) { Slog.w(TAG, "Default display mode no longer available, using currently" + " active mode as default."); mDefaultModeId = activeRecord.mMode.getModeId(); - mDefaultModeGroup = displayModes[activeDisplayModeId].group; + mDefaultModeGroup = mActiveSfDisplayMode.group; } // Determine whether the display mode specs' base mode is still there. @@ -410,14 +407,14 @@ final class LocalDisplayAdapter extends DisplayAdapter { @Override public DisplayDeviceConfig getDisplayDeviceConfig() { + if (mDisplayDeviceConfig == null) { + loadDisplayDeviceConfig(); + } return mDisplayDeviceConfig; } - private void loadDisplayConfiguration() { - Spline nitsToHal = null; - Spline sysToNits = null; - - // Load the mapping from nits to HAL brightness range (display-device-config.xml) + private void loadDisplayDeviceConfig() { + // Load display device config final Context context = getOverlayContext(); mDisplayDeviceConfig = DisplayDeviceConfig.create(context, mPhysicalDisplayId, mIsDefaultDisplay); @@ -425,40 +422,16 @@ final class LocalDisplayAdapter extends DisplayAdapter { return; } + // Load brightness HWC quirk mBacklightAdapter.setForceSurfaceControl(mDisplayDeviceConfig.hasQuirk( DisplayDeviceConfig.QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC)); - - final float[] halNits = mDisplayDeviceConfig.getNits(); - final float[] halBrightness = mDisplayDeviceConfig.getBrightness(); - if (halNits == null || halBrightness == null) { - return; - } - nitsToHal = Spline.createSpline(halNits, halBrightness); - - // Load the mapping from system brightness range to nits (config.xml) - final Resources res = context.getResources(); - final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray( - com.android.internal.R.array.config_screenBrightnessNits)); - final int[] sysBrightness = res.getIntArray( - com.android.internal.R.array.config_screenBrightnessBacklight); - if (sysNits.length == 0 || sysBrightness.length != sysNits.length) { - return; - } - final float[] sysBrightnessFloat = new float[sysBrightness.length]; - for (int i = 0; i < sysBrightness.length; i++) { - sysBrightnessFloat[i] = sysBrightness[i]; - } - sysToNits = Spline.createSpline(sysBrightnessFloat, sysNits); - - mNitsToHalBrightness = nitsToHal; - mSystemBrightnessToNits = sysToNits; } - private boolean updateDisplayInfo(SurfaceControl.DisplayInfo info) { - if (Objects.equals(mDisplayInfo, info)) { + private boolean updateStaticInfo(SurfaceControl.StaticDisplayInfo info) { + if (Objects.equals(mStaticDisplayInfo, info)) { return false; } - mDisplayInfo = info; + mStaticDisplayInfo = info; return true; } @@ -520,6 +493,33 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + private boolean updateAllmSupport(boolean supported) { + if (mAllmSupported == supported) { + return false; + } + mAllmSupported = supported; + return true; + } + + private boolean updateGameContentTypeSupport(boolean supported) { + if (mGameContentTypeSupported == supported) { + return false; + } + mGameContentTypeSupported = supported; + return true; + } + + private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes, + int modeId) { + for (SurfaceControl.DisplayMode mode : supportedModes) { + if (mode.id == modeId) { + return mode; + } + } + Slog.e(TAG, "Can't find display mode with id " + modeId); + return null; + } + private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayMode mode, List<Float> alternativeRefreshRates) { for (int i = 0; i < mSupportedModes.size(); i++) { @@ -556,10 +556,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { @Override public DisplayDeviceInfo getDisplayDeviceInfoLocked() { if (mInfo == null) { - SurfaceControl.DisplayMode mode = mDisplayModes[mActiveDisplayModeId]; mInfo = new DisplayDeviceInfo(); - mInfo.width = mode.width; - mInfo.height = mode.height; + mInfo.width = mActiveSfDisplayMode.width; + mInfo.height = mActiveSfDisplayMode.height; mInfo.modeId = mActiveModeId; mInfo.defaultModeId = mDefaultModeId; mInfo.supportedModes = getDisplayModes(mSupportedModes); @@ -572,21 +571,21 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.supportedColorModes[i] = mSupportedColorModes.get(i); } mInfo.hdrCapabilities = mHdrCapabilities; - mInfo.appVsyncOffsetNanos = mode.appVsyncOffsetNanos; - mInfo.presentationDeadlineNanos = mode.presentationDeadlineNanos; + mInfo.appVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos; + mInfo.presentationDeadlineNanos = mActiveSfDisplayMode.presentationDeadlineNanos; mInfo.state = mState; mInfo.uniqueId = getUniqueId(); final DisplayAddress.Physical physicalAddress = DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId); mInfo.address = physicalAddress; - mInfo.densityDpi = (int) (mDisplayInfo.density * 160 + 0.5f); - mInfo.xDpi = mode.xDpi; - mInfo.yDpi = mode.yDpi; - mInfo.deviceProductInfo = mDisplayInfo.deviceProductInfo; + mInfo.densityDpi = (int) (mStaticDisplayInfo.density * 160 + 0.5f); + mInfo.xDpi = mActiveSfDisplayMode.xDpi; + mInfo.yDpi = mActiveSfDisplayMode.yDpi; + mInfo.deviceProductInfo = mStaticDisplayInfo.deviceProductInfo; // Assume that all built-in displays that have secure output (eg. HDCP) also // support compositing from gralloc protected buffers. - if (mDisplayInfo.secure) { + if (mStaticDisplayInfo.secure) { mInfo.flags = DisplayDeviceInfo.FLAG_SECURE | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS; } @@ -620,7 +619,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - if (mDisplayInfo.isInternal) { + if (mStaticDisplayInfo.isInternal) { mInfo.type = Display.TYPE_INTERNAL; mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL; mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; @@ -637,15 +636,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { // The display is trusted since it is created by system. mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED; - if (mDisplayDeviceConfig != null) { - mInfo.brightnessMinimum = mDisplayDeviceConfig.getBrightnessMinimum(); - mInfo.brightnessMaximum = mDisplayDeviceConfig.getBrightnessMaximum(); - mInfo.brightnessDefault = mDisplayDeviceConfig.getBrightnessDefault(); - } else { - mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN; - mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX; - mInfo.brightnessDefault = 0.5f; - } + mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN; + mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX; + mInfo.brightnessDefault = getDisplayDeviceConfig().getBrightnessDefault(); } return mInfo; } @@ -690,7 +683,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { setDisplayState(Display.STATE_ON); currentState = Display.STATE_ON; } else { - return; // old state and new state is off + if (oldState == Display.STATE_UNKNOWN) { + // There's no guarantee about what the initial state is + // at startup, so we have to set it if previous was UNKNOWN. + setDisplayState(state); + } + return; } } @@ -749,7 +747,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { + "id=" + physicalDisplayId + ", state=" + Display.stateToString(state) + ")"); try { - SurfaceControl.setDisplayPowerMode(token, mode); + mSurfaceControlProxy.setDisplayPowerMode(token, mode); Trace.traceCounter(Trace.TRACE_TAG_POWER, "DisplayPowerMode", mode); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); @@ -778,8 +776,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness(" + "id=" + physicalDisplayId + ", brightness=" + brightness + ")"); try { - brightness = displayBrightnessToHalBrightness(brightness); - mBacklightAdapter.setBrightness(brightness); + float backlight = brightnessToBacklight(brightness); + mBacklightAdapter.setBacklight(backlight); Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenBrightness", BrightnessSynchronizer.brightnessFloatToInt(brightness)); @@ -788,35 +786,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - /** - * Converts brightness range from the framework's brightness space to the - * Hal brightness space if the HAL brightness space has been provided via - * a display device configuration file. - */ - private float displayBrightnessToHalBrightness(float brightness) { - // TODO: b/171380847 - This needs to be deprecated. The nits-to-brightness - // relationship should be specified in display-config OR config.xml, but not - // both, and no nits-space conversion should be necessary. - // - // Only do a conversion if there exists a unique system brightness and a - // unique HAL brightness-to-nits range defined. - if (mSystemBrightnessToNits == null || mNitsToHalBrightness == null) { - return brightness; - } - - // Sys brightness in this conversion is always specified in the old 1-255 - // range, so convert that here before the translation. - final float brightnessInt = - BrightnessSynchronizer.brightnessFloatToIntRange(brightness); - - if (BrightnessSynchronizer.floatEquals( - brightnessInt, PowerManager.BRIGHTNESS_OFF)) { - return PowerManager.BRIGHTNESS_OFF_FLOAT; - } - - final float nits = mSystemBrightnessToNits.interpolate(brightnessInt); - final float halBrightness = mNitsToHalBrightness.interpolate(nits); - return halBrightness; + private float brightnessToBacklight(float brightness) { + return getDisplayDeviceConfig().getBacklightFromBrightness(brightness); } }; } @@ -877,10 +848,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { // Do not lock when calling these SurfaceControl methods because they are sync // operations that may block for a while when setting display power mode. - SurfaceControl.setDesiredDisplayModeSpecs(displayToken, modeSpecs); - final int activeMode = SurfaceControl.getActiveDisplayMode(displayToken); + mSurfaceControlProxy.setDesiredDisplayModeSpecs(displayToken, modeSpecs); + + final int sfActiveModeId = mSurfaceControlProxy + .getDynamicDisplayInfo(displayToken).activeDisplayModeId; synchronized (getSyncRoot()) { - if (updateActiveModeLocked(activeMode)) { + if (updateActiveModeLocked(sfActiveModeId)) { updateDeviceInfoLocked(); } } @@ -891,8 +864,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { updateDeviceInfoLocked(); } - public void onActiveDisplayModeChangedLocked(int modeId) { - if (updateActiveModeLocked(modeId)) { + public void onActiveDisplayModeChangedLocked(int sfModeId) { + if (updateActiveModeLocked(sfModeId)) { updateDeviceInfoLocked(); } } @@ -904,15 +877,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - public boolean updateActiveModeLocked(int activeModeId) { - if (mActiveDisplayModeId == activeModeId) { + public boolean updateActiveModeLocked(int activeSfModeId) { + if (mActiveSfDisplayMode.id == activeSfModeId) { return false; } - mActiveDisplayModeId = activeModeId; - mActiveModeId = findMatchingModeIdLocked(activeModeId); + mActiveSfDisplayMode = getModeById(mSfDisplayModes, activeSfModeId); + mActiveModeId = findMatchingModeIdLocked(activeSfModeId); if (mActiveModeId == NO_DISPLAY_MODE_ID) { Slog.w(TAG, "In unknown mode after setting allowed modes" - + ", activeModeId=" + mActiveDisplayModeId); + + ", activeModeId=" + activeSfModeId); } return true; } @@ -946,7 +919,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private void requestColorModeAsync(IBinder displayToken, int colorMode) { // Do not lock when calling this SurfaceControl method because it is a sync operation // that may block for a while when setting display power mode. - SurfaceControl.setActiveColorMode(displayToken, colorMode); + mSurfaceControlProxy.setActiveColorMode(displayToken, colorMode); synchronized (getSyncRoot()) { updateDeviceInfoLocked(); } @@ -966,7 +939,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { return; } - SurfaceControl.setAutoLowLatencyMode(getDisplayTokenLocked(), on); + mSurfaceControlProxy.setAutoLowLatencyMode(getDisplayTokenLocked(), on); } @Override @@ -983,7 +956,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { return; } - SurfaceControl.setGameContentType(getDisplayTokenLocked(), on); + mSurfaceControlProxy.setGameContentType(getDisplayTokenLocked(), on); } @Override @@ -992,7 +965,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId); pw.println("mDisplayModeSpecs={" + mDisplayModeSpecs + "}"); pw.println("mDisplayModeSpecsInvalid=" + mDisplayModeSpecsInvalid); - pw.println("mActiveDisplayModeId=" + mActiveDisplayModeId); pw.println("mActiveModeId=" + mActiveModeId); pw.println("mActiveColorMode=" + mActiveColorMode); pw.println("mDefaultModeId=" + mDefaultModeId); @@ -1003,11 +975,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mAllmRequested=" + mAllmRequested); pw.println("mGameContentTypeSupported=" + mGameContentTypeSupported); pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested); - pw.println("mDisplayInfo=" + mDisplayInfo); - pw.println("mDisplayModes="); - for (int i = 0; i < mDisplayModes.length; i++) { - pw.println(" " + mDisplayModes[i]); + pw.println("mStaticDisplayInfo=" + mStaticDisplayInfo); + pw.println("mSfDisplayModes="); + for (int i = 0; i < mSfDisplayModes.length; i++) { + pw.println(" " + mSfDisplayModes[i]); } + pw.println("mActiveSfDisplayMode=" + mActiveSfDisplayMode); pw.println("mSupportedModes="); for (int i = 0; i < mSupportedModes.size(); i++) { pw.println(" " + mSupportedModes.valueAt(i)); @@ -1020,17 +993,16 @@ final class LocalDisplayAdapter extends DisplayAdapter { int matchingModeId = SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID; DisplayModeRecord record = mSupportedModes.get(modeId); if (record != null) { - for (int i = 0; i < mDisplayModes.length; i++) { - SurfaceControl.DisplayMode mode = mDisplayModes[i]; + for (SurfaceControl.DisplayMode mode : mSfDisplayModes) { if (record.hasMatchingMode(mode)) { if (matchingModeId == SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID) { - matchingModeId = i; + matchingModeId = mode.id; } // Prefer to return a mode that matches the modeGroup if (mode.group == modeGroup) { - return i; + return mode.id; } } } @@ -1038,12 +1010,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { return matchingModeId; } - private int findMatchingModeIdLocked(int modeId) { - if (modeId < 0 || modeId >= mDisplayModes.length) { - Slog.e(TAG, "Invalid display config index " + modeId); + private int findMatchingModeIdLocked(int sfModeId) { + SurfaceControl.DisplayMode mode = getModeById(mSfDisplayModes, sfModeId); + if (mode == null) { + Slog.e(TAG, "Invalid display mode ID " + sfModeId); return NO_DISPLAY_MODE_ID; } - SurfaceControl.DisplayMode mode = mDisplayModes[modeId]; for (int i = 0; i < mSupportedModes.size(); i++) { DisplayModeRecord record = mSupportedModes.valueAt(i); if (record.hasMatchingMode(mode)) { @@ -1128,6 +1100,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { public void setDisplayEventListenerLocked(Looper looper, DisplayEventListener listener) { mReceiver = new ProxyDisplayEventReceiver(looper, listener); } + public SurfaceControlProxy getSurfaceControlProxy() { + return new SurfaceControlProxy(); + } } public interface DisplayEventListener { @@ -1219,24 +1194,79 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } + @VisibleForTesting + static class SurfaceControlProxy { + public SurfaceControl.DynamicDisplayInfo getDynamicDisplayInfo(IBinder token) { + return SurfaceControl.getDynamicDisplayInfo(token); + } + + public long[] getPhysicalDisplayIds() { + return SurfaceControl.getPhysicalDisplayIds(); + } + + public IBinder getPhysicalDisplayToken(long physicalDisplayId) { + return SurfaceControl.getPhysicalDisplayToken(physicalDisplayId); + } + + public SurfaceControl.StaticDisplayInfo getStaticDisplayInfo(IBinder displayToken) { + return SurfaceControl.getStaticDisplayInfo(displayToken); + } + + public SurfaceControl.DesiredDisplayModeSpecs getDesiredDisplayModeSpecs( + IBinder displayToken) { + return SurfaceControl.getDesiredDisplayModeSpecs(displayToken); + } + + public boolean setDesiredDisplayModeSpecs(IBinder token, + SurfaceControl.DesiredDisplayModeSpecs specs) { + return SurfaceControl.setDesiredDisplayModeSpecs(token, specs); + } + + public void setDisplayPowerMode(IBinder displayToken, int mode) { + SurfaceControl.setDisplayPowerMode(displayToken, mode); + } + + public boolean setActiveColorMode(IBinder displayToken, int colorMode) { + return SurfaceControl.setActiveColorMode(displayToken, colorMode); + } + + public void setAutoLowLatencyMode(IBinder displayToken, boolean on) { + SurfaceControl.setAutoLowLatencyMode(displayToken, on); + + } + + public void setGameContentType(IBinder displayToken, boolean on) { + SurfaceControl.setGameContentType(displayToken, on); + } + + public boolean getDisplayBrightnessSupport(IBinder displayToken) { + return SurfaceControl.getDisplayBrightnessSupport(displayToken); + } + + public boolean setDisplayBrightness(IBinder displayToken, float brightness) { + return SurfaceControl.setDisplayBrightness(displayToken, brightness); + } + } + static class BacklightAdapter { private final IBinder mDisplayToken; private final LogicalLight mBacklight; private final boolean mUseSurfaceControlBrightness; + private final SurfaceControlProxy mSurfaceControlProxy; private boolean mForceSurfaceControl = false; /** * @param displayToken Token for display associated with this backlight. * @param isDefaultDisplay {@code true} if it is the default display. - * @param forceSurfaceControl {@code true} if brightness should always be - * set via SurfaceControl API. */ - BacklightAdapter(IBinder displayToken, boolean isDefaultDisplay) { + BacklightAdapter(IBinder displayToken, boolean isDefaultDisplay, + SurfaceControlProxy surfaceControlProxy) { mDisplayToken = displayToken; + mSurfaceControlProxy = surfaceControlProxy; - mUseSurfaceControlBrightness = - SurfaceControl.getDisplayBrightnessSupport(mDisplayToken); + mUseSurfaceControlBrightness = mSurfaceControlProxy + .getDisplayBrightnessSupport(mDisplayToken); if (!mUseSurfaceControlBrightness && isDefaultDisplay) { LightsManager lights = LocalServices.getService(LightsManager.class); @@ -1246,11 +1276,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - void setBrightness(float brightness) { + // Set backlight within min and max backlight values + void setBacklight(float backlight) { if (mUseSurfaceControlBrightness || mForceSurfaceControl) { - SurfaceControl.setDisplayBrightness(mDisplayToken, brightness); + mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, backlight); } else if (mBacklight != null) { - mBacklight.setBrightness(brightness); + mBacklight.setBrightness(backlight); } } diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index a054db533e3f..a3ff534e336e 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -17,7 +17,6 @@ package com.android.server.display; import android.content.Context; -import android.os.Process; import android.os.SystemProperties; import android.text.TextUtils; import android.util.IndentingPrintWriter; @@ -143,10 +142,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { return null; } - public int[] getDisplayIdsLocked() { - return getDisplayIdsLocked(Process.SYSTEM_UID); - } - public int[] getDisplayIdsLocked(int callingUid) { final int count = mLogicalDisplays.size(); int[] displayIds = new int[count]; @@ -171,15 +166,6 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } } - public int getDisplayGroupIdLocked(int displayId) { - final DisplayGroup displayGroup = mDisplayIdToGroupMap.get(displayId); - if (displayGroup != null) { - return displayGroup.getGroupId(); - } - - return -1; - } - public DisplayGroup getDisplayGroupLocked(int groupId) { final int size = mDisplayIdToGroupMap.size(); for (int i = 0; i < size; i++) { diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index 69943e3904ed..330379cf58eb 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -281,7 +281,8 @@ final class OverlayDisplayAdapter extends DisplayAdapter { List<OverlayMode> modes, int activeMode, int defaultMode, float refreshRate, long presentationDeadlineNanos, OverlayFlags flags, int state, SurfaceTexture surfaceTexture, int number) { - super(OverlayDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + number); + super(OverlayDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + number, + getContext()); mName = name; mRefreshRate = refreshRate; mDisplayPresentationDeadlineNanos = presentationDeadlineNanos; diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index ff4717b7131b..52a810bd8caa 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -236,7 +236,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { int ownerUid, String ownerPackageName, Surface surface, int flags, Callback callback, String uniqueId, int uniqueIndex, VirtualDisplayConfig virtualDisplayConfig) { - super(VirtualDisplayAdapter.this, displayToken, uniqueId); + super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext()); mAppToken = appToken; mOwnerUid = ownerUid; mOwnerPackageName = ownerPackageName; diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java index 57323170b327..d2baaf2228a1 100644 --- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java @@ -598,7 +598,8 @@ final class WifiDisplayAdapter extends DisplayAdapter { public WifiDisplayDevice(IBinder displayToken, String name, int width, int height, float refreshRate, int flags, String address, Surface surface) { - super(WifiDisplayAdapter.this, displayToken, DISPLAY_NAME_PREFIX + address); + super(WifiDisplayAdapter.this, displayToken, DISPLAY_NAME_PREFIX + address, + getContext()); mName = name; mWidth = width; mHeight = height; diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java index 09c0937802a5..01e839dae07a 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -41,7 +41,6 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.security.FileIntegrityService; import com.android.server.security.VerityUtils; import java.io.File; @@ -226,7 +225,7 @@ public final class FontManagerService extends IFontManager.Stub { @Nullable private static UpdatableFontDir createUpdatableFontDir() { // If apk verity is supported, fs-verity should be available. - if (!FileIntegrityService.isApkVeritySupported()) return null; + if (!VerityUtils.isFsVeritySupported()) return null; return new UpdatableFontDir(new File(FONT_FILES_DIR), Arrays.asList(new File(SystemFonts.SYSTEM_FONT_DIR), new File(SystemFonts.OEM_FONT_DIR)), diff --git a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java index d514aab31e8d..62337c7df03b 100644 --- a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java +++ b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java @@ -46,7 +46,7 @@ import java.util.Set; private static final String ATTR_VALUE = "value"; /* package */ static class Config { - public long lastModifiedDate; + public long lastModifiedMillis; public final Set<String> updatedFontDirs = new ArraySet<>(); public final List<FontConfig.FontFamily> fontFamilies = new ArrayList<>(); } @@ -73,7 +73,7 @@ import java.util.Set; } else if (depth == 2) { switch (tag) { case TAG_LAST_MODIFIED_DATE: - out.lastModifiedDate = parseLongAttribute(parser, ATTR_VALUE, 0); + out.lastModifiedMillis = parseLongAttribute(parser, ATTR_VALUE, 0); break; case TAG_UPDATED_FONT_DIR: out.updatedFontDirs.add(getAttribute(parser, ATTR_VALUE)); @@ -101,7 +101,7 @@ import java.util.Set; out.startTag(null, TAG_ROOT); out.startTag(null, TAG_LAST_MODIFIED_DATE); - out.attribute(null, ATTR_VALUE, Long.toString(config.lastModifiedDate)); + out.attribute(null, ATTR_VALUE, Long.toString(config.lastModifiedMillis)); out.endTag(null, TAG_LAST_MODIFIED_DATE); for (String dir : config.updatedFontDirs) { out.startTag(null, TAG_UPDATED_FONT_DIR); diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java index 08ddc6ddf4ae..86dbe86f85ee 100644 --- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java +++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java @@ -112,7 +112,7 @@ final class UpdatableFontDir { private final File mConfigFile; private final File mTmpConfigFile; - private long mLastModifiedDate; + private long mLastModifiedMillis; private int mConfigVersion = 1; /** @@ -147,7 +147,7 @@ final class UpdatableFontDir { /* package */ void loadFontFileMap() { mFontFileInfoMap.clear(); mFontFamilyMap.clear(); - mLastModifiedDate = 0; + mLastModifiedMillis = 0; boolean success = false; try { PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); @@ -157,7 +157,7 @@ final class UpdatableFontDir { Slog.e(TAG, "Failed to load config xml file", e); return; } - mLastModifiedDate = config.lastModifiedDate; + mLastModifiedMillis = config.lastModifiedMillis; File[] dirs = mFilesDir.listFiles(); if (dirs == null) return; @@ -200,7 +200,7 @@ final class UpdatableFontDir { if (!success) { mFontFileInfoMap.clear(); mFontFamilyMap.clear(); - mLastModifiedDate = 0; + mLastModifiedMillis = 0; FileUtils.deleteContents(mFilesDir); } } @@ -211,7 +211,7 @@ final class UpdatableFontDir { FileUtils.deleteContents(mFilesDir); mFontFamilyMap.clear(); - mLastModifiedDate = Instant.now().getEpochSecond(); + mLastModifiedMillis = System.currentTimeMillis(); try (FileOutputStream fos = new FileOutputStream(mConfigFile)) { PersistentSystemFontConfig.writeToXml(fos, createPersistentConfig()); } catch (Exception e) { @@ -231,7 +231,7 @@ final class UpdatableFontDir { // Backup the mapping for rollback. ArrayMap<String, FontFileInfo> backupMap = new ArrayMap<>(mFontFileInfoMap); ArrayMap<String, FontConfig.FontFamily> backupFamilies = new ArrayMap<>(mFontFamilyMap); - long backupLastModifiedDate = mLastModifiedDate; + long backupLastModifiedDate = mLastModifiedMillis; boolean success = false; try { for (FontUpdateRequest request : requests) { @@ -247,7 +247,7 @@ final class UpdatableFontDir { } // Write config file. - mLastModifiedDate = Instant.now().getEpochSecond(); + mLastModifiedMillis = Instant.now().getEpochSecond(); try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) { PersistentSystemFontConfig.writeToXml(fos, createPersistentConfig()); } catch (Exception e) { @@ -269,7 +269,7 @@ final class UpdatableFontDir { mFontFileInfoMap.putAll(backupMap); mFontFamilyMap.clear(); mFontFamilyMap.putAll(backupFamilies); - mLastModifiedDate = backupLastModifiedDate; + mLastModifiedMillis = backupLastModifiedDate; } } } @@ -527,7 +527,7 @@ final class UpdatableFontDir { private PersistentSystemFontConfig.Config createPersistentConfig() { PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); - config.lastModifiedDate = mLastModifiedDate; + config.lastModifiedMillis = mLastModifiedMillis; for (FontFileInfo info : mFontFileInfoMap.values()) { config.updatedFontDirs.add(info.getRandomizedFontDir().getName()); } @@ -560,7 +560,7 @@ final class UpdatableFontDir { // which will be used as a fallback font without being overridden. mergedFamilies.addAll(mFontFamilyMap.values()); return new FontConfig( - mergedFamilies, config.getAliases(), mLastModifiedDate, mConfigVersion); + mergedFamilies, config.getAliases(), mLastModifiedMillis, mConfigVersion); } /* package */ int getConfigVersion() { diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java index 86a8e36d748d..983b6b5bceb4 100644 --- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java @@ -100,9 +100,9 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { @Override public boolean start() { - if (mIsCec20) { - sendSetStreamPath(); - } + // Wake-up on <Set Stream Path> was not mandatory before CEC 2.0. + // The message is re-sent at the end of the action for devices that don't support 2.0. + sendSetStreamPath(); int targetPowerStatus = localDevice().mService.getHdmiCecNetwork() .getCecDeviceInfo(getTargetAddress()).getDevicePowerStatus(); if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java index f64efe7c26cc..624af30854dc 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java @@ -39,6 +39,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ConcurrentUtils; import com.android.server.hdmi.cec.config.CecSettings; import com.android.server.hdmi.cec.config.Setting; import com.android.server.hdmi.cec.config.Value; @@ -55,7 +56,9 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.Executor; import javax.xml.datatype.DatatypeConfigurationException; @@ -99,7 +102,7 @@ public class HdmiCecConfig { private final Object mLock = new Object(); @GuardedBy("mLock") - private final ArrayMap<Setting, Set<SettingChangeListener>> + private final ArrayMap<Setting, ArrayMap<SettingChangeListener, Executor>> mSettingChangeListeners = new ArrayMap<>(); private SettingsObserver mSettingsObserver; @@ -292,7 +295,7 @@ public class HdmiCecConfig { case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED: return STORAGE_GLOBAL_SETTINGS; case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION: - return STORAGE_GLOBAL_SETTINGS; + return STORAGE_SHARED_PREFS; case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE: return STORAGE_GLOBAL_SETTINGS; case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE: @@ -329,7 +332,7 @@ public class HdmiCecConfig { case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED: return Global.HDMI_CONTROL_ENABLED; case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION: - return Global.HDMI_CEC_VERSION; + return setting.getName(); case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE: return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP; case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE: @@ -402,9 +405,6 @@ public class HdmiCecConfig { case Global.HDMI_CONTROL_ENABLED: notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); break; - case Global.HDMI_CEC_VERSION: - notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION); - break; case Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP: notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE); break; @@ -430,12 +430,20 @@ public class HdmiCecConfig { private void notifySettingChanged(@NonNull Setting setting) { synchronized (mLock) { - Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); + ArrayMap<SettingChangeListener, Executor> listeners = + mSettingChangeListeners.get(setting); if (listeners == null) { return; // No listeners registered, do nothing. } - for (SettingChangeListener listener: listeners) { - listener.onChange(setting.getName()); + for (Entry<SettingChangeListener, Executor> entry: listeners.entrySet()) { + SettingChangeListener listener = entry.getKey(); + Executor executor = entry.getValue(); + executor.execute(new Runnable() { + @Override + public void run() { + listener.onChange(setting.getName()); + } + }); } } } @@ -450,7 +458,6 @@ public class HdmiCecConfig { ContentResolver resolver = mContext.getContentResolver(); String[] settings = new String[] { Global.HDMI_CONTROL_ENABLED, - Global.HDMI_CEC_VERSION, Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, @@ -471,10 +478,19 @@ public class HdmiCecConfig { } /** - * Register change listener for a given setting name. + * Register change listener for a given setting name using DirectExecutor. */ public void registerChangeListener(@NonNull @CecSettingName String name, SettingChangeListener listener) { + registerChangeListener(name, listener, ConcurrentUtils.DIRECT_EXECUTOR); + } + + /** + * Register change listener for a given setting name and executor. + */ + public void registerChangeListener(@NonNull @CecSettingName String name, + SettingChangeListener listener, + Executor executor) { Setting setting = getSetting(name); if (setting == null) { throw new IllegalArgumentException("Setting '" + name + "' does not exist."); @@ -486,9 +502,9 @@ public class HdmiCecConfig { } synchronized (mLock) { if (!mSettingChangeListeners.containsKey(setting)) { - mSettingChangeListeners.put(setting, new HashSet<>()); + mSettingChangeListeners.put(setting, new ArrayMap<>()); } - mSettingChangeListeners.get(setting).add(listener); + mSettingChangeListeners.get(setting).put(listener, executor); } } @@ -503,7 +519,8 @@ public class HdmiCecConfig { } synchronized (mLock) { if (mSettingChangeListeners.containsKey(setting)) { - Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); + ArrayMap<SettingChangeListener, Executor> listeners = + mSettingChangeListeners.get(setting); listeners.remove(listener); if (listeners.isEmpty()) { mSettingChangeListeners.remove(setting); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index fa1fb48ce124..b4d9b01f716f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -108,6 +108,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.Executor; import java.util.stream.Collectors; /** @@ -199,6 +200,13 @@ public class HdmiControlService extends SystemService { public @interface WakeReason { } + private final Executor mServiceThreadExecutor = new Executor() { + @Override + public void execute(Runnable r) { + runOnServiceThread(r); + } + }; + // Logical address of the active source. @GuardedBy("mLock") protected final ActiveSource mActiveSource = new ActiveSource(); @@ -520,14 +528,14 @@ public class HdmiControlService extends SystemService { HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); setControlEnabled(enabled); } - }); + }, mServiceThreadExecutor); mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, new HdmiCecConfig.SettingChangeListener() { @Override public void onChange(String setting) { initializeCec(INITIATED_BY_ENABLE_CEC); } - }); + }, mServiceThreadExecutor); mHdmiCecConfig.registerChangeListener( HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY, new HdmiCecConfig.SettingChangeListener() { @@ -537,7 +545,7 @@ public class HdmiControlService extends SystemService { setCecOption(OptionKey.WAKEUP, tv().getAutoWakeup()); } } - }); + }, mServiceThreadExecutor); } private void bootCompleted() { @@ -774,8 +782,14 @@ public class HdmiControlService extends SystemService { private void initializeCec(int initiatedBy) { mAddressAllocated = false; - mCecVersion = getHdmiCecConfig().getIntValue( + int settingsCecVersion = getHdmiCecConfig().getIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION); + int supportedCecVersion = mCecController.getVersion(); + + // Limit the used CEC version to the highest supported version by HAL and selected + // version in settings (but at least v1.4b). + mCecVersion = Math.max(HdmiControlManager.HDMI_CEC_VERSION_1_4_B, + Math.min(settingsCecVersion, supportedCecVersion)); mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true); mCecController.setLanguage(mMenuLanguage); @@ -2184,6 +2198,7 @@ public class HdmiControlService extends SystemService { pw.println("mProhibitMode: " + mProhibitMode); pw.println("mPowerStatus: " + mPowerStatusController.getPowerStatus()); + pw.println("mCecVersion: " + mCecVersion); // System settings pw.println("System_settings:"); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java index ee3427f0a383..a1e613635116 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java @@ -70,6 +70,10 @@ final class HdmiControlShellCommand extends ShellCommand { pw.println(" --args <vendor specific arguments>"); pw.println(" [--id <true if vendor command should be sent with vendor id>]"); pw.println(" Send a Vendor Command to the given target device"); + pw.println(" cec_setting get <setting name>"); + pw.println(" Get the current value of a CEC setting"); + pw.println(" cec_setting set <setting name> <value>"); + pw.println(" Set the value of a CEC setting"); } private int handleShellCommand(String cmd) throws RemoteException { @@ -81,6 +85,8 @@ final class HdmiControlShellCommand extends ShellCommand { return oneTouchPlay(pw); case "vendorcommand": return vendorCommand(pw); + case "cec_setting": + return cecSetting(pw); } getErrPrintWriter().println("Unhandled command: " + cmd); @@ -157,4 +163,39 @@ final class HdmiControlShellCommand extends ShellCommand { mBinderService.sendVendorCommand(deviceType, destination, params, hasVendorId); return 0; } + + private int cecSetting(PrintWriter pw) throws RemoteException { + if (getRemainingArgsCount() < 1) { + throw new IllegalArgumentException("Expected at least 1 argument (operation)."); + } + String operation = getNextArgRequired(); + switch (operation) { + case "get": { + String setting = getNextArgRequired(); + try { + String value = mBinderService.getCecSettingStringValue(setting); + pw.println(setting + " = " + value); + } catch (IllegalArgumentException e) { + int intValue = mBinderService.getCecSettingIntValue(setting); + pw.println(setting + " = " + intValue); + } + return 0; + } + case "set": { + String setting = getNextArgRequired(); + String value = getNextArgRequired(); + try { + mBinderService.setCecSettingStringValue(setting, value); + pw.println(setting + " = " + value); + } catch (IllegalArgumentException e) { + int intValue = Integer.parseInt(value); + mBinderService.setCecSettingIntValue(setting, intValue); + pw.println(setting + " = " + intValue); + } + return 0; + } + default: + throw new IllegalArgumentException("Unknown operation: " + operation); + } + } } diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java index a7f34ed85e0d..4c4c9783fab0 100644 --- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java +++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java @@ -259,7 +259,7 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction { } private void mayDisableSystemAudioAndARC(int address) { - if (HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, address)) { + if (!HdmiUtils.isEligibleAddressForDevice(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, address)) { return; } diff --git a/services/core/java/com/android/server/hdmi/cec_key_handling.md b/services/core/java/com/android/server/hdmi/cec_key_handling.md index 1b41a6740869..69877bee0034 100644 --- a/services/core/java/com/android/server/hdmi/cec_key_handling.md +++ b/services/core/java/com/android/server/hdmi/cec_key_handling.md @@ -1,36 +1,37 @@ # CEC Key Handling -The mapping of CEC key codes to Android key codes are at -[HdmiCecKeycode](HdmiCecKeycode.java) +The mapping of CEC keycodes to Android keycodes is done at [HdmiCecKeycode](HdmiCecKeycode.java). # Android TV -Android TV requires special handling of some keys. +Android TV (ATV) requires special handling of some keys. -The general action for key handling is described in the table +The general action for key handling is described in the table below. | Android Key | TV Panel | OTT | Soundbar | | ----------- | ----------------- | ------------------- | ------------------- | -| general | Send to active source | handle on device | handle on device | | POWER | Toggle the device power state | Toggle the OTT power state, TV power state follows | Toggle the soundbar power state, TV power state follows| | TV_POWER | Toggle the device power state | Toggle the TV power state, OTT power state follows | Toggle the TV power state, soundbar power state follows| | HOME | Turn on TV, Set active Source to TV, go to home screen | OTP, and go to home screen | OTP, and go to home screen | -| volume keys | Handle on device or send to soundbar | Send to TV or soundbar | Handle on device or send to TV | +| Volume keys | Handle on device or send to soundbar | Send to TV or soundbar | Handle on device or send to TV | +| Other keys | Forward to active source | Handle on device | Handle on device | -Special cases and flags for each key are described below +Special cases and flags per key are described below. -## POWER +## TV_POWER ### TV Panel -TODO +For ATV TV panel devices, TV_POWER is an alias of POWER. -### OTT +### Source Devices (OTT and Soundbar) -TODO +For ATV source devices with POWER_CONTROL_MODE set to none or CEC control disabled, TV_POWER is an alias of POWER. -### Soundbar +For all other source devices, TV_POWER toggles the TV power state and makes the OTT power state follow. -TODO +### Other Devices + +For any device that is not connected to a TV via HDMI and not an ATV device, TV_POWER is ignored. diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index bd577f35f0a5..016c5ed36f5f 100644 --- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -1052,6 +1052,9 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) { if (verbose) Slog.v(mTag, "onChange(): uri=" + uri + ", userId=" + userId); final String property = uri.getLastPathSegment(); + if (property == null) { + return; + } if (property.equals(getServiceSettingsProperty()) || property.equals(Settings.Secure.USER_SETUP_COMPLETE)) { synchronized (mLock) { diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 4e974112a5c3..edb5d97f1a5a 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -17,6 +17,7 @@ package com.android.server.input; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -37,6 +38,7 @@ import android.content.res.Resources.NotFoundException; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.database.ContentObserver; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayViewport; import android.hardware.input.IInputDevicesChangedListener; @@ -50,6 +52,8 @@ import android.hardware.input.InputManagerInternal.LidSwitchCallback; import android.hardware.input.InputSensorInfo; import android.hardware.input.KeyboardLayout; import android.hardware.input.TouchCalibration; +import android.hardware.lights.Light; +import android.hardware.lights.LightState; import android.media.AudioManager; import android.os.Binder; import android.os.Bundle; @@ -69,6 +73,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.VibrationEffect; import android.provider.DeviceConfig; @@ -168,6 +173,9 @@ public class InputManagerService extends IInputManager.Stub /** TODO(b/169067926): Remove this. */ private static final boolean UNTRUSTED_TOUCHES_TOAST = false; + public static final boolean ENABLE_PER_WINDOW_INPUT_ROTATION = + SystemProperties.getBoolean("persist.debug.per_window_input_rotation", false); + // Pointer to native input manager service object. private final long mPtr; @@ -232,9 +240,21 @@ public class InputManagerService extends IInputManager.Stub // List of vibrator states by device id. @GuardedBy("mVibratorLock") private final SparseBooleanArray mIsVibrating = new SparseBooleanArray(); + private Object mLightLock = new Object(); + // State for light tokens. A light token marks a lights manager session, it is generated + // by light session open() and deleted in session close(). + // When lights session requests light states, the token will be used to find the light session. + @GuardedBy("mLightLock") + private final ArrayMap<IBinder, LightSession> mLightSessions = + new ArrayMap<IBinder, LightSession>(); // State for lid switch + // Lock for the lid switch state. Held when triggering callbacks to guarantee lid switch events + // are delivered in order. For ex, when a new lid switch callback is registered the lock is held + // while the callback is processing the initial lid switch event which guarantees that any + // events that occur at the same time are delivered after the callback has returned. private final Object mLidSwitchLock = new Object(); + @GuardedBy("mLidSwitchLock") private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>(); // State for the currently installed input filter. @@ -298,6 +318,12 @@ public class InputManagerService extends IInputManager.Stub private static native int[] nativeGetVibratorIds(long ptr, int deviceId); private static native int nativeGetBatteryCapacity(long ptr, int deviceId); private static native int nativeGetBatteryStatus(long ptr, int deviceId); + private static native List<Light> nativeGetLights(long ptr, int deviceId); + private static native int nativeGetLightPlayerId(long ptr, int deviceId, int lightId); + private static native int nativeGetLightColor(long ptr, int deviceId, int lightId); + private static native void nativeSetLightPlayerId(long ptr, int deviceId, int lightId, + int playerId); + private static native void nativeSetLightColor(long ptr, int deviceId, int lightId, int color); private static native void nativeReloadKeyboardLayouts(long ptr); private static native void nativeReloadDeviceAliases(long ptr); private static native String nativeDump(long ptr); @@ -382,9 +408,6 @@ public class InputManagerService extends IInputManager.Stub public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER; public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE; - /** Indicates an open state for the lid switch. */ - public static final int SW_STATE_LID_OPEN = 0; - /** Whether to use the dev/input/event or uevent subsystem for the audio jack. */ final boolean mUseDevInputEventForAudioJack; @@ -420,13 +443,18 @@ public class InputManagerService extends IInputManager.Stub } void registerLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) { - boolean lidOpen; synchronized (mLidSwitchLock) { mLidSwitchCallbacks.add(callback); - lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID) - == SW_STATE_LID_OPEN; + + // Skip triggering the initial callback if the system is not yet ready as the switch + // state will be reported as KEY_STATE_UNKNOWN. The callback will be triggered in + // systemRunning(). + if (mSystemReady) { + boolean lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID) + == KEY_STATE_UP; + callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen); + } } - callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen); } void unregisterLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) { @@ -474,7 +502,18 @@ public class InputManagerService extends IInputManager.Stub } mNotificationManager = (NotificationManager)mContext.getSystemService( Context.NOTIFICATION_SERVICE); - mSystemReady = true; + + synchronized (mLidSwitchLock) { + mSystemReady = true; + + // Send the initial lid switch state to any callback registered before the system was + // ready. + int switchState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID); + for (int i = 0; i < mLidSwitchCallbacks.size(); i++) { + LidSwitchCallback callback = mLidSwitchCallbacks.get(i); + callback.notifyLidSwitchChanged(0 /* whenNanos */, switchState == KEY_STATE_UP); + } + } IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -518,8 +557,54 @@ public class InputManagerService extends IInputManager.Stub nativeReloadDeviceAliases(mPtr); } + /** Rotates CCW by `delta` 90-degree increments. */ + private static void rotateBounds(Rect inOutBounds, int parentW, int parentH, int delta) { + int rdelta = ((delta % 4) + 4) % 4; + int origLeft = inOutBounds.left; + switch (rdelta) { + case 0: + return; + case 1: + inOutBounds.left = inOutBounds.top; + inOutBounds.top = parentW - inOutBounds.right; + inOutBounds.right = inOutBounds.bottom; + inOutBounds.bottom = parentW - origLeft; + return; + case 2: + inOutBounds.left = parentW - inOutBounds.right; + inOutBounds.right = parentW - origLeft; + return; + case 3: + inOutBounds.left = parentH - inOutBounds.bottom; + inOutBounds.bottom = inOutBounds.right; + inOutBounds.right = parentH - inOutBounds.top; + inOutBounds.top = origLeft; + return; + } + } + private void setDisplayViewportsInternal(List<DisplayViewport> viewports) { - nativeSetDisplayViewports(mPtr, viewports.toArray(new DisplayViewport[0])); + final DisplayViewport[] vArray = new DisplayViewport[viewports.size()]; + if (ENABLE_PER_WINDOW_INPUT_ROTATION) { + // Remove all viewport operations. They will be built-into the window transforms. + for (int i = viewports.size() - 1; i >= 0; --i) { + final DisplayViewport v = vArray[i] = viewports.get(i).makeCopy(); + // deviceWidth/Height are apparently in "rotated" space, so flip them if needed. + if (v.orientation % 2 != 0) { + final int dw = v.deviceWidth; + v.deviceWidth = v.deviceHeight; + v.deviceHeight = dw; + } + v.logicalFrame.set(0, 0, v.deviceWidth, v.deviceHeight); + v.physicalFrame.set(0, 0, v.deviceWidth, v.deviceHeight); + v.orientation = 0; + } + } else { + for (int i = viewports.size() - 1; i >= 0; --i) { + vArray[i] = viewports.get(i); + } + } + nativeSetDisplayViewports(mPtr, vArray); } /** @@ -2246,6 +2331,151 @@ public class InputManagerService extends IInputManager.Stub } } + /** + * LightSession represents a light session for lights manager. + */ + private final class LightSession implements DeathRecipient { + private final int mDeviceId; + private final IBinder mToken; + private final String mOpPkg; + // The light ids and states that are requested by the light seesion + private int[] mLightIds; + private LightState[] mLightStates; + + LightSession(int deviceId, String opPkg, IBinder token) { + mDeviceId = deviceId; + mOpPkg = opPkg; + mToken = token; + } + + @Override + public void binderDied() { + if (DEBUG) { + Slog.d(TAG, "Light token died."); + } + synchronized (mLightLock) { + closeLightSession(mDeviceId, mToken); + mLightSessions.remove(mToken); + } + } + } + + /** + * Returns the lights available for apps to control on the specified input device. + * Only lights that aren't reserved for system use are available to apps. + */ + @Override // Binder call + public List<Light> getLights(int deviceId) { + return nativeGetLights(mPtr, deviceId); + } + + /** + * Set specified light state with for a specific input device. + */ + private void setLightStateInternal(int deviceId, Light light, LightState lightState) { + Preconditions.checkNotNull(light, "light does not exist"); + if (DEBUG) { + Slog.d(TAG, "setLightStateInternal device " + deviceId + " light " + light + + "lightState " + lightState); + } + if (light.getType() == Light.LIGHT_TYPE_INPUT_PLAYER_ID) { + nativeSetLightPlayerId(mPtr, deviceId, light.getId(), lightState.getPlayerId()); + } else if (light.getType() == Light.LIGHT_TYPE_INPUT_SINGLE + || light.getType() == Light.LIGHT_TYPE_INPUT_RGB) { + // Set ARGB format color to input device light + // Refer to https://developer.android.com/reference/kotlin/android/graphics/Color + nativeSetLightColor(mPtr, deviceId, light.getId(), lightState.getColor()); + } else { + Slog.e(TAG, "setLightStates for unsupported light type " + light.getType()); + } + } + + /** + * Set multiple light states with multiple light ids for a specific input device. + */ + private void setLightStatesInternal(int deviceId, int[] lightIds, LightState[] lightStates) { + final List<Light> lights = nativeGetLights(mPtr, deviceId); + SparseArray<Light> lightArray = new SparseArray<>(); + for (int i = 0; i < lights.size(); i++) { + lightArray.put(lights.get(i).getId(), lights.get(i)); + } + for (int i = 0; i < lightIds.length; i++) { + if (lightArray.contains(lightIds[i])) { + setLightStateInternal(deviceId, lightArray.get(lightIds[i]), lightStates[i]); + } + } + } + + /** + * Set states for multiple lights for an opened light session. + */ + @Override + public void setLightStates(int deviceId, int[] lightIds, LightState[] lightStates, + IBinder token) { + Preconditions.checkArgument(lightIds.length == lightStates.length, + "lights and light states are not same length"); + synchronized (mLightLock) { + LightSession lightSession = mLightSessions.get(token); + Preconditions.checkArgument(lightSession != null, "not registered"); + Preconditions.checkState(lightSession.mDeviceId == deviceId, "Incorrect device ID"); + lightSession.mLightIds = lightIds.clone(); + lightSession.mLightStates = lightStates.clone(); + if (DEBUG) { + Slog.d(TAG, "setLightStates for " + lightSession.mOpPkg + " device " + deviceId); + } + } + setLightStatesInternal(deviceId, lightIds, lightStates); + } + + @Override + public @Nullable LightState getLightState(int deviceId, int lightId) { + synchronized (mLightLock) { + int color = nativeGetLightColor(mPtr, deviceId, lightId); + int playerId = nativeGetLightPlayerId(mPtr, deviceId, lightId); + + return new LightState(color, playerId); + } + } + + @Override + public void openLightSession(int deviceId, String opPkg, IBinder token) { + Preconditions.checkNotNull(token); + synchronized (mLightLock) { + Preconditions.checkState(mLightSessions.get(token) == null, "already registered"); + LightSession lightSession = new LightSession(deviceId, opPkg, token); + try { + token.linkToDeath(lightSession, 0); + } catch (RemoteException ex) { + // give up + ex.rethrowAsRuntimeException(); + } + mLightSessions.put(token, lightSession); + if (DEBUG) { + Slog.d(TAG, "Open light session for " + opPkg + " device " + deviceId); + } + } + } + + @Override + public void closeLightSession(int deviceId, IBinder token) { + Preconditions.checkNotNull(token); + synchronized (mLightLock) { + LightSession lightSession = mLightSessions.get(token); + Preconditions.checkState(lightSession != null, "not registered"); + // Turn off the lights that were previously requested by the session to be closed. + Arrays.fill(lightSession.mLightStates, new LightState(0)); + setLightStatesInternal(deviceId, lightSession.mLightIds, + lightSession.mLightStates); + mLightSessions.remove(token); + // If any other session is still pending with light request, apply the first session's + // request. + if (!mLightSessions.isEmpty()) { + LightSession nextSession = mLightSessions.valueAt(0); + setLightStatesInternal(deviceId, nextSession.mLightIds, nextSession.mLightStates); + } + } + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; @@ -2331,14 +2561,13 @@ public class InputManagerService extends IInputManager.Stub if ((switchMask & SW_LID_BIT) != 0) { final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0); - - ArrayList<LidSwitchCallback> callbacksCopy; synchronized (mLidSwitchLock) { - callbacksCopy = new ArrayList<>(mLidSwitchCallbacks); - } - for (int i = 0; i < callbacksCopy.size(); i++) { - LidSwitchCallback callbacks = callbacksCopy.get(i); - callbacks.notifyLidSwitchChanged(whenNanos, lidOpen); + if (mSystemReady) { + for (int i = 0; i < mLidSwitchCallbacks.size(); i++) { + LidSwitchCallback callbacks = mLidSwitchCallbacks.get(i); + callbacks.notifyLidSwitchChanged(whenNanos, lidOpen); + } + } } } diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java index d08980cfbf24..720be826fd38 100644 --- a/services/core/java/com/android/server/input/InputShellCommand.java +++ b/services/core/java/com/android/server/input/InputShellCommand.java @@ -29,6 +29,7 @@ import android.view.MotionEvent; import android.view.ViewConfiguration; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -199,6 +200,8 @@ public class InputShellCommand extends ShellCommand { runRoll(inputSource, displayId); } else if ("motionevent".equals(arg)) { runMotionEvent(inputSource, displayId); + } else if ("keycombination".equals(arg)) { + runKeyCombination(inputSource, displayId); } else { handleDefaultCommands(arg); } @@ -224,7 +227,7 @@ public class InputShellCommand extends ShellCommand { out.println(); out.println("The commands and default sources are:"); out.println(" text <string> (Default: touchscreen)"); - out.println(" keyevent [--longpress] <key code number or name> ..." + out.println(" keyevent [--longpress|--doubletap] <key code number or name> ..." + " (Default: keyboard)"); out.println(" tap <x> <y> (Default: touchscreen)"); out.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]" @@ -234,6 +237,8 @@ public class InputShellCommand extends ShellCommand { out.println(" press (Default: trackball)"); out.println(" roll <dx> <dy> (Default: trackball)"); out.println(" motionevent <DOWN|UP|MOVE|CANCEL> <x> <y> (Default: touchscreen)"); + out.println(" keycombination <key code 1> <key code 2> ..." + + " (Default: keyboard)"); } } @@ -282,6 +287,14 @@ public class InputShellCommand extends ShellCommand { final boolean longpress = "--longpress".equals(arg); if (longpress) { arg = getNextArgRequired(); + } else { + final boolean doubleTap = "--doubletap".equals(arg); + if (doubleTap) { + arg = getNextArgRequired(); + final int keycode = KeyEvent.keyCodeFromString(arg); + sendKeyDoubleTap(inputSource, keycode, displayId); + return; + } } do { @@ -292,22 +305,32 @@ public class InputShellCommand extends ShellCommand { private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) { final long now = SystemClock.uptimeMillis(); - int repeatCount = 0; - KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount, + KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0 /* repeatCount */, 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/, inputSource); event.setDisplayId(displayId); injectKeyEvent(event); if (longpress) { - repeatCount++; - injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount, + // Some long press behavior would check the event time, we set a new event time here. + final long nextEventTime = now + ViewConfiguration.getGlobalActionKeyTimeout(); + injectKeyEvent(KeyEvent.changeTimeRepeat(event, nextEventTime, 1 /* repeatCount */, KeyEvent.FLAG_LONG_PRESS)); } injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP)); } + private void sendKeyDoubleTap(int inputSource, int keyCode, int displayId) { + sendKeyEvent(inputSource, keyCode, false, displayId); + try { + Thread.sleep(ViewConfiguration.getDoubleTapMinTime()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + sendKeyEvent(inputSource, keyCode, false, displayId); + } + private void runTap(int inputSource, int displayId) { inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN); sendTap(inputSource, Float.parseFloat(getNextArgRequired()), @@ -440,4 +463,59 @@ public class InputShellCommand extends ShellCommand { final long now = SystemClock.uptimeMillis(); injectMotionEvent(inputSource, action, now, now, x, y, pressure, displayId); } + + private void runKeyCombination(int inputSource, int displayId) { + String arg = getNextArgRequired(); + ArrayList<Integer> keyCodes = new ArrayList<>(); + + while (arg != null) { + final int keyCode = KeyEvent.keyCodeFromString(arg); + if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { + throw new IllegalArgumentException("Unknown keycode: " + arg); + } + keyCodes.add(keyCode); + arg = getNextArg(); + } + + // At least 2 keys. + if (keyCodes.size() < 2) { + throw new IllegalArgumentException("keycombination requires at least 2 keycodes"); + } + + sendKeyCombination(inputSource, keyCodes, displayId); + } + + private void injectKeyEventAsync(KeyEvent event) { + InputManager.getInstance().injectInputEvent(event, + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + + private void sendKeyCombination(int inputSource, ArrayList<Integer> keyCodes, int displayId) { + final long now = SystemClock.uptimeMillis(); + final int count = keyCodes.size(); + final KeyEvent[] events = new KeyEvent[count]; + for (int i = 0; i < count; i++) { + final KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCodes.get(i), 0, + 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/, + inputSource); + event.setDisplayId(displayId); + events[i] = event; + } + + for (KeyEvent event: events) { + // Use async inject so interceptKeyBeforeQueueing or interceptKeyBeforeDispatching could + // handle keys. + injectKeyEventAsync(event); + } + + try { + Thread.sleep(ViewConfiguration.getTapTimeout()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + for (KeyEvent event: events) { + injectKeyEventAsync(KeyEvent.changeAction(event, KeyEvent.ACTION_UP)); + } + } } diff --git a/services/core/java/com/android/server/input/OWNERS b/services/core/java/com/android/server/input/OWNERS index 0313a40f7270..82c6ee12c7ae 100644 --- a/services/core/java/com/android/server/input/OWNERS +++ b/services/core/java/com/android/server/input/OWNERS @@ -1,2 +1,3 @@ +lzye@google.com michaelwr@google.com svv@google.com diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0754df0e6b9f..d41f4c76861e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3646,12 +3646,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub hideCurrentInputLocked(mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR); } - res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute, - startInputFlags, startInputReason); - } else { - res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute, - startInputFlags, startInputReason); } + res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute, + startInputFlags, startInputReason); } else { res = InputBindResult.NULL_EDITOR_INFO; } @@ -5412,37 +5409,38 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @ShellCommandResult private int onCommandWithSystemIdentity(@Nullable String cmd) { - if ("get-last-switch-user-id".equals(cmd)) { - return mService.getLastSwitchUserId(this); - } - - // For existing "adb shell ime <command>". - if ("ime".equals(cmd)) { - final String imeCommand = getNextArg(); - if (imeCommand == null || "help".equals(imeCommand) || "-h".equals(imeCommand)) { - onImeCommandHelp(); - return ShellCommandResult.SUCCESS; - } - switch (imeCommand) { - case "list": - return mService.handleShellCommandListInputMethods(this); - case "enable": - return mService.handleShellCommandEnableDisableInputMethod(this, true); - case "disable": - return mService.handleShellCommandEnableDisableInputMethod(this, false); - case "set": - return mService.handleShellCommandSetInputMethod(this); - case "reset": - return mService.handleShellCommandResetInputMethod(this); - case "tracing": - return mService.handleShellCommandTraceInputMethod(this); - default: - getOutPrintWriter().println("Unknown command: " + imeCommand); - return ShellCommandResult.FAILURE; + switch (TextUtils.emptyIfNull(cmd)) { + case "get-last-switch-user-id": + return mService.getLastSwitchUserId(this); + case "tracing": + return mService.handleShellCommandTraceInputMethod(this); + case "ime": { // For "adb shell ime <command>". + final String imeCommand = TextUtils.emptyIfNull(getNextArg()); + switch (imeCommand) { + case "": + case "-h": + case "help": + return onImeCommandHelp(); + case "list": + return mService.handleShellCommandListInputMethods(this); + case "enable": + return mService.handleShellCommandEnableDisableInputMethod(this, true); + case "disable": + return mService.handleShellCommandEnableDisableInputMethod(this, false); + case "set": + return mService.handleShellCommandSetInputMethod(this); + case "reset": + return mService.handleShellCommandResetInputMethod(this); + case "tracing": // TODO(b/180765389): Unsupport "adb shell ime tracing" + return mService.handleShellCommandTraceInputMethod(this); + default: + getOutPrintWriter().println("Unknown command: " + imeCommand); + return ShellCommandResult.FAILURE; + } } + default: + return handleDefaultCommands(cmd); } - - return handleDefaultCommands(cmd); } @BinderThread @@ -5456,10 +5454,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub pw.println(" Synonym of dumpsys."); pw.println(" ime <command> [options]"); pw.println(" Manipulate IMEs. Run \"ime help\" for details."); + pw.println(" tracing <command>"); + pw.println(" start: Start tracing."); + pw.println(" stop : Stop tracing."); + pw.println(" help : Show help."); } } - private void onImeCommandHelp() { + @BinderThread + @ShellCommandResult + private int onImeCommandHelp() { try (IndentingPrintWriter pw = new IndentingPrintWriter(getOutPrintWriter(), " ", 100)) { pw.println("ime <command>:"); @@ -5514,6 +5518,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub pw.decreaseIndent(); } + return ShellCommandResult.SUCCESS; } } diff --git a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java index 7b400b6e0309..6ea4bd2b1d6d 100644 --- a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java +++ b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java @@ -17,7 +17,6 @@ package com.android.server.location; import android.annotation.Nullable; -import android.content.ComponentName; import android.content.Context; import android.hardware.location.ActivityRecognitionHardware; import android.hardware.location.IActivityRecognitionHardwareClient; @@ -27,6 +26,7 @@ import android.os.RemoteException; import android.util.Log; import com.android.server.ServiceWatcher; +import com.android.server.ServiceWatcher.BoundService; /** * Proxy class to bind GmsCore to the ActivityRecognitionHardware. @@ -82,7 +82,7 @@ public class HardwareActivityRecognitionProxy { return resolves; } - private void onBind(IBinder binder, ComponentName service) throws RemoteException { + private void onBind(IBinder binder, BoundService service) throws RemoteException { String descriptor = binder.getInterfaceDescriptor(); if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) { diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 6deb19a53629..2920ddb2d76d 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -52,6 +52,7 @@ import android.location.GnssCapabilities; import android.location.GnssMeasurementCorrections; import android.location.GnssMeasurementRequest; import android.location.IGeocodeListener; +import android.location.IGnssAntennaInfoListener; import android.location.IGnssMeasurementsListener; import android.location.IGnssNavigationMessageListener; import android.location.IGnssNmeaListener; @@ -63,6 +64,7 @@ import android.location.LastLocationRequest; import android.location.Location; import android.location.LocationManager; import android.location.LocationManagerInternal; +import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener; import android.location.LocationProvider; import android.location.LocationRequest; import android.location.LocationTime; @@ -70,6 +72,7 @@ import android.location.provider.IProviderRequestListener; import android.location.provider.ProviderProperties; import android.location.util.identity.CallerIdentity; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.ICancellationSignal; import android.os.ParcelFileDescriptor; @@ -81,6 +84,7 @@ import android.os.WorkSource.WorkChain; import android.provider.Settings; import android.stats.location.LocationStatsEnums; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Log; @@ -251,9 +255,12 @@ public class LocationManagerService extends ILocationManager.Stub { // @GuardedBy("mProviderManagers") // hold lock for writes, no lock necessary for simple reads - private final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers = + final CopyOnWriteArrayList<LocationProviderManager> mProviderManagers = new CopyOnWriteArrayList<>(); + @GuardedBy("mLock") + private @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener; + LocationManagerService(Context context, Injector injector, LocationEventLog eventLog) { mContext = context.createAttributionContext(ATTRIBUTION_TAG); mInjector = injector; @@ -283,7 +290,7 @@ public class LocationManagerService extends ILocationManager.Stub { } @Nullable - private LocationProviderManager getLocationProviderManager(String providerName) { + LocationProviderManager getLocationProviderManager(String providerName) { if (providerName == null) { return null; } @@ -318,8 +325,9 @@ public class LocationManagerService extends ILocationManager.Stub { Preconditions.checkState(getLocationProviderManager(manager.getName()) == null); manager.startManager(); + manager.setOnProviderLocationTagsChangeListener( + mOnProviderLocationTagsChangeListener); if (realProvider != null) { - // custom logic wrapping all non-passive providers if (manager != mPassiveManager) { boolean enableStationaryThrottling = Settings.Global.getInt( @@ -330,7 +338,6 @@ public class LocationManagerService extends ILocationManager.Stub { mInjector, realProvider, mEventLog); } } - manager.setRealProvider(realProvider); } mProviderManagers.add(manager); @@ -350,6 +357,22 @@ public class LocationManagerService extends ILocationManager.Stub { void onSystemReady() { mInjector.getSettingsHelper().addOnLocationEnabledChangedListener( this::onLocationModeChanged); + + if (Build.IS_DEBUGGABLE) { + // on debug builds, watch for location noteOps while location is off. there are some + // scenarios (emergency location) where this is expected, but generally this should + // rarely occur, and may indicate bugs. dump occurrences to logs for further evaluation + AppOpsManager appOps = Objects.requireNonNull( + mContext.getSystemService(AppOpsManager.class)); + appOps.startWatchingNoted( + new int[]{AppOpsManager.OP_FINE_LOCATION, AppOpsManager.OP_COARSE_LOCATION}, + (code, uid, packageName, attributionTag, flags, result) -> { + if (!isLocationEnabledForUser(UserHandle.getUserId(uid))) { + Log.w(TAG, "location noteOp with location off - " + + CallerIdentity.forTest(uid, 0, packageName, attributionTag)); + } + }); + } } void onSystemThirdPartyAppsCanStart() { @@ -439,8 +462,9 @@ public class LocationManagerService extends ILocationManager.Stub { .setPowerUsage(Integer.parseInt(fragments[8])) .setAccuracy(Integer.parseInt(fragments[9])) .build(); - getOrAddLocationProviderManager(name).setMockProvider( - new MockLocationProvider(properties, CallerIdentity.fromContext(mContext))); + final LocationProviderManager manager = getOrAddLocationProviderManager(name); + manager.setMockProvider(new MockLocationProvider(properties, + CallerIdentity.fromContext(mContext), Collections.emptySet())); } } @@ -479,7 +503,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void startGnssBatch(long periodNanos, ILocationListener listener, String packageName, - String attributionTag, String listenerId) { + @Nullable String attributionTag, String listenerId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null); if (mGnssManagerService == null) { @@ -616,7 +640,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Nullable @Override public ICancellationSignal getCurrentLocation(String provider, LocationRequest request, - ILocationCallback consumer, String packageName, String attributionTag, + ILocationCallback consumer, String packageName, @Nullable String attributionTag, String listenerId) { CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, listenerId); @@ -640,7 +664,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void registerLocationListener(String provider, LocationRequest request, ILocationListener listener, String packageName, @Nullable String attributionTag, - @Nullable String listenerId) { + String listenerId) { CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, listenerId); int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), @@ -791,7 +815,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public Location getLastLocation(String provider, LastLocationRequest request, - String packageName, String attributionTag) { + String packageName, @Nullable String attributionTag) { CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); int permissionLevel = LocationPermissions.getPermissionLevel(mContext, identity.getUid(), identity.getPid()); @@ -858,9 +882,10 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void registerGnssStatusCallback(IGnssStatusListener listener, String packageName, - String attributionTag) { + @Nullable String attributionTag, String listenerId) { if (mGnssManagerService != null) { - mGnssManagerService.registerGnssStatusCallback(listener, packageName, attributionTag); + mGnssManagerService.registerGnssStatusCallback(listener, packageName, attributionTag, + listenerId); } } @@ -873,9 +898,10 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void registerGnssNmeaCallback(IGnssNmeaListener listener, String packageName, - String attributionTag) { + @Nullable String attributionTag, String listenerId) { if (mGnssManagerService != null) { - mGnssManagerService.registerGnssNmeaCallback(listener, packageName, attributionTag); + mGnssManagerService.registerGnssNmeaCallback(listener, packageName, attributionTag, + listenerId); } } @@ -887,11 +913,12 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public void addGnssMeasurementsListener(@Nullable GnssMeasurementRequest request, - IGnssMeasurementsListener listener, String packageName, String attributionTag) { + public void addGnssMeasurementsListener(GnssMeasurementRequest request, + IGnssMeasurementsListener listener, String packageName, @Nullable String attributionTag, + String listenerId) { if (mGnssManagerService != null) { mGnssManagerService.addGnssMeasurementsListener(request, listener, packageName, - attributionTag); + attributionTag, listenerId); } } @@ -904,6 +931,22 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override + public void addGnssAntennaInfoListener(IGnssAntennaInfoListener listener, String packageName, + @Nullable String attributionTag, String listenerId) { + if (mGnssManagerService != null) { + mGnssManagerService.addGnssAntennaInfoListener(listener, packageName, attributionTag, + listenerId); + } + } + + @Override + public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) { + if (mGnssManagerService != null) { + mGnssManagerService.removeGnssAntennaInfoListener(listener); + } + } + + @Override public void addProviderRequestListener(IProviderRequestListener listener) { for (LocationProviderManager manager : mProviderManagers) { manager.addProviderRequestListener(listener); @@ -937,10 +980,10 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void addGnssNavigationMessageListener(IGnssNavigationMessageListener listener, - String packageName, String attributionTag) { + String packageName, @Nullable String attributionTag, String listenerId) { if (mGnssManagerService != null) { mGnssManagerService.addGnssNavigationMessageListener(listener, packageName, - attributionTag); + attributionTag, listenerId); } } @@ -1127,15 +1170,16 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void addTestProvider(String provider, ProviderProperties properties, - String packageName, String attributionTag) { + List<String> extraAttributionTags, String packageName, String attributionTag) { // unsafe is ok because app ops will verify the package name CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag); if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) { return; } - getOrAddLocationProviderManager(provider).setMockProvider( - new MockLocationProvider(properties, identity)); + final LocationProviderManager manager = getOrAddLocationProviderManager(provider); + manager.setMockProvider(new MockLocationProvider(properties, identity, + new ArraySet<>(extraAttributionTags))); } @Override @@ -1268,13 +1312,13 @@ public class LocationManagerService extends ILocationManager.Stub { ipw.println("Historical Aggregate Location Provider Data:"); ipw.increaseIndent(); - ArrayMap<String, ArrayMap<String, LocationEventLog.AggregateStats>> aggregateStats = + ArrayMap<String, ArrayMap<CallerIdentity, LocationEventLog.AggregateStats>> aggregateStats = mEventLog.copyAggregateStats(); for (int i = 0; i < aggregateStats.size(); i++) { ipw.print(aggregateStats.keyAt(i)); ipw.println(":"); ipw.increaseIndent(); - ArrayMap<String, LocationEventLog.AggregateStats> providerStats = + ArrayMap<CallerIdentity, LocationEventLog.AggregateStats> providerStats = aggregateStats.valueAt(i); for (int j = 0; j < providerStats.size(); j++) { ipw.print(providerStats.keyAt(j)); @@ -1342,7 +1386,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (provider != null && !provider.equals(manager.getName())) { continue; } - if (identity.equalsIgnoringListenerId(manager.getIdentity())) { + if (identity.equals(manager.getIdentity())) { return true; } } @@ -1372,6 +1416,19 @@ public class LocationManagerService extends ILocationManager.Stub { return new LocationTime(location.getTime(), location.getElapsedRealtimeNanos()); } + + @Override + public void setOnProviderLocationTagsChangeListener( + @Nullable OnProviderLocationTagsChangeListener listener) { + synchronized (mLock) { + mOnProviderLocationTagsChangeListener = listener; + final int providerCount = mProviderManagers.size(); + for (int i = 0; i < providerCount; i++) { + final LocationProviderManager manager = mProviderManagers.get(i); + manager.setOnProviderLocationTagsChangeListener(listener); + } + } + } } private static class SystemInjector implements Injector { diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java index f0dd8b559d64..5dc3ed8c47bd 100644 --- a/services/core/java/com/android/server/location/LocationShellCommand.java +++ b/services/core/java/com/android/server/location/LocationShellCommand.java @@ -26,6 +26,8 @@ import com.android.modules.utils.BasicShellCommandHandler; import java.io.PrintWriter; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -50,15 +52,11 @@ class LocationShellCommand extends BasicShellCommandHandler { switch (cmd) { case "is-location-enabled": { - int userId = parseUserId(); - boolean enabled = mService.isLocationEnabledForUser(userId); - getOutPrintWriter().println(enabled); + handleIsLocationEnabled(); return 0; } case "set-location-enabled": { - int userId = parseUserId(); - boolean enabled = Boolean.parseBoolean(getNextArgRequired()); - mService.setLocationEnabledForUser(enabled, userId); + handleSetLocationEnabled(); return 0; } case "providers": { @@ -73,36 +71,23 @@ class LocationShellCommand extends BasicShellCommandHandler { private int parseProvidersCommand(String cmd) { switch (cmd) { case "add-test-provider": { - String provider = getNextArgRequired(); - ProviderProperties properties = parseTestProviderProviderProperties(); - mService.addTestProvider(provider, properties, mContext.getOpPackageName(), - mContext.getFeatureId()); + handleAddTestProvider(); return 0; } case "remove-test-provider": { - String provider = getNextArgRequired(); - mService.removeTestProvider(provider, mContext.getOpPackageName(), - mContext.getFeatureId()); + handleRemoveTestProvider(); return 0; } case "set-test-provider-enabled": { - String provider = getNextArgRequired(); - boolean enabled = Boolean.parseBoolean(getNextArgRequired()); - mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName(), - mContext.getFeatureId()); + handleSetTestProviderEnabled(); return 0; } case "set-test-provider-location": { - String provider = getNextArgRequired(); - Location location = parseTestProviderLocation(provider); - mService.setTestProviderLocation(provider, location, mContext.getOpPackageName(), - mContext.getFeatureId()); + handleSetTestProviderLocation(); return 0; } case "send-extra-command": { - String provider = getNextArgRequired(); - String command = getNextArgRequired(); - mService.sendExtraCommand(provider, command, null); + handleSendExtraCommand(); return 0; } default: @@ -110,21 +95,47 @@ class LocationShellCommand extends BasicShellCommandHandler { } } - private int parseUserId() { - final String option = getNextOption(); - if (option != null) { - if (option.equals("--user")) { - return UserHandle.parseUserArg(getNextArgRequired()); + private void handleIsLocationEnabled() { + int userId = UserHandle.USER_CURRENT_OR_SELF; + + do { + String option = getNextOption(); + if (option == null) { + break; + } + if ("--user".equals(option)) { + userId = UserHandle.parseUserArg(getNextArgRequired()); } else { - throw new IllegalArgumentException( - "Expected \"--user\" option, but got \"" + option + "\" instead"); + throw new IllegalArgumentException("Unknown option: " + option); } - } + } while (true); - return UserHandle.USER_CURRENT_OR_SELF; + getOutPrintWriter().println(mService.isLocationEnabledForUser(userId)); } - private ProviderProperties parseTestProviderProviderProperties() { + private void handleSetLocationEnabled() { + boolean enabled = Boolean.parseBoolean(getNextArgRequired()); + + int userId = UserHandle.USER_CURRENT_OR_SELF; + + do { + String option = getNextOption(); + if (option == null) { + break; + } + if ("--user".equals(option)) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } else { + throw new IllegalArgumentException("Unknown option: " + option); + } + } while (true); + + mService.setLocationEnabledForUser(enabled, userId); + } + + private void handleAddTestProvider() { + String provider = getNextArgRequired(); + boolean requiresNetwork = false; boolean requiresSatellite = false; boolean requiresCell = false; @@ -135,8 +146,13 @@ class LocationShellCommand extends BasicShellCommandHandler { int powerRequirement = Criteria.POWER_LOW; int accuracy = Criteria.ACCURACY_FINE; - String option = getNextOption(); - while (option != null) { + List<String> extraAttributionTags = Collections.emptyList(); + + do { + String option = getNextOption(); + if (option == null) { + break; + } switch (option) { case "--requiresNetwork": { requiresNetwork = true; @@ -174,12 +190,15 @@ class LocationShellCommand extends BasicShellCommandHandler { accuracy = Integer.parseInt(getNextArgRequired()); break; } + case "--extraAttributionTags": { + extraAttributionTags = Arrays.asList(getNextArgRequired().split(",")); + break; + } default: throw new IllegalArgumentException( "Received unexpected option: " + option); } - option = getNextOption(); - } + } while(true); ProviderProperties properties = new ProviderProperties.Builder() .setHasNetworkRequirement(requiresNetwork) @@ -192,30 +211,50 @@ class LocationShellCommand extends BasicShellCommandHandler { .setPowerUsage(powerRequirement) .setAccuracy(accuracy) .build(); + mService.addTestProvider(provider, properties, extraAttributionTags, + mContext.getOpPackageName(), mContext.getAttributionTag()); + } - return properties; + private void handleRemoveTestProvider() { + String provider = getNextArgRequired(); + mService.removeTestProvider(provider, mContext.getOpPackageName(), + mContext.getAttributionTag()); } - private Location parseTestProviderLocation(String provider) { - boolean hasLatitude = false; - boolean hasLongitude = false; + private void handleSetTestProviderEnabled() { + String provider = getNextArgRequired(); + boolean enabled = Boolean.parseBoolean(getNextArgRequired()); + mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName(), + mContext.getAttributionTag()); + } + + private void handleSetTestProviderLocation() { + String provider = getNextArgRequired(); + + boolean hasLatLng = false; Location location = new Location(provider); location.setAccuracy(DEFAULT_TEST_LOCATION_ACCURACY); location.setTime(System.currentTimeMillis()); + location.setElapsedRealtimeNanos(System.nanoTime()); - String option = getNextOption(); - while (option != null) { + do { + String option = getNextOption(); + if (option == null) { + break; + } switch (option) { case "--location": { String[] locationInput = getNextArgRequired().split(","); if (locationInput.length != 2) { - throw new IllegalArgumentException( - "Unexpected location format: " + Arrays.toString(locationInput)); + throw new IllegalArgumentException("Location argument must be in the form " + + "of \"<LATITUDE>,<LONGITUDE>\", not " + + Arrays.toString(locationInput)); } location.setLatitude(Double.parseDouble(locationInput[0])); location.setLongitude(Double.parseDouble(locationInput[1])); + hasLatLng = true; break; } case "--accuracy": { @@ -227,15 +266,22 @@ class LocationShellCommand extends BasicShellCommandHandler { break; } default: - throw new IllegalArgumentException( - "Received unexpected option: " + option); + throw new IllegalArgumentException("Unknown option: " + option); } - option = getNextOption(); + } while (true); + + if (!hasLatLng) { + throw new IllegalArgumentException("Option \"--location\" is required"); } - location.setElapsedRealtimeNanos(System.nanoTime()); + mService.setTestProviderLocation(provider, location, mContext.getOpPackageName(), + mContext.getAttributionTag()); + } - return location; + private void handleSendExtraCommand() { + String provider = getNextArgRequired(); + String command = getNextArgRequired(); + mService.sendExtraCommand(provider, command, null); } @Override @@ -245,24 +291,29 @@ class LocationShellCommand extends BasicShellCommandHandler { pw.println(" help or -h"); pw.println(" Print this help text."); pw.println(" is-location-enabled [--user <USER_ID>]"); - pw.println(" Gets the master location switch enabled state."); - pw.println(" set-location-enabled [--user <USER_ID>] true|false"); - pw.println(" Sets the master location switch enabled state."); + pw.println(" Gets the master location switch enabled state. If no user is specified,"); + pw.println(" the current user is assumed."); + pw.println(" set-location-enabled true|false [--user <USER_ID>]"); + pw.println(" Sets the master location switch enabled state. If no user is specified,"); + pw.println(" the current user is assumed."); pw.println(" providers"); + pw.println(" The providers command is followed by a subcommand, as listed below:"); + pw.println(); pw.println(" add-test-provider <PROVIDER> [--requiresNetwork] [--requiresSatellite]"); pw.println(" [--requiresCell] [--hasMonetaryCost] [--supportsAltitude]"); pw.println(" [--supportsSpeed] [--supportsBearing]"); pw.println(" [--powerRequirement <POWER_REQUIREMENT>]"); + pw.println(" [--extraAttributionTags <TAG>,<TAG>,...]"); pw.println(" Add the given test provider. Requires MOCK_LOCATION permissions which"); pw.println(" can be enabled by running \"adb shell appops set <uid>"); pw.println(" android:mock_location allow\". There are optional flags that can be"); - pw.println(" used to configure the provider properties. If no flags are included,"); - pw.println(" then default values will be used."); + pw.println(" used to configure the provider properties and additional arguments. If"); + pw.println(" no flags are included, then default values will be used."); pw.println(" remove-test-provider <PROVIDER>"); pw.println(" Remove the given test provider."); pw.println(" set-test-provider-enabled <PROVIDER> true|false"); pw.println(" Sets the given test provider enabled state."); - pw.println(" set-test-provider-location <PROVIDER> [--location <LATITUDE>,<LONGITUDE>]"); + pw.println(" set-test-provider-location <PROVIDER> --location <LATITUDE>,<LONGITUDE>"); pw.println(" [--accuracy <ACCURACY>] [--time <TIME>]"); pw.println(" Set location for given test provider. Accuracy and time are optional."); pw.println(" send-extra-command <PROVIDER> <COMMAND>"); diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java index 6249a068f591..e1c011d821a7 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java @@ -46,6 +46,7 @@ import android.util.proto.ProtoOutputStream; import com.android.server.location.ClientBrokerProto; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -101,6 +102,11 @@ public class ContextHubClientBroker extends IContextHubClient.Stub private static final String TAG = "ContextHubClientBroker"; /** + * Internal only authorization value used when the auth state is unknown. + */ + private static final int AUTHORIZATION_UNKNOWN = -1; + + /** * Message used by noteOp when this client receives a message from a nanoapp. */ private static final String RECEIVE_MSG_NOTE = "NanoappMessageDelivery "; @@ -227,7 +233,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub if (mMessageChannelNanoappIdMap.containsKey(state.getNanoAppId())) { List<String> permissions = state.getNanoAppPermissions(); updateNanoAppAuthState(state.getNanoAppId(), - hasPermissions(permissions), false /* gracePeriodExpired */); + permissions, false /* gracePeriodExpired */); } } } @@ -344,32 +350,13 @@ public class ContextHubClientBroker extends IContextHubClient.Stub public int sendMessageToNanoApp(NanoAppMessage message) { ContextHubServiceUtil.checkPermissions(mContext); - int authState; - synchronized (mMessageChannelNanoappIdMap) { - // Default to the granted auth state. The true auth state will be checked async if it's - // not denied. - authState = mMessageChannelNanoappIdMap.getOrDefault( - message.getNanoAppId(), AUTHORIZATION_GRANTED); - if (authState == AUTHORIZATION_DENIED) { - return ContextHubTransaction.RESULT_FAILED_PERMISSION_DENIED; - } - } - int result; if (isRegistered()) { - // Even though the auth state is currently not denied, query the nanoapp permissions - // async and verify that the host app currently holds all the requisite permissions. - // This can't be done synchronously due to the async query that needs to be performed to - // obtain the nanoapp permissions. - boolean initialNanoappMessage = false; - synchronized (mMessageChannelNanoappIdMap) { - if (mMessageChannelNanoappIdMap.get(message.getNanoAppId()) == null) { - mMessageChannelNanoappIdMap.put(message.getNanoAppId(), AUTHORIZATION_GRANTED); - initialNanoappMessage = true; - } - } - - if (initialNanoappMessage) { + int authState = mMessageChannelNanoappIdMap.getOrDefault( + message.getNanoAppId(), AUTHORIZATION_UNKNOWN); + if (authState == AUTHORIZATION_DENIED) { + return ContextHubTransaction.RESULT_FAILED_PERMISSION_DENIED; + } else if (authState == AUTHORIZATION_UNKNOWN) { // Only check permissions the first time a nanoapp is queried since nanoapp // permissions don't currently change at runtime. If the host permission changes // later, that'll be checked by onOpChanged. @@ -472,7 +459,8 @@ public class ContextHubClientBroker extends IContextHubClient.Stub List<String> messagePermissions) { long nanoAppId = message.getNanoAppId(); - int authState = mMessageChannelNanoappIdMap.getOrDefault(nanoAppId, AUTHORIZATION_GRANTED); + int authState = updateNanoAppAuthState(nanoAppId, nanoappPermissions, + false /* gracePeriodExpired */); // If in the grace period, the host may not receive any messages containing permissions // covered data. @@ -482,7 +470,9 @@ public class ContextHubClientBroker extends IContextHubClient.Stub return; } - if (authState == AUTHORIZATION_DENIED || !hasPermissions(nanoappPermissions) + // If in the grace period, don't check permissions state since it'll cause cleanup + // messages to be dropped. + if (authState == AUTHORIZATION_DENIED || !notePermissions(messagePermissions, RECEIVE_MSG_NOTE + nanoAppId)) { Log.e(TAG, "Dropping message from " + Long.toHexString(nanoAppId) + ". " + mPackage + " doesn't have permission"); @@ -629,7 +619,8 @@ public class ContextHubClientBroker extends IContextHubClient.Stub if (timer != null) { updateNanoAppAuthState( - nanoAppId, false /* hasPermissions */, true /* gracePeriodExpired */); + nanoAppId, Collections.emptyList() /* nanoappPermissions */, + true /* gracePeriodExpired */); } } @@ -643,24 +634,43 @@ public class ContextHubClientBroker extends IContextHubClient.Stub mTransactionManager.addTransaction(transaction); } - /** - * Updates the latest authentication state for this client to be able to communicate with the - * given nanoapp. - */ - private void updateNanoAppAuthState( - long nanoAppId, boolean hasPermissions, boolean gracePeriodExpired) { - updateNanoAppAuthState( - nanoAppId, hasPermissions, gracePeriodExpired, false /* forceDenied */); + private int updateNanoAppAuthState( + long nanoAppId, List<String> nanoappPermissions, boolean gracePeriodExpired) { + return updateNanoAppAuthState( + nanoAppId, nanoappPermissions, gracePeriodExpired, false /* forceDenied */); } - /* package */ void updateNanoAppAuthState( - long nanoAppId, boolean hasPermissions, boolean gracePeriodExpired, + /** + * Updates the latest authenticatication state for the given nanoapp. + * + * @param nanoAppId the nanoapp that's auth state is being updated + * @param nanoappPermissions the Android permissions required to communicate with the nanoapp + * @param gracePeriodExpired indicates whether this invocation is a result of the grace period + * expiring + * @param forceDenied indicates that no matter what auth state is asssociated with this nanoapp + * it should transition to denied + * @return the latest auth state as of the completion of this method. + */ + /* package */ int updateNanoAppAuthState( + long nanoAppId, List<String> nanoappPermissions, boolean gracePeriodExpired, boolean forceDenied) { int curAuthState; int newAuthState; synchronized (mMessageChannelNanoappIdMap) { + // Check permission granted state synchronously since this method can be invoked from + // multiple threads. + boolean hasPermissions = hasPermissions(nanoappPermissions); + curAuthState = mMessageChannelNanoappIdMap.getOrDefault( - nanoAppId, AUTHORIZATION_GRANTED); + nanoAppId, AUTHORIZATION_UNKNOWN); + if (curAuthState == AUTHORIZATION_UNKNOWN) { + // If there's never been an auth check performed, start the state as granted so the + // appropriate state transitions occur below and clients don't receive a granted + // callback if they're determined to be in the granted state initially. + curAuthState = AUTHORIZATION_GRANTED; + mMessageChannelNanoappIdMap.put(nanoAppId, AUTHORIZATION_GRANTED); + } + newAuthState = curAuthState; // The below logic ensures that only the following transitions are possible: // GRANTED -> DENIED_GRACE_PERIOD only if permissions have been lost @@ -701,6 +711,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub // Don't send the callback in the synchronized block or it could end up in a deadlock. sendAuthStateCallback(nanoAppId, newAuthState); } + return newAuthState; } private void sendAuthStateCallback(long nanoAppId, int authState) { diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index 0737db7b8358..dde45c4ab52a 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -124,9 +124,6 @@ public class ContextHubService extends IContextHubService.Stub { // True if WiFi is available for the Context Hub private boolean mIsWifiAvailable = false; - // True if audio is disabled for the ContextHub - private boolean mIsAudioDisabled = false; - // Lock object for sendWifiSettingUpdate() private final Object mSendWifiSettingUpdateLock = new Object(); @@ -298,7 +295,7 @@ public class ContextHubService extends IContextHubService.Stub { }; SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext); manager.addSensorPrivacyListener( - SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE, listener); + SensorPrivacyManager.Sensors.MICROPHONE, listener); } } @@ -945,8 +942,8 @@ public class ContextHubService extends IContextHubService.Stub { mClientManager.forEachClientOfHub(contextHubId, client -> { if (client.getPackageName().equals(packageName)) { client.updateNanoAppAuthState( - nanoAppId, false /* hasPermissions */, false /* gracePeriodExpired */, - true /* forceDenied */); + nanoAppId, Collections.emptyList() /* nanoappPermissions */, + false /* gracePeriodExpired */, true /* forceDenied */); } }); } @@ -1082,12 +1079,10 @@ public class ContextHubService extends IContextHubService.Stub { */ private void sendMicrophoneDisableSettingUpdate() { SensorPrivacyManager manager = SensorPrivacyManager.getInstance(mContext); - boolean disabled = manager.isIndividualSensorPrivacyEnabled( - SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE); - if (mIsAudioDisabled != disabled) { - mIsAudioDisabled = disabled; - mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled); - } + boolean disabled = manager.isSensorPrivacyEnabled( + SensorPrivacyManager.Sensors.MICROPHONE); + Log.d(TAG, "Mic Disabled Setting: " + disabled); + mContextHubWrapper.onMicrophoneDisableSettingChanged(disabled); } diff --git a/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java b/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java index af3907e78432..dda502b2b9aa 100644 --- a/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java +++ b/services/core/java/com/android/server/location/countrydetector/ComprehensiveCountryDetector.java @@ -320,13 +320,15 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase { "(source: " + detectedCountry.getSource() + ", countryISO: " + detectedCountry.getCountryIso() + ")") + " isAirplaneModeOff()=" + isAirplaneModeOff() + + " isWifiOn()=" + isWifiOn() + " mListener=" + mListener + " isGeoCoderImplemnted()=" + isGeoCoderImplemented()); } if (startLocationBasedDetection && (detectedCountry == null || detectedCountry.getSource() > Country.COUNTRY_SOURCE_LOCATION) - && isAirplaneModeOff() && mListener != null && isGeoCoderImplemented()) { + && (isAirplaneModeOff() || isWifiOn()) && mListener != null + && isGeoCoderImplemented()) { if (DEBUG) Slog.d(TAG, "run startLocationBasedDetector()"); // Start finding location when the source is less reliable than the // location and the airplane mode is off (as geocoder will not @@ -387,6 +389,11 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase { mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 0; } + protected boolean isWifiOn() { + return Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.WIFI_ON, 0) != 0; + } + /** * Notify the country change. */ diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java index dbfd0a52b013..29ce3783c4a1 100644 --- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java +++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java @@ -64,16 +64,17 @@ public class LocationEventLog extends LocalEventLog { private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 10; @GuardedBy("mAggregateStats") - private final ArrayMap<String, ArrayMap<String, AggregateStats>> mAggregateStats; + private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats; public LocationEventLog() { super(getLogSize()); mAggregateStats = new ArrayMap<>(4); } - public ArrayMap<String, ArrayMap<String, AggregateStats>> copyAggregateStats() { + /** Copies out all aggregated stats. */ + public ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> copyAggregateStats() { synchronized (mAggregateStats) { - ArrayMap<String, ArrayMap<String, AggregateStats>> copy = new ArrayMap<>( + ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> copy = new ArrayMap<>( mAggregateStats); for (int i = 0; i < copy.size(); i++) { copy.setValueAt(i, new ArrayMap<>(copy.valueAt(i))); @@ -82,17 +83,18 @@ public class LocationEventLog extends LocalEventLog { } } - private AggregateStats getAggregateStats(String provider, String packageName) { + private AggregateStats getAggregateStats(String provider, CallerIdentity identity) { synchronized (mAggregateStats) { - ArrayMap<String, AggregateStats> packageMap = mAggregateStats.get(provider); + ArrayMap<CallerIdentity, AggregateStats> packageMap = mAggregateStats.get(provider); if (packageMap == null) { packageMap = new ArrayMap<>(2); mAggregateStats.put(provider, packageMap); } - AggregateStats stats = packageMap.get(packageName); + CallerIdentity stripped = identity.stripListenerId(); + AggregateStats stats = packageMap.get(stripped); if (stats == null) { stats = new AggregateStats(); - packageMap.put(packageName, stats); + packageMap.put(stripped, stats); } return stats; } @@ -117,34 +119,33 @@ public class LocationEventLog extends LocalEventLog { public void logProviderClientRegistered(String provider, CallerIdentity identity, LocationRequest request) { addLogEvent(EVENT_PROVIDER_REGISTER_CLIENT, provider, identity, request); - getAggregateStats(provider, identity.getPackageName()) - .markRequestAdded(request.getIntervalMillis()); + getAggregateStats(provider, identity).markRequestAdded(request.getIntervalMillis()); } /** Logs a client unregistration for a location provider. */ public void logProviderClientUnregistered(String provider, CallerIdentity identity) { addLogEvent(EVENT_PROVIDER_UNREGISTER_CLIENT, provider, identity); - getAggregateStats(provider, identity.getPackageName()).markRequestRemoved(); + getAggregateStats(provider, identity).markRequestRemoved(); } /** Logs a client for a location provider entering the active state. */ public void logProviderClientActive(String provider, CallerIdentity identity) { - getAggregateStats(provider, identity.getPackageName()).markRequestActive(); + getAggregateStats(provider, identity).markRequestActive(); } /** Logs a client for a location provider leaving the active state. */ public void logProviderClientInactive(String provider, CallerIdentity identity) { - getAggregateStats(provider, identity.getPackageName()).markRequestInactive(); + getAggregateStats(provider, identity).markRequestInactive(); } /** Logs a client for a location provider entering the foreground state. */ public void logProviderClientForeground(String provider, CallerIdentity identity) { - getAggregateStats(provider, identity.getPackageName()).markRequestForeground(); + getAggregateStats(provider, identity).markRequestForeground(); } /** Logs a client for a location provider leaving the foreground state. */ public void logProviderClientBackground(String provider, CallerIdentity identity) { - getAggregateStats(provider, identity.getPackageName()).markRequestBackground(); + getAggregateStats(provider, identity).markRequestBackground(); } /** Logs a change to the provider request for a location provider. */ @@ -165,7 +166,7 @@ public class LocationEventLog extends LocalEventLog { if (Build.IS_DEBUGGABLE || D) { addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity); } - getAggregateStats(provider, identity.getPackageName()).markLocationDelivered(); + getAggregateStats(provider, identity).markLocationDelivered(); } /** Logs that a provider has entered or exited stationary throttling. */ @@ -218,7 +219,7 @@ public class LocationEventLog extends LocalEventLog { protected final String mProvider; - protected ProviderEvent(long timeDelta, String provider) { + ProviderEvent(long timeDelta, String provider) { super(timeDelta); mProvider = provider; } @@ -234,7 +235,7 @@ public class LocationEventLog extends LocalEventLog { private final int mUserId; private final boolean mEnabled; - protected ProviderEnabledEvent(long timeDelta, String provider, int userId, + ProviderEnabledEvent(long timeDelta, String provider, int userId, boolean enabled) { super(timeDelta, provider); mUserId = userId; @@ -252,7 +253,7 @@ public class LocationEventLog extends LocalEventLog { private final boolean mMocked; - protected ProviderMockedEvent(long timeDelta, String provider, boolean mocked) { + ProviderMockedEvent(long timeDelta, String provider, boolean mocked) { super(timeDelta, provider); mMocked = mocked; } @@ -273,7 +274,7 @@ public class LocationEventLog extends LocalEventLog { private final CallerIdentity mIdentity; @Nullable private final LocationRequest mLocationRequest; - private ProviderRegisterEvent(long timeDelta, String provider, boolean registered, + ProviderRegisterEvent(long timeDelta, String provider, boolean registered, CallerIdentity identity, @Nullable LocationRequest locationRequest) { super(timeDelta, provider); mRegistered = registered; @@ -296,7 +297,7 @@ public class LocationEventLog extends LocalEventLog { private final ProviderRequest mRequest; - private ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) { + ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) { super(timeDelta, provider); mRequest = request; } @@ -311,7 +312,7 @@ public class LocationEventLog extends LocalEventLog { private final int mNumLocations; - private ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) { + ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) { super(timeDelta, provider); mNumLocations = numLocations; } @@ -327,7 +328,7 @@ public class LocationEventLog extends LocalEventLog { private final int mNumLocations; @Nullable private final CallerIdentity mIdentity; - private ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations, + ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations, @Nullable CallerIdentity identity) { super(timeDelta, provider); mNumLocations = numLocations; @@ -345,7 +346,7 @@ public class LocationEventLog extends LocalEventLog { private final boolean mStationaryThrottled; - private ProviderStationaryThrottledEvent(long timeDelta, String provider, + ProviderStationaryThrottledEvent(long timeDelta, String provider, boolean stationaryThrottled) { super(timeDelta, provider); mStationaryThrottled = stationaryThrottled; @@ -363,7 +364,7 @@ public class LocationEventLog extends LocalEventLog { @LocationPowerSaveMode private final int mLocationPowerSaveMode; - private LocationPowerSaveModeEvent(long timeDelta, + LocationPowerSaveModeEvent(long timeDelta, @LocationPowerSaveMode int locationPowerSaveMode) { super(timeDelta); mLocationPowerSaveMode = locationPowerSaveMode; @@ -401,7 +402,7 @@ public class LocationEventLog extends LocalEventLog { private final int mUserId; private final boolean mEnabled; - private LocationEnabledEvent(long timeDelta, int userId, boolean enabled) { + LocationEnabledEvent(long timeDelta, int userId, boolean enabled) { super(timeDelta); mUserId = userId; mEnabled = enabled; diff --git a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java new file mode 100644 index 000000000000..1967e026e46c --- /dev/null +++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java @@ -0,0 +1,144 @@ +/* + * 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.location.gnss; + +import static com.android.server.location.gnss.GnssManagerService.TAG; + +import android.annotation.Nullable; +import android.location.GnssAntennaInfo; +import android.location.IGnssAntennaInfoListener; +import android.location.util.identity.CallerIdentity; +import android.os.Binder; +import android.os.IBinder; + +import com.android.server.location.gnss.hal.GnssNative; +import com.android.server.location.listeners.BinderListenerRegistration; +import com.android.server.location.listeners.ListenerMultiplexer; +import com.android.server.location.listeners.ListenerRegistration; + +import java.util.Collection; +import java.util.List; + +/** + * Antenna info HAL module and listener multiplexer. + */ +public class GnssAntennaInfoProvider extends + ListenerMultiplexer<IBinder, IGnssAntennaInfoListener, + ListenerRegistration<IGnssAntennaInfoListener>, Void> implements + GnssNative.BaseCallbacks, GnssNative.AntennaInfoCallbacks { + + /** + * Registration object for GNSS listeners. + */ + protected class AntennaInfoListenerRegistration extends + BinderListenerRegistration<Void, IGnssAntennaInfoListener> { + + protected AntennaInfoListenerRegistration(CallerIdentity callerIdentity, + IGnssAntennaInfoListener listener) { + super(null, callerIdentity, listener); + } + + @Override + protected GnssAntennaInfoProvider getOwner() { + return GnssAntennaInfoProvider.this; + } + } + + private final GnssNative mGnssNative; + + private volatile @Nullable List<GnssAntennaInfo> mAntennaInfos; + + GnssAntennaInfoProvider(GnssNative gnssNative) { + mGnssNative = gnssNative; + mGnssNative.addBaseCallbacks(this); + mGnssNative.addAntennaInfoCallbacks(this); + } + + @Nullable List<GnssAntennaInfo> getAntennaInfos() { + return mAntennaInfos; + } + + @Override + public String getTag() { + return TAG; + } + + public boolean isSupported() { + return mGnssNative.isAntennaInfoSupported(); + } + + public void addListener(CallerIdentity callerIdentity, IGnssAntennaInfoListener listener) { + long identity = Binder.clearCallingIdentity(); + try { + putRegistration(listener.asBinder(), + new AntennaInfoListenerRegistration(callerIdentity, listener)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + public void removeListener(IGnssAntennaInfoListener listener) { + long identity = Binder.clearCallingIdentity(); + try { + removeRegistration(listener.asBinder()); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + protected boolean registerWithService(Void merged, + Collection<ListenerRegistration<IGnssAntennaInfoListener>> listenerRegistrations) { + return true; + } + + @Override + protected void unregisterWithService() {} + + @Override + protected boolean isActive(ListenerRegistration<IGnssAntennaInfoListener> registration) { + return true; + } + + @Override + protected Void mergeRegistrations( + Collection<ListenerRegistration<IGnssAntennaInfoListener>> listenerRegistrations) { + return null; + } + + @Override + public void onHalStarted() { + mGnssNative.startAntennaInfoListening(); + } + + @Override + public void onHalRestarted() { + mGnssNative.startAntennaInfoListening(); + } + + @Override + public void onReportAntennaInfo(List<GnssAntennaInfo> antennaInfos) { + if (antennaInfos.equals(mAntennaInfos)) { + return; + } + + mAntennaInfos = antennaInfos; + deliverToListeners(listener -> { + listener.onGnssAntennaInfoChanged(antennaInfos); + }); + } +} diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java index 87e6ef4110ee..5e6ae68c02f2 100644 --- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java @@ -229,7 +229,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter * registrations will be treated as inactive and the backing service will never be registered. * */ - protected boolean isServiceSupported() { + public boolean isSupported() { return true; } @@ -276,7 +276,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter @Override protected boolean isActive(GnssListenerRegistration registration) { - if (!isServiceSupported()) { + if (!isSupported()) { return false; } @@ -339,7 +339,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter @Override protected void onRegister() { - if (!isServiceSupported()) { + if (!isSupported()) { return; } @@ -356,7 +356,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter @Override protected void onUnregister() { - if (!isServiceSupported()) { + if (!isSupported()) { return; } @@ -404,7 +404,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter @Override protected String getServiceState() { - if (!isServiceSupported()) { + if (!isSupported()) { return "unsupported"; } else { return super.getServiceState(); diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index 9216a6b245a6..1df29ab5791f 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -103,6 +103,7 @@ import com.android.server.location.provider.AbstractLocationProvider; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -371,7 +372,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements public GnssLocationProvider(Context context, Injector injector, GnssNative gnssNative, GnssMetrics gnssMetrics) { - super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES); + super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES, + Collections.emptySet()); mContext = context; mGnssNative = gnssNative; diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java index b6695c20bd97..bcdfed45bd9c 100644 --- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java +++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java @@ -27,6 +27,7 @@ import android.location.GnssAntennaInfo; import android.location.GnssCapabilities; import android.location.GnssMeasurementCorrections; import android.location.GnssMeasurementRequest; +import android.location.IGnssAntennaInfoListener; import android.location.IGnssMeasurementsListener; import android.location.IGnssNavigationMessageListener; import android.location.IGnssNmeaListener; @@ -49,7 +50,6 @@ import com.android.server.location.gnss.hal.GnssNative; import com.android.server.location.injector.Injector; import java.io.FileDescriptor; -import java.util.ArrayList; import java.util.List; /** Manages Gnss providers and related Gnss functions for LocationManagerService. */ @@ -60,7 +60,7 @@ public class GnssManagerService { private static final String ATTRIBUTION_ID = "GnssService"; - private final Context mContext; + final Context mContext; private final GnssNative mGnssNative; private final GnssLocationProvider mGnssLocationProvider; @@ -68,11 +68,11 @@ public class GnssManagerService { private final GnssNmeaProvider mGnssNmeaProvider; private final GnssMeasurementsProvider mGnssMeasurementsProvider; private final GnssNavigationMessageProvider mGnssNavigationMessageProvider; + private final GnssAntennaInfoProvider mGnssAntennaInfoProvider; private final IGpsGeofenceHardware mGnssGeofenceProxy; private final GnssGeofenceHalModule mGeofenceHalModule; private final GnssCapabilitiesHalModule mCapabilitiesHalModule; - private final GnssAntennaInfoHalModule mAntennaInfoHalModule; private final GnssMetrics mGnssMetrics; @@ -89,11 +89,11 @@ public class GnssManagerService { mGnssNmeaProvider = new GnssNmeaProvider(injector, mGnssNative); mGnssMeasurementsProvider = new GnssMeasurementsProvider(injector, mGnssNative); mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(injector, mGnssNative); + mGnssAntennaInfoProvider = new GnssAntennaInfoProvider(mGnssNative); mGnssGeofenceProxy = new GnssGeofenceProxy(mGnssNative); mGeofenceHalModule = new GnssGeofenceHalModule(mGnssNative); mCapabilitiesHalModule = new GnssCapabilitiesHalModule(mGnssNative); - mAntennaInfoHalModule = new GnssAntennaInfoHalModule(mGnssNative); // allow gnss access to begin - we must assume that callbacks can start immediately mGnssNative.register(); @@ -140,7 +140,7 @@ public class GnssManagerService { * Get GNSS antenna information. */ public @Nullable List<GnssAntennaInfo> getGnssAntennaInfos() { - return mAntennaInfoHalModule.getAntennaInfos(); + return mGnssAntennaInfoProvider.getAntennaInfos(); } /** @@ -154,10 +154,11 @@ public class GnssManagerService { * Registers listener for GNSS status changes. */ public void registerGnssStatusCallback(IGnssStatusListener listener, String packageName, - @Nullable String attributionTag) { + @Nullable String attributionTag, String listenerId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, + listenerId); mGnssStatusProvider.addListener(identity, listener); } @@ -172,10 +173,11 @@ public class GnssManagerService { * Registers listener for GNSS NMEA messages. */ public void registerGnssNmeaCallback(IGnssNmeaListener listener, String packageName, - @Nullable String attributionTag) { + @Nullable String attributionTag, String listenerId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, + listenerId); mGnssNmeaProvider.addListener(identity, listener); } @@ -191,12 +193,13 @@ public class GnssManagerService { */ public void addGnssMeasurementsListener(GnssMeasurementRequest request, IGnssMeasurementsListener listener, String packageName, - @Nullable String attributionTag) { + @Nullable String attributionTag, String listenerId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); if (request.isCorrelationVectorOutputsEnabled()) { mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null); } - CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, + listenerId); mGnssMeasurementsProvider.addListener(request, identity, listener); } @@ -223,10 +226,11 @@ public class GnssManagerService { * Adds a GNSS navigation message listener. */ public void addGnssNavigationMessageListener(IGnssNavigationMessageListener listener, - String packageName, @Nullable String attributionTag) { + String packageName, @Nullable String attributionTag, String listenerId) { mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null); - CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag); + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, + listenerId); mGnssNavigationMessageProvider.addListener(identity, listener); } @@ -238,6 +242,24 @@ public class GnssManagerService { } /** + * Adds a GNSS antenna info listener. + */ + public void addGnssAntennaInfoListener(IGnssAntennaInfoListener listener, String packageName, + @Nullable String attributionTag, String listenerId) { + + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, + listenerId); + mGnssAntennaInfoProvider.addListener(identity, listener); + } + + /** + * Removes a GNSS antenna info listener. + */ + public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) { + mGnssAntennaInfoProvider.removeListener(listener); + } + + /** * Send Ni Response, indicating a location request initiated by a network carrier. */ public void sendNiResponse(int notifId, int userResponse) { @@ -259,25 +281,34 @@ public class GnssManagerService { ipw.println("Capabilities: " + mGnssNative.getCapabilities()); - List<GnssAntennaInfo> infos = mAntennaInfoHalModule.getAntennaInfos(); - if (infos != null) { - ipw.println("Antenna Infos: " + infos); + if (mGnssStatusProvider.isSupported()) { + ipw.println("Status Provider:"); + ipw.increaseIndent(); + mGnssStatusProvider.dump(fd, ipw, args); + ipw.decreaseIndent(); } - ipw.println("Measurements Provider:"); - ipw.increaseIndent(); - mGnssMeasurementsProvider.dump(fd, ipw, args); - ipw.decreaseIndent(); + if (mGnssMeasurementsProvider.isSupported()) { + ipw.println("Measurements Provider:"); + ipw.increaseIndent(); + mGnssMeasurementsProvider.dump(fd, ipw, args); + ipw.decreaseIndent(); + } - ipw.println("Navigation Message Provider:"); - ipw.increaseIndent(); - mGnssNavigationMessageProvider.dump(fd, ipw, args); - ipw.decreaseIndent(); + if (mGnssNavigationMessageProvider.isSupported()) { + ipw.println("Navigation Message Provider:"); + ipw.increaseIndent(); + mGnssNavigationMessageProvider.dump(fd, ipw, args); + ipw.decreaseIndent(); + } - ipw.println("Status Provider:"); - ipw.increaseIndent(); - mGnssStatusProvider.dump(fd, ipw, args); - ipw.decreaseIndent(); + if (mGnssAntennaInfoProvider.isSupported()) { + ipw.println("Navigation Message Provider:"); + ipw.increaseIndent(); + ipw.println("Antenna Infos: " + mGnssAntennaInfoProvider.getAntennaInfos()); + mGnssAntennaInfoProvider.dump(fd, ipw, args); + ipw.decreaseIndent(); + } GnssPowerStats powerStats = mGnssNative.getPowerStats(); if (powerStats != null) { @@ -395,53 +426,4 @@ public class GnssManagerService { } } } - - private class GnssAntennaInfoHalModule implements GnssNative.BaseCallbacks, - GnssNative.AntennaInfoCallbacks { - - private final GnssNative mGnssNative; - - private volatile @Nullable List<GnssAntennaInfo> mAntennaInfos; - - GnssAntennaInfoHalModule(GnssNative gnssNative) { - mGnssNative = gnssNative; - mGnssNative.addBaseCallbacks(this); - mGnssNative.addAntennaInfoCallbacks(this); - } - - @Nullable List<GnssAntennaInfo> getAntennaInfos() { - return mAntennaInfos; - } - - @Override - public void onHalStarted() { - mGnssNative.startAntennaInfoListening(); - } - - @Override - public void onHalRestarted() { - mGnssNative.startAntennaInfoListening(); - } - - @Override - public void onReportAntennaInfo(List<GnssAntennaInfo> antennaInfos) { - if (antennaInfos.equals(mAntennaInfos)) { - return; - } - - mAntennaInfos = antennaInfos; - - long ident = Binder.clearCallingIdentity(); - try { - Intent intent = new Intent(LocationManager.ACTION_GNSS_ANTENNA_INFOS_CHANGED) - .putParcelableArrayListExtra(LocationManager.EXTRA_GNSS_ANTENNA_INFOS, - new ArrayList<>(antennaInfos)) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } } diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java index 305bc9b69e39..b3119d7aa53e 100644 --- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java @@ -99,7 +99,7 @@ public final class GnssMeasurementsProvider extends } @Override - protected boolean isServiceSupported() { + public boolean isSupported() { return mGnssNative.isMeasurementSupported(); } diff --git a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java index ff9ad65b4ca5..e9fce0514a18 100644 --- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java @@ -70,7 +70,7 @@ public class GnssNavigationMessageProvider extends } @Override - protected boolean isServiceSupported() { + public boolean isSupported() { return mGnssNative.isNavigationMessageCollectionSupported(); } diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java index 7e2f089d32c9..f275663a1309 100644 --- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java +++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java @@ -699,11 +699,11 @@ public class GnssNative { } /** - * Returns true if antenna info listening is supported. + * Returns true if antenna info is supported. */ - public boolean isAntennaInfoListeningSupported() { + public boolean isAntennaInfoSupported() { Preconditions.checkState(mRegistered); - return mGnssHal.isAntennaInfoListeningSupported(); + return mGnssHal.isAntennaInfoSupported(); } /** @@ -1259,7 +1259,7 @@ public class GnssNative { return native_stop_navigation_message_collection(); } - protected boolean isAntennaInfoListeningSupported() { + protected boolean isAntennaInfoSupported() { return native_is_antenna_info_supported(); } diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java index 4e0a0b89f238..ba7f44f2818b 100644 --- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java @@ -28,7 +28,9 @@ import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Collections; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; import java.util.function.UnaryOperator; @@ -65,9 +67,10 @@ public abstract class AbstractLocationProvider { /** * Default state value for a location provider that is disabled with no properties and an - * empty provider package list. + * empty extra attribution tag set. */ - public static final State EMPTY_STATE = new State(false, null, null); + public static final State EMPTY_STATE = new State(false, null, null, + Collections.emptySet()); /** * The provider's allowed state. @@ -84,10 +87,18 @@ public abstract class AbstractLocationProvider { */ @Nullable public final CallerIdentity identity; - private State(boolean allowed, ProviderProperties properties, CallerIdentity identity) { + /** + * A set of attribution tags also associated with this provider - these attribution tags may + * be afforded special privileges. + */ + public final Set<String> extraAttributionTags; + + private State(boolean allowed, ProviderProperties properties, CallerIdentity identity, + Set<String> extraAttributionTags) { this.allowed = allowed; this.properties = properties; this.identity = identity; + this.extraAttributionTags = Objects.requireNonNull(extraAttributionTags); } /** @@ -97,7 +108,7 @@ public abstract class AbstractLocationProvider { if (allowed == this.allowed) { return this; } else { - return new State(allowed, properties, identity); + return new State(allowed, properties, identity, extraAttributionTags); } } @@ -108,7 +119,7 @@ public abstract class AbstractLocationProvider { if (Objects.equals(properties, this.properties)) { return this; } else { - return new State(allowed, properties, identity); + return new State(allowed, properties, identity, extraAttributionTags); } } @@ -119,10 +130,22 @@ public abstract class AbstractLocationProvider { if (Objects.equals(identity, this.identity)) { return this; } else { - return new State(allowed, properties, identity); + return new State(allowed, properties, identity, extraAttributionTags); + } + } + + /** + * Returns a state the same as the current but with extra attribution tags set as specified. + */ + public State withExtraAttributionTags(Set<String> extraAttributionTags) { + if (extraAttributionTags.equals(this.extraAttributionTags)) { + return this; + } else { + return new State(allowed, properties, identity, extraAttributionTags); } } + @Override public boolean equals(Object o) { if (this == o) { @@ -133,12 +156,13 @@ public abstract class AbstractLocationProvider { } State state = (State) o; return allowed == state.allowed && properties == state.properties - && Objects.equals(identity, state.identity); + && Objects.equals(identity, state.identity) + && extraAttributionTags.equals(state.extraAttributionTags); } @Override public int hashCode() { - return Objects.hash(allowed, properties, identity); + return Objects.hash(allowed, properties, identity, extraAttributionTags); } } @@ -195,10 +219,14 @@ public abstract class AbstractLocationProvider { * An optional identity and properties may be provided to initialize the location provider. */ protected AbstractLocationProvider(Executor executor, @Nullable CallerIdentity identity, - @Nullable ProviderProperties properties) { - mExecutor = executor; + @Nullable ProviderProperties properties, Set<String> extraAttributionTags) { + Preconditions.checkArgument(identity == null || identity.getListenerId() == null); + mExecutor = Objects.requireNonNull(executor); mInternalState = new AtomicReference<>(new InternalState(null, - State.EMPTY_STATE.withIdentity(identity).withProperties(properties))); + State.EMPTY_STATE + .withIdentity(identity) + .withProperties(properties) + .withExtraAttributionTags(extraAttributionTags))); mController = new Controller(); } @@ -270,13 +298,21 @@ public abstract class AbstractLocationProvider { } /** - * Call this method to report a change in provider packages. + * Call this method to report a change in the provider's identity. */ protected void setIdentity(@Nullable CallerIdentity identity) { + Preconditions.checkArgument(identity == null || identity.getListenerId() == null); setState(state -> state.withIdentity(identity)); } /** + * Call this method to report a change in the provider's extra attribution tags. + */ + protected void setExtraAttributionTags(Set<String> extraAttributionTags) { + setState(state -> state.withExtraAttributionTags(extraAttributionTags)); + } + + /** * Call this method to report a new location. */ protected void reportLocation(LocationResult locationResult) { @@ -335,6 +371,8 @@ public abstract class AbstractLocationProvider { private boolean mStarted = false; + Controller() {} + @Override public State setListener(@Nullable Listener listener) { InternalState oldInternalState = mInternalState.getAndUpdate( diff --git a/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java b/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java index a3ec867220dd..423f833bf7e0 100644 --- a/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java @@ -24,6 +24,7 @@ import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Collections; import java.util.concurrent.Executor; /** @@ -40,7 +41,7 @@ class DelegateLocationProvider extends AbstractLocationProvider private boolean mInitialized = false; DelegateLocationProvider(Executor executor, AbstractLocationProvider delegate) { - super(executor, null, null); + super(executor, null, null, Collections.emptySet()); mDelegate = delegate; } 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 388b5a4dd54e..fef30f9ea811 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -52,6 +52,8 @@ import android.location.LastLocationRequest; import android.location.Location; import android.location.LocationManager; import android.location.LocationManagerInternal; +import android.location.LocationManagerInternal.LocationTagInfo; +import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener; import android.location.LocationManagerInternal.ProviderEnabledListener; import android.location.LocationRequest; import android.location.LocationResult; @@ -85,6 +87,7 @@ import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.location.LocationPermissions; @@ -117,6 +120,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; @@ -304,6 +308,7 @@ public class LocationProviderManager extends LocationTransport transport, @PermissionLevel int permissionLevel) { super(Objects.requireNonNull(request), identity, transport); + Preconditions.checkArgument(identity.getListenerId() != null); Preconditions.checkArgument(permissionLevel > PERMISSION_NONE); Preconditions.checkArgument(!request.getWorkSource().isEmpty()); @@ -1287,6 +1292,9 @@ public class LocationProviderManager extends @GuardedBy("mLock") private @Nullable OnAlarmListener mDelayedRegister; + @GuardedBy("mLock") + private @Nullable OnProviderLocationTagsChangeListener mOnLocationTagsChangeListener; + public LocationProviderManager(Context context, Injector injector, LocationEventLog eventLog, String name, @Nullable PassiveLocationProviderManager passiveManager) { mContext = context; @@ -1447,6 +1455,19 @@ public class LocationProviderManager extends } } + /** + * Registers a listener for the location tags of the provider. + * + * @param listener The listener + */ + public void setOnProviderLocationTagsChangeListener( + @Nullable OnProviderLocationTagsChangeListener listener) { + Preconditions.checkArgument(mOnLocationTagsChangeListener == null || listener == null); + synchronized (mLock) { + mOnLocationTagsChangeListener = listener; + } + } + public void setMockProvider(@Nullable MockLocationProvider provider) { synchronized (mLock) { Preconditions.checkState(mState != STATE_STOPPED); @@ -2243,6 +2264,27 @@ public class LocationProviderManager extends if (oldState.allowed != newState.allowed) { onEnabledChanged(UserHandle.USER_ALL); } + + if (mOnLocationTagsChangeListener != null) { + if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)) { + if (oldState.identity != null) { + FgThread.getHandler().sendMessage(PooledLambda.obtainMessage( + OnProviderLocationTagsChangeListener::onLocationTagsChanged, + mOnLocationTagsChangeListener, new LocationTagInfo( + oldState.identity.getUid(), oldState.identity.getPackageName(), + Collections.emptySet()) + )); + } + if (newState.identity != null) { + FgThread.getHandler().sendMessage(PooledLambda.obtainMessage( + OnProviderLocationTagsChangeListener::onLocationTagsChanged, + mOnLocationTagsChangeListener, new LocationTagInfo( + newState.identity.getUid(), newState.identity.getPackageName(), + newState.extraAttributionTags) + )); + } + } + } } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/location/provider/MockLocationProvider.java b/services/core/java/com/android/server/location/provider/MockLocationProvider.java index 0d8f64377db0..52b04d4bab14 100644 --- a/services/core/java/com/android/server/location/provider/MockLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/MockLocationProvider.java @@ -28,6 +28,7 @@ import android.os.Bundle; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Set; /** * A mock location provider used by LocationManagerService to implement test providers. @@ -38,9 +39,10 @@ public class MockLocationProvider extends AbstractLocationProvider { @Nullable private Location mLocation; - public MockLocationProvider(ProviderProperties properties, CallerIdentity identity) { + public MockLocationProvider(ProviderProperties properties, CallerIdentity identity, + Set<String> extraAttributionTags) { // using a direct executor is ok because this class has no locks that could deadlock - super(DIRECT_EXECUTOR, identity, properties); + super(DIRECT_EXECUTOR, identity, properties, extraAttributionTags); } /** Sets the allowed state of this mock provider. */ diff --git a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java index cb7264e55fa9..81936440f6a7 100644 --- a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java @@ -31,6 +31,7 @@ import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Collections; /** * Represents a location provider that may switch between a mock implementation and a real @@ -75,7 +76,7 @@ public class MockableLocationProvider extends AbstractLocationProvider { public MockableLocationProvider(Object ownerLock) { // using a direct executor is acceptable because all inbound calls are delegated to the // actual provider implementations which will use their own executors - super(DIRECT_EXECUTOR, null, null); + super(DIRECT_EXECUTOR, null, null, Collections.emptySet()); mOwnerLock = ownerLock; mRequest = ProviderRequest.EMPTY_REQUEST; } diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java index a5758a37b983..68ede88c6a4a 100644 --- a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java @@ -30,6 +30,7 @@ import android.os.Bundle; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Collections; /** * A passive location provider reports locations received from other providers @@ -47,7 +48,8 @@ public class PassiveLocationProvider extends AbstractLocationProvider { public PassiveLocationProvider(Context context) { // using a direct executor is ok because this class has no locks that could deadlock - super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context), PROPERTIES); + super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context), PROPERTIES, + Collections.emptySet()); setAllowed(true); } diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java index 4c97f645aac9..44b62b3659dc 100644 --- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java +++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java @@ -32,15 +32,18 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; +import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; import com.android.server.ServiceWatcher; +import com.android.server.ServiceWatcher.BoundService; import com.android.server.location.provider.AbstractLocationProvider; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -49,6 +52,9 @@ import java.util.Objects; */ public class ProxyLocationProvider extends AbstractLocationProvider { + private static final String KEY_EXTRA_ATTRIBUTION_TAGS = "android:location_allow_listed_tags"; + private static final String EXTRA_ATTRIBUTION_TAGS_SEPARATOR = ";"; + /** * Creates and registers this proxy. If no suitable service is available for the proxy, returns * null. @@ -84,7 +90,7 @@ public class ProxyLocationProvider extends AbstractLocationProvider { int nonOverlayPackageResId) { // safe to use direct executor since our locks are not acquired in a code path invoked by // our owning provider - super(DIRECT_EXECUTOR, null, null); + super(DIRECT_EXECUTOR, null, null, Collections.emptySet()); mContext = context; mServiceWatcher = new ServiceWatcher(context, action, this::onBind, @@ -98,12 +104,22 @@ public class ProxyLocationProvider extends AbstractLocationProvider { return mServiceWatcher.checkServiceResolves(); } - private void onBind(IBinder binder, ComponentName service) throws RemoteException { + private void onBind(IBinder binder, BoundService boundService) throws RemoteException { ILocationProvider provider = ILocationProvider.Stub.asInterface(binder); synchronized (mLock) { mProxy = new Proxy(); - mService = service; + mService = boundService.component; + + // update extra attribution tag info from manifest + if (boundService.metadata != null) { + String tagsList = boundService.metadata.getString(KEY_EXTRA_ATTRIBUTION_TAGS); + if (tagsList != null) { + setExtraAttributionTags( + new ArraySet<>(tagsList.split(EXTRA_ATTRIBUTION_TAGS_SEPARATOR))); + } + } + provider.setLocationProviderManager(mProxy); ProviderRequest request = mRequest; diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 8da2d67d6691..43c736544ee6 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -88,6 +88,7 @@ import android.os.storage.StorageManager; import android.provider.Settings; import android.provider.Settings.Secure; import android.provider.Settings.SettingNotFoundException; +import android.security.AndroidKeyStoreMaintenance; import android.security.Authorization; import android.security.KeyStore; import android.security.keystore.AndroidKeyStoreProvider; @@ -224,7 +225,6 @@ public class LockSettingsService extends ILockSettings.Stub { private final SyntheticPasswordManager mSpManager; private final KeyStore mKeyStore; - private final RecoverableKeyStoreManager mRecoverableKeyStoreManager; private ManagedProfilePasswordCache mManagedProfilePasswordCache; @@ -479,8 +479,8 @@ public class LockSettingsService extends ILockSettings.Stub { return KeyStore.getInstance(); } - public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) { - return RecoverableKeyStoreManager.getInstance(mContext, keyStore); + public RecoverableKeyStoreManager getRecoverableKeyStoreManager() { + return RecoverableKeyStoreManager.getInstance(mContext); } public IStorageManager getStorageManager() { @@ -561,7 +561,7 @@ public class LockSettingsService extends ILockSettings.Stub { mInjector = injector; mContext = injector.getContext(); mKeyStore = injector.getKeyStore(); - mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager(mKeyStore); + mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager(); mHandler = injector.getHandler(injector.getServiceThread()); mStrongAuth = injector.getStrongAuth(); mActivityManager = injector.getActivityManager(); @@ -795,6 +795,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) { // Notify keystore that a new user was added. final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); + AndroidKeyStoreMaintenance.onUserAdded(userHandle); final KeyStore ks = KeyStore.getInstance(); final UserInfo parentInfo = mUserManager.getProfileParent(userHandle); final int parentHandle = parentInfo != null ? parentInfo.id : -1; @@ -1266,6 +1267,7 @@ public class LockSettingsService extends ILockSettings.Stub { } private void setKeystorePassword(byte[] password, int userHandle) { + AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password); final KeyStore ks = KeyStore.getInstance(); // TODO(b/120484642): Update keystore to accept byte[] passwords String passwordString = password == null ? null : new String(password); @@ -1274,7 +1276,7 @@ public class LockSettingsService extends ILockSettings.Stub { private void unlockKeystore(byte[] password, int userHandle) { if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle); - new Authorization().onLockScreenEvent(false, userHandle, password); + Authorization.onLockScreenEvent(false, userHandle, password); // TODO(b/120484642): Update keystore to accept byte[] passwords String passwordString = password == null ? null : new String(password); final KeyStore ks = KeyStore.getInstance(); @@ -2292,6 +2294,7 @@ public class LockSettingsService extends ILockSettings.Stub { mSpManager.removeUser(userId); mStrongAuth.removeUser(userId); + AndroidKeyStoreMaintenance.onUserRemoved(userId); final KeyStore ks = KeyStore.getInstance(); ks.onUserRemoved(userId); mManagedProfilePasswordCache.removePassword(userId); diff --git a/services/core/java/com/android/server/locksettings/OWNERS b/services/core/java/com/android/server/locksettings/OWNERS index 08b8a8c106b7..7577ee5c9fde 100644 --- a/services/core/java/com/android/server/locksettings/OWNERS +++ b/services/core/java/com/android/server/locksettings/OWNERS @@ -1,3 +1,4 @@ jaggies@google.com kchyn@google.com rubinxu@google.com +xunchang@google.com diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 30ea5556b41c..7e00fd69a148 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -194,7 +194,9 @@ class RebootEscrowManager { } public void reportMetric(boolean success) { - FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, success); + // TODO(b/179105110) design error code; and report the true value for other fields. + FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED, 0, 1, 1, + -1, 0); } public RebootEscrowEventLog getEventLog() { diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java index b3b45460899d..697bf08a232e 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java @@ -44,7 +44,7 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa * Use the default lifetime of 10 minutes. The lifetime covers the following activities: * Server wrap secret -> device reboot -> server unwrap blob. */ - private static final long DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS = 600_1000; + private static final long DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS = 600_000; private final LockSettingsStorage mStorage; diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java index 6d420a95e26c..35e6489debcf 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java @@ -18,7 +18,6 @@ package com.android.server.locksettings; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; -import android.security.keystore2.AndroidKeyStoreProvider; import android.util.Slog; import java.io.ByteArrayOutputStream; @@ -141,19 +140,8 @@ public class SyntheticPasswordCrypto { } } - /** - * TODO This function redirects keystore access to the legacy keystore during a transitional - * phase during which not all calling code has been adjusted to use Keystore 2.0. - * This can be reverted to a constant of "AndroidKeyStore" when b/171305684 is complete. - * The specific bug for this component is b/171305115. - */ static String androidKeystoreProviderName() { - if (AndroidKeyStoreProvider.isInstalled()) { - return "AndroidKeyStoreLegacy"; - } else { - return "AndroidKeystore"; - } - + return "AndroidKeyStore"; } public static byte[] decryptBlob(String keyAlias, byte[] blob, byte[] applicationId) { diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java index 9857fb637b59..f5941361bd89 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeyStoreProxyImpl.java @@ -16,8 +16,6 @@ package com.android.server.locksettings.recoverablekeystore; -import android.security.keystore2.AndroidKeyStoreProvider; - import java.io.IOException; import java.security.Key; import java.security.KeyStore; @@ -31,22 +29,9 @@ import java.security.cert.CertificateException; */ public class KeyStoreProxyImpl implements KeyStoreProxy { - private final KeyStore mKeyStore; - - /** - * TODO This function redirects keystore access to the legacy keystore during a transitional - * phase during which not all calling code has been adjusted to use Keystore 2.0. - * This can be reverted to a constant of "AndroidKeyStore" when b/171305684 is complete. - * The specific bug for this component is b/171305545. - */ - static String androidKeystoreProviderName() { - if (AndroidKeyStoreProvider.isInstalled()) { - return "AndroidKeyStoreLegacy"; - } else { - return "AndroidKeyStore"; - } + public static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore"; - } + private final KeyStore mKeyStore; /** * A new instance, delegating to {@code keyStore}. @@ -84,7 +69,7 @@ public class KeyStoreProxyImpl implements KeyStoreProxy { * @throws KeyStoreException if there was a problem getting or initializing the key store. */ public static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException { - KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName()); + KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER); try { keyStore.load(/*param=*/ null); } catch (CertificateException | IOException | NoSuchAlgorithmException e) { diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java index dff1df7afa11..f448a6ef8c0b 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java @@ -470,7 +470,7 @@ public class PlatformKeyManager { * @throws KeyStoreException if there was a problem getting or initializing the key store. */ private static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException { - KeyStore keyStore = KeyStore.getInstance(KeyStoreProxyImpl.androidKeystoreProviderName()); + KeyStore keyStore = KeyStore.getInstance(KeyStoreProxyImpl.ANDROID_KEY_STORE_PROVIDER); try { keyStore.load(/*param=*/ null); } catch (CertificateException | IOException | NoSuchAlgorithmException e) { diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index 6d97ed7a69a7..b49bced4e567 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -34,7 +34,6 @@ import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; -import android.security.KeyStore; import android.security.keystore.recovery.KeyChainProtectionParams; import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.RecoveryCertPath; @@ -110,14 +109,14 @@ public class RecoverableKeyStoreManager { * @hide */ public static synchronized RecoverableKeyStoreManager - getInstance(Context context, KeyStore keystore) { + getInstance(Context context) { if (mInstance == null) { RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(context); PlatformKeyManager platformKeyManager; ApplicationKeyStorage applicationKeyStorage; try { platformKeyManager = PlatformKeyManager.getInstance(context, db); - applicationKeyStorage = ApplicationKeyStorage.getInstance(keystore); + applicationKeyStorage = ApplicationKeyStorage.getInstance(); } catch (NoSuchAlgorithmException e) { // Impossible: all algorithms must be supported by AOSP throw new RuntimeException(e); diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java index 84ddbf778c70..2398f56f847c 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java @@ -21,9 +21,13 @@ import static android.security.keystore.recovery.RecoveryController.ERROR_SERVIC import android.annotation.Nullable; import android.os.ServiceSpecificException; import android.security.Credentials; +import android.security.KeyStore; +import android.security.KeyStore2; import android.security.keystore.KeyProperties; import android.security.keystore.KeyProtection; -import android.security.KeyStore; +import android.system.keystore2.Domain; +import android.system.keystore2.KeyDescriptor; +import android.system.keystore2.KeyPermission; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -47,32 +51,37 @@ public class ApplicationKeyStorage { private static final String APPLICATION_KEY_ALIAS_PREFIX = "com.android.server.locksettings.recoverablekeystore/application/"; + private static final String APPLICATION_KEY_GRANT_PREFIX = "recoverable_key:"; private final KeyStoreProxy mKeyStore; - private final KeyStore mKeystoreService; - public static ApplicationKeyStorage getInstance(KeyStore keystoreService) + /** + * Creates a new instance. + */ + public static ApplicationKeyStorage getInstance() throws KeyStoreException { return new ApplicationKeyStorage( - new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()), - keystoreService); + new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore())); } @VisibleForTesting - ApplicationKeyStorage(KeyStoreProxy keyStore, KeyStore keystoreService) { + ApplicationKeyStorage(KeyStoreProxy keyStore) { mKeyStore = keyStore; - mKeystoreService = keystoreService; } /** - * Returns grant alias, valid in Applications namespace. + * Returns String representation of {@code KeyDescriptor} valid in application's namespace. */ public @Nullable String getGrantAlias(int userId, int uid, String alias) { - // Aliases used by {@link KeyStore} are different than used by public API. - // {@code USER_PRIVATE_KEY} prefix is used secret keys. Log.i(TAG, String.format(Locale.US, "Get %d/%d/%s", userId, uid, alias)); - String keystoreAlias = Credentials.USER_PRIVATE_KEY + getInternalAlias(userId, uid, alias); - return mKeystoreService.grant(keystoreAlias, uid); + String keystoreAlias = getInternalAlias(userId, uid, alias); + if (useKeyStore2()) { + return makeKeystoreEngineGrantString(uid, keystoreAlias); + } else { + // Aliases used by {@link KeyStore} are different than used by public API. + // {@code USER_PRIVATE_KEY} prefix is used secret keys. + return KeyStore.getInstance().grant(Credentials.USER_PRIVATE_KEY + keystoreAlias, uid); + } } public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey) @@ -117,4 +126,31 @@ public class ApplicationKeyStorage { private String getInternalAlias(int userId, int uid, String alias) { return APPLICATION_KEY_ALIAS_PREFIX + userId + "/" + uid + "/" + alias; } + + private String makeKeystoreEngineGrantString(int uid, String alias) { + if (alias == null) { + return null; + } + + KeyDescriptor key = new KeyDescriptor(); + key.domain = Domain.APP; + key.nspace = KeyProperties.NAMESPACE_APPLICATION; + key.alias = alias; + key.blob = null; + + int grantAccessVector = KeyPermission.USE | KeyPermission.GET_INFO | KeyPermission.DELETE; + + try { + key = KeyStore2.getInstance().grant(key, uid, grantAccessVector); + } catch (android.security.KeyStoreException e) { + Log.e(TAG, "Failed to get grant for KeyStore key.", e); + throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); + } + return String.format("%s%016X", APPLICATION_KEY_GRANT_PREFIX, key.nspace); + } + + private static boolean useKeyStore2() { + return android.security.keystore2.AndroidKeyStoreProvider.isInstalled(); + } + } diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java index 7ef4924879bf..55511ad637ac 100644 --- a/services/core/java/com/android/server/media/MediaKeyDispatcher.java +++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java @@ -77,7 +77,7 @@ public abstract class MediaKeyDispatcher { mOverriddenKeyEvents.put(KeyEvent.KEYCODE_VOLUME_MUTE, 0); } - // TODO: Move this method into SessionPolicyProvider.java for better readability. + // TODO: Move this method into MediaSessionPolicyProvider.java for better readability. /** * Implement this to customize the logic for which MediaSession should consume which key event. * diff --git a/services/core/java/com/android/server/media/SessionPolicyProvider.java b/services/core/java/com/android/server/media/MediaSessionPolicyProvider.java index 332c85adec01..ceadb596dd42 100644 --- a/services/core/java/com/android/server/media/SessionPolicyProvider.java +++ b/services/core/java/com/android/server/media/MediaSessionPolicyProvider.java @@ -31,7 +31,7 @@ import java.lang.annotation.RetentionPolicy; * without any parameters. */ // TODO: Move this class to apex/media/ -public abstract class SessionPolicyProvider { +public abstract class MediaSessionPolicyProvider { @IntDef(value = { SESSION_POLICY_IGNORE_BUTTON_RECEIVER, SESSION_POLICY_IGNORE_BUTTON_SESSION @@ -55,7 +55,7 @@ public abstract class SessionPolicyProvider { */ static final int SESSION_POLICY_IGNORE_BUTTON_SESSION = 1 << 1; - public SessionPolicyProvider(Context context) { + public MediaSessionPolicyProvider(Context context) { // Constructor used for reflection } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index c462a9274117..0a074e1e7c50 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -895,7 +895,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR throws RemoteException { final long token = Binder.clearCallingIdentity(); try { - if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) + if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) != 0) { return; } @@ -911,7 +911,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException { final long token = Binder.clearCallingIdentity(); try { - if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) + if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) != 0) { return; } diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java index 032523ef9694..3c50597b8cfc 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java @@ -20,7 +20,7 @@ import android.media.AudioManager; import android.os.ResultReceiver; import android.view.KeyEvent; -import com.android.server.media.SessionPolicyProvider.SessionPolicy; +import com.android.server.media.MediaSessionPolicyProvider.SessionPolicy; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 6c1d3991c4e9..23d84298b41e 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -40,10 +40,10 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.content.pm.ParceledListSlice; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.IRemoteSessionCallback; +import android.media.MediaCommunicationManager; import android.media.Session2Token; import android.media.session.IActiveSessionsListener; import android.media.session.IOnMediaKeyEventDispatchedListener; @@ -148,9 +148,28 @@ public class MediaSessionService extends SystemService implements Monitor { final RemoteCallbackList<IRemoteSessionCallback> mRemoteVolumeControllers = new RemoteCallbackList<>(); - private SessionPolicyProvider mCustomSessionPolicyProvider; + private MediaSessionPolicyProvider mCustomMediaSessionPolicyProvider; private MediaKeyDispatcher mCustomMediaKeyDispatcher; + private MediaCommunicationManager mCommunicationManager; + private final MediaCommunicationManager.SessionCallback mSession2TokenCallback = + new MediaCommunicationManager.SessionCallback() { + @Override + public void onSession2TokenCreated(Session2Token token) { + if (DEBUG) { + Log.d(TAG, "Session2 is created " + token); + } + MediaSession2Record record = new MediaSession2Record(token, + MediaSessionService.this, mRecordThread.getLooper(), 0); + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(record.getUserId()); + if (user != null) { + user.mPriorityStack.addSession(record); + } + } + } + }; + public MediaSessionService(Context context) { super(context); mContext = context; @@ -191,8 +210,10 @@ public class MediaSessionService extends SystemService implements Monitor { updateUser(); - instantiateCustomProvider(null); - instantiateCustomDispatcher(null); + instantiateCustomProvider(mContext.getResources().getString( + R.string.config_customMediaSessionPolicyProvider)); + instantiateCustomDispatcher(mContext.getResources().getString( + R.string.config_customMediaKeyDispatcher)); mRecordThread.start(); final IntentFilter filter = new IntentFilter( @@ -200,6 +221,19 @@ public class MediaSessionService extends SystemService implements Monitor { mContext.registerReceiver(mNotificationListenerEnabledChangedReceiver, filter); } + @Override + public void onBootPhase(int phase) { + super.onBootPhase(phase); + switch (phase) { + // This ensures MediaCommunicationService is started + case PHASE_BOOT_COMPLETED: + mCommunicationManager = mContext.getSystemService(MediaCommunicationManager.class); + mCommunicationManager.registerSessionCallback(new HandlerExecutor(mHandler), + mSession2TokenCallback); + break; + } + } + private final BroadcastReceiver mNotificationListenerEnabledChangedReceiver = new BroadcastReceiver() { @Override @@ -589,8 +623,8 @@ public class MediaSessionService extends SystemService implements Monitor { String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) { synchronized (mLock) { int policies = 0; - if (mCustomSessionPolicyProvider != null) { - policies = mCustomSessionPolicyProvider.getSessionPoliciesForApplication( + if (mCustomMediaSessionPolicyProvider != null) { + policies = mCustomMediaSessionPolicyProvider.getSessionPoliciesForApplication( callerUid, callerPackageName); } @@ -778,16 +812,13 @@ public class MediaSessionService extends SystemService implements Monitor { return null; } - private void instantiateCustomDispatcher(String nameFromTesting) { + private void instantiateCustomDispatcher(String componentName) { synchronized (mLock) { mCustomMediaKeyDispatcher = null; - String customDispatcherClassName = (nameFromTesting == null) - ? mContext.getResources().getString(R.string.config_customMediaKeyDispatcher) - : nameFromTesting; try { - if (!TextUtils.isEmpty(customDispatcherClassName)) { - Class customDispatcherClass = Class.forName(customDispatcherClassName); + if (componentName != null && !TextUtils.isEmpty(componentName)) { + Class customDispatcherClass = Class.forName(componentName); Constructor constructor = customDispatcherClass.getDeclaredConstructor(Context.class); mCustomMediaKeyDispatcher = @@ -801,20 +832,17 @@ public class MediaSessionService extends SystemService implements Monitor { } } - private void instantiateCustomProvider(String nameFromTesting) { + private void instantiateCustomProvider(String componentName) { synchronized (mLock) { - mCustomSessionPolicyProvider = null; + mCustomMediaSessionPolicyProvider = null; - String customProviderClassName = (nameFromTesting == null) - ? mContext.getResources().getString(R.string.config_customSessionPolicyProvider) - : nameFromTesting; try { - if (!TextUtils.isEmpty(customProviderClassName)) { - Class customProviderClass = Class.forName(customProviderClassName); + if (componentName != null && !TextUtils.isEmpty(componentName)) { + Class customProviderClass = Class.forName(componentName); Constructor constructor = customProviderClass.getDeclaredConstructor(Context.class); - mCustomSessionPolicyProvider = - (SessionPolicyProvider) constructor.newInstance(mContext); + mCustomMediaSessionPolicyProvider = + (MediaSessionPolicyProvider) constructor.newInstance(mContext); } } catch (ClassNotFoundException | InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { @@ -1143,31 +1171,6 @@ public class MediaSessionService extends SystemService implements Monitor { } @Override - public void notifySession2Created(Session2Token sessionToken) throws RemoteException { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - try { - if (DEBUG) { - Log.d(TAG, "Session2 is created " + sessionToken); - } - if (uid != sessionToken.getUid()) { - throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid - + " but actually=" + sessionToken.getUid()); - } - MediaSession2Record record = new MediaSession2Record(sessionToken, - MediaSessionService.this, mRecordThread.getLooper(), 0); - synchronized (mLock) { - FullUserRecord user = getFullUserRecordLocked(record.getUserId()); - user.mPriorityStack.addSession(record); - } - // Do not immediately notify changes -- do so when framework can dispatch command - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override public List<MediaSession.Token> getSessions(ComponentName componentName, int userId) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); @@ -1189,26 +1192,6 @@ public class MediaSessionService extends SystemService implements Monitor { } @Override - public ParceledListSlice getSession2Tokens(int userId) { - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); - - try { - // Check that they can make calls on behalf of the user and get the final user id - int resolvedUserId = handleIncomingUser(pid, uid, userId, null); - List<Session2Token> result; - synchronized (mLock) { - FullUserRecord user = getFullUserRecordLocked(userId); - result = user.mPriorityStack.getSession2Tokens(resolvedUserId); - } - return new ParceledListSlice(result); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override public void addSessionsListener(IActiveSessionsListener listener, ComponentName componentName, int userId) throws RemoteException { final int pid = Binder.getCallingPid(); @@ -1933,16 +1916,30 @@ public class MediaSessionService extends SystemService implements Monitor { } @Override - public void setCustomMediaKeyDispatcherForTesting(String name) { + public void setCustomMediaKeyDispatcher(String name) { instantiateCustomDispatcher(name); } @Override - public void setCustomSessionPolicyProviderForTesting(String name) { + public void setCustomMediaSessionPolicyProvider(String name) { instantiateCustomProvider(name); } @Override + public boolean hasCustomMediaKeyDispatcher(String componentName) { + return mCustomMediaKeyDispatcher == null ? false + : TextUtils.equals(componentName, + mCustomMediaKeyDispatcher.getClass().getName()); + } + + @Override + public boolean hasCustomMediaSessionPolicyProvider(String componentName) { + return mCustomMediaSessionPolicyProvider == null ? false + : TextUtils.equals(componentName, + mCustomMediaSessionPolicyProvider.getClass().getName()); + } + + @Override public int getSessionPolicies(MediaSession.Token token) { synchronized (mLock) { MediaSessionRecord record = getMediaSessionRecordLocked(token); diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index f8ff5b5f8e66..50eed19389dc 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -16,7 +16,7 @@ package com.android.server.media; -import static com.android.server.media.SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_SESSION; +import static com.android.server.media.MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_SESSION; import android.media.Session2Token; import android.media.session.MediaSession; diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java index bce80696f72c..22ed781da92d 100644 --- a/services/core/java/com/android/server/net/NetworkIdentitySet.java +++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java @@ -40,6 +40,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements private static final int VERSION_ADD_NETWORK_ID = 3; private static final int VERSION_ADD_METERED = 4; private static final int VERSION_ADD_DEFAULT_NETWORK = 5; + private static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6; public NetworkIdentitySet() { } @@ -84,13 +85,20 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements defaultNetwork = true; } + final int oemNetCapabilities; + if (version >= VERSION_ADD_OEM_MANAGED_NETWORK) { + oemNetCapabilities = in.readInt(); + } else { + oemNetCapabilities = NetworkIdentity.OEM_NONE; + } + add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered, - defaultNetwork)); + defaultNetwork, oemNetCapabilities)); } } public void writeToStream(DataOutput out) throws IOException { - out.writeInt(VERSION_ADD_DEFAULT_NETWORK); + out.writeInt(VERSION_ADD_OEM_MANAGED_NETWORK); out.writeInt(size()); for (NetworkIdentity ident : this) { out.writeInt(ident.getType()); @@ -100,6 +108,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements out.writeBoolean(ident.getRoaming()); out.writeBoolean(ident.getMetered()); out.writeBoolean(ident.getDefaultNetwork()); + out.writeInt(ident.getOemManaged()); } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 676f4218f745..4b4146683a09 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -29,6 +29,7 @@ import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.os.Process.INVALID_UID; import android.app.ActivityManager; +import android.app.ActivityManager.ProcessCapability; import android.net.NetworkPolicyManager; import android.os.UserHandle; import android.util.Log; @@ -97,13 +98,15 @@ public class NetworkPolicyLogger { } } - void uidStateChanged(int uid, int procState, long procStateSeq) { + void uidStateChanged(int uid, int procState, long procStateSeq, + @ProcessCapability int capability) { synchronized (mLock) { if (LOGV || uid == mDebugUid) { Slog.v(TAG, uid + " state changed to " - + ProcessList.makeProcStateString(procState) + " with seq=" + procStateSeq); + + ProcessList.makeProcStateString(procState) + ",seq=" + procStateSeq + + ",cap=" + ActivityManager.getCapabilitiesSummary(capability)); } - mUidStateChangeBuffer.uidStateChanged(uid, procState, procStateSeq); + mUidStateChangeBuffer.uidStateChanged(uid, procState, procStateSeq, capability); } } @@ -373,7 +376,8 @@ public class NetworkPolicyLogger { super(Data.class, capacity); } - public void uidStateChanged(int uid, int procState, long procStateSeq) { + public void uidStateChanged(int uid, int procState, long procStateSeq, + @ProcessCapability int capability) { final Data data = getNextSlot(); if (data == null) return; @@ -381,6 +385,7 @@ public class NetworkPolicyLogger { data.type = EVENT_UID_STATE_CHANGED; data.ifield1 = uid; data.ifield2 = procState; + data.ifield3 = capability; data.lfield1 = procStateSeq; data.timeStamp = System.currentTimeMillis(); } @@ -546,8 +551,9 @@ public class NetworkPolicyLogger { case EVENT_NETWORK_BLOCKED: return data.ifield1 + "-" + getBlockedReason(data.ifield2); case EVENT_UID_STATE_CHANGED: - return data.ifield1 + "-" + ProcessList.makeProcStateString(data.ifield2) - + "-" + data.lfield1; + return data.ifield1 + ":" + ProcessList.makeProcStateString(data.ifield2) + + ":" + ActivityManager.getCapabilitiesSummary(data.ifield3) + + ":" + data.lfield1; case EVENT_POLICIES_CHANGED: return getPolicyChangedLog(data.ifield1, data.ifield2, data.ifield3); case EVENT_METEREDNESS_CHANGED: diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index aa7da54b2e1d..067c5c0e7620 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -54,6 +54,7 @@ import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkIdentity.OEM_NONE; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; @@ -131,6 +132,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManager.ProcessCapability; import android.app.ActivityManagerInternal; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -166,6 +168,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkIdentity; import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; +import android.net.NetworkPolicyManager.UidState; import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkSpecifier; @@ -557,7 +560,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Foreground at UID granularity. */ @GuardedBy("mUidRulesFirstLock") - final SparseIntArray mUidState = new SparseIntArray(); + final SparseArray<UidState> mUidState = new SparseArray<UidState>(); /** Map from network ID to last observed meteredness state */ @GuardedBy("mNetworkPoliciesSecondLock") @@ -880,9 +883,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); try { + // TODO: There shouldn't be a need to receive callback for all changes. mActivityManager.registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE, - NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android"); + ActivityManager.PROCESS_STATE_UNKNOWN, "android"); mNetworkManager.registerObserver(mAlertObserver); } catch (RemoteException e) { // ignored; both services live in system_server @@ -988,9 +992,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final private IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, - int capability) { + @ProcessCapability int capability) { + // TODO: Avoid creating a new UidStateCallbackInfo object every time + // we get a callback for an uid mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, - uid, procState, procStateSeq).sendToTarget(); + new UidStateCallbackInfo(uid, procState, procStateSeq, capability)) + .sendToTarget(); } @Override public void onUidGone(int uid, boolean disabled) { @@ -1007,6 +1014,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } }; + private static final class UidStateCallbackInfo { + public final int uid; + public final int procState; + public final long procStateSeq; + @ProcessCapability + public final int capability; + + UidStateCallbackInfo(int uid, int procState, long procStateSeq, + @ProcessCapability int capability) { + this.uid = uid; + this.procState = procState; + this.procStateSeq = procStateSeq; + this.capability = capability; + } + } + final private BroadcastReceiver mPowerSaveWhitelistReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -1382,7 +1405,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final String subscriberId = mSubIdToSubscriberId.valueAt(i); final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, - true); + true, OEM_NONE); + /* While OEM_NONE indicates "any non OEM managed network", OEM_NONE is meant to be a + * placeholder value here. The probeIdent is matched against a NetworkTemplate which + * should have its OEM managed value set to OEM_MANAGED_ALL, which will cause the + * template to match probeIdent without regard to OEM managed status. */ if (template.matches(probeIdent)) { return subId; } @@ -1612,7 +1639,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // find and update the mobile NetworkPolicy for this subscriber id boolean policyUpdated = false; final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true); + TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true, + OEM_NONE); for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) { final NetworkTemplate template = mNetworkPolicy.keyAt(i); if (template.matches(probeIdent)) { @@ -1841,7 +1869,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, - true); + true, OEM_NONE); // Template is matched when subscriber id matches. if (template.matches(probeIdent)) { matchingSubIds.add(subId); @@ -2156,7 +2184,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private boolean ensureActiveMobilePolicyAL(int subId, String subscriberId) { // Poke around to see if we already have a policy final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, - TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true); + TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true, + OEM_NONE); for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) { final NetworkTemplate template = mNetworkPolicy.keyAt(i); if (template.matches(probeIdent)) { @@ -3718,14 +3747,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.print("UID="); fout.print(uid); - final int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - fout.print(" state="); - fout.print(state); - if (state <= ActivityManager.PROCESS_STATE_TOP) { - fout.print(" (fg)"); + final UidState uidState = mUidState.get(uid); + if (uidState == null) { + fout.print(" state={null}"); } else { - fout.print(state <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE - ? " (fg svc)" : " (bg)"); + fout.print(" state="); + fout.print(uidState.toString()); } final int uidRules = mUidRules.get(uid, RULE_NONE); @@ -3783,26 +3810,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @VisibleForTesting boolean isUidForeground(int uid) { synchronized (mUidRulesFirstLock) { - return isUidStateForeground( - mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY)); + return isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.get(uid)); } } @GuardedBy("mUidRulesFirstLock") private boolean isUidForegroundOnRestrictBackgroundUL(int uid) { - final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - return isProcStateAllowedWhileOnRestrictBackground(procState); + final UidState uidState = mUidState.get(uid); + return isProcStateAllowedWhileOnRestrictBackground(uidState); } @GuardedBy("mUidRulesFirstLock") private boolean isUidForegroundOnRestrictPowerUL(int uid) { - final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - return isProcStateAllowedWhileIdleOrPowerSaveMode(procState); - } - - private boolean isUidStateForeground(int state) { - // only really in foreground when screen is also on - return state <= NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE; + final UidState uidState = mUidState.get(uid); + return isProcStateAllowedWhileIdleOrPowerSaveMode(uidState); } /** @@ -3811,16 +3832,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * {@link #updateRulesForPowerRestrictionsUL(int)}. Returns true if the state was updated. */ @GuardedBy("mUidRulesFirstLock") - private boolean updateUidStateUL(int uid, int uidState) { + private boolean updateUidStateUL(int uid, int procState, @ProcessCapability int capability) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateUidStateUL"); try { - final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - if (oldUidState != uidState) { + final UidState oldUidState = mUidState.get(uid); + if (oldUidState == null || oldUidState.procState != procState + || oldUidState.capability != capability) { + final UidState newUidState = new UidState(uid, procState, capability); // state changed, push updated rules - mUidState.put(uid, uidState); - updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, uidState); + mUidState.put(uid, newUidState); + updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, newUidState); if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState) - != isProcStateAllowedWhileIdleOrPowerSaveMode(uidState) ) { + != isProcStateAllowedWhileIdleOrPowerSaveMode(newUidState)) { updateRuleForAppIdleUL(uid); if (mDeviceIdleMode) { updateRuleForDeviceIdleUL(uid); @@ -3842,11 +3865,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private boolean removeUidStateUL(int uid) { final int index = mUidState.indexOfKey(uid); if (index >= 0) { - final int oldUidState = mUidState.valueAt(index); + final UidState oldUidState = mUidState.valueAt(index); mUidState.removeAt(index); - if (oldUidState != ActivityManager.PROCESS_STATE_CACHED_EMPTY) { - updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, - ActivityManager.PROCESS_STATE_CACHED_EMPTY); + if (oldUidState != null) { + updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, null); if (mDeviceIdleMode) { updateRuleForDeviceIdleUL(uid); } @@ -3873,8 +3895,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private void updateRestrictBackgroundRulesOnUidStatusChangedUL(int uid, int oldUidState, - int newUidState) { + private void updateRestrictBackgroundRulesOnUidStatusChangedUL(int uid, + @Nullable UidState oldUidState, @Nullable UidState newUidState) { final boolean oldForeground = isProcStateAllowedWhileOnRestrictBackground(oldUidState); final boolean newForeground = @@ -4979,11 +5001,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public boolean handleMessage(Message msg) { switch (msg.what) { case UID_MSG_STATE_CHANGED: { - final int uid = msg.arg1; - final int procState = msg.arg2; - final long procStateSeq = (Long) msg.obj; - - handleUidChanged(uid, procState, procStateSeq); + final UidStateCallbackInfo uidStateCallbackInfo = + (UidStateCallbackInfo) msg.obj; + final int uid = uidStateCallbackInfo.uid; + final int procState = uidStateCallbackInfo.procState; + final long procStateSeq = uidStateCallbackInfo.procStateSeq; + final int capability = uidStateCallbackInfo.capability; + + handleUidChanged(uid, procState, procStateSeq, capability); return true; } case UID_MSG_GONE: { @@ -4998,23 +5023,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } }; - void handleUidChanged(int uid, int procState, long procStateSeq) { + void handleUidChanged(int uid, int procState, long procStateSeq, + @ProcessCapability int capability) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidStateChanged"); try { boolean updated; synchronized (mUidRulesFirstLock) { // We received a uid state change callback, add it to the history so that it // will be useful for debugging. - mLogger.uidStateChanged(uid, procState, procStateSeq); + mLogger.uidStateChanged(uid, procState, procStateSeq, capability); // Now update the network policy rules as per the updated uid state. - updated = updateUidStateUL(uid, procState); + updated = updateUidStateUL(uid, procState, capability); // Updating the network rules is done, so notify AMS about this. mActivityManagerInternal.notifyNetworkPolicyRulesUpdated(uid, procStateSeq); } // Do this without the lock held. handleUidChanged() and handleUidGone() are // called from the handler, so there's no multi-threading issue. if (updated) { - updateNetworkStats(uid, isUidStateForeground(procState)); + updateNetworkStats(uid, isProcStateAllowedWhileOnRestrictBackground(procState)); } } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); @@ -5349,6 +5375,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private static void collectKeys(SparseArray<UidState> source, SparseBooleanArray target) { + final int size = source.size(); + for (int i = 0; i < size; i++) { + target.put(source.keyAt(i), true); + } + } + @Override public void factoryReset(String subscriber) { mContext.enforceCallingOrSelfPermission(NETWORK_SETTINGS, TAG); diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java index 6aefe41891f9..557fa8944445 100644 --- a/services/core/java/com/android/server/net/NetworkStatsCollection.java +++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java @@ -54,6 +54,8 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FastDataInput; +import com.android.internal.util.FastDataOutput; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; @@ -89,6 +91,9 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W /** File header magic number: "ANET" */ private static final int FILE_MAGIC = 0x414E4554; + /** Default buffer size from BufferedInputStream */ + private static final int BUFFER_SIZE = 8192; + private static final int VERSION_NETWORK_INIT = 1; private static final int VERSION_UID_INIT = 1; @@ -434,7 +439,8 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W @Override public void read(InputStream in) throws IOException { - read((DataInput) new DataInputStream(in)); + final FastDataInput dataIn = new FastDataInput(in, BUFFER_SIZE); + read(dataIn); } private void read(DataInput in) throws IOException { @@ -473,8 +479,9 @@ public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.W @Override public void write(OutputStream out) throws IOException { - write((DataOutput) new DataOutputStream(out)); - out.flush(); + final FastDataOutput dataOut = new FastDataOutput(out, BUFFER_SIZE); + write(dataOut); + dataOut.flush(); } private void write(DataOutput out) throws IOException { diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 9706bcece924..5b9a11bc5a31 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -1319,7 +1319,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(), ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(), ident.getRoaming(), true /* metered */, - true /* onDefaultNetwork */); + true /* onDefaultNetwork */, ident.getOemManaged()); findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent); findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent); } diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index 1051423ea17f..afb47e831bdb 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -18,6 +18,7 @@ package com.android.server.notification; import android.app.Notification; import android.net.Uri; +import android.os.Bundle; import android.os.UserHandle; import android.service.notification.NotificationStats; @@ -31,7 +32,7 @@ public interface NotificationDelegate { void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex, Notification.Action action, NotificationVisibility nv, boolean generatedByAssistant); void onNotificationClear(int callingUid, int callingPid, - String pkg, String tag, int id, int userId, String key, + String pkg, int userId, String key, @NotificationStats.DismissalSurface int dismissalSurface, @NotificationStats.DismissalSentiment int dismissalSentiment, NotificationVisibility nv); @@ -88,5 +89,13 @@ public interface NotificationDelegate { void onNotificationSmartReplySent(String key, int clickedIndex, CharSequence reply, int notificationLocation, boolean modifiedBeforeSending); + /** + * Notifies a user feedback is provided. + * + * @param key the notification key + * @param feedback the feedback detail + */ + void onNotificationFeedbackReceived(String key, Bundle feedback); + void prepareForPossibleShutdown(); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 917be29243ad..73db7054cec5 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -64,6 +64,8 @@ import static android.media.AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY; import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; +import static android.os.PowerWhitelistManager.REASON_NOTIFICATION_SERVICE; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.os.UserHandle.USER_NULL; import static android.os.UserHandle.USER_SYSTEM; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; @@ -73,6 +75,8 @@ import static android.service.notification.NotificationListenerService.FLAG_FILT import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS; import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS; import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS; +import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES; +import static android.service.notification.NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; @@ -172,7 +176,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; -import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutServiceInternal; @@ -215,7 +218,6 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.service.notification.Adjustment; import android.service.notification.Condition; -import android.service.notification.ConditionProviderService; import android.service.notification.ConversationChannelWrapper; import android.service.notification.IConditionProvider; import android.service.notification.INotificationListener; @@ -383,7 +385,8 @@ public class NotificationManagerService extends SystemService { Adjustment.KEY_TEXT_REPLIES, Adjustment.KEY_NOT_CONVERSATION, Adjustment.KEY_IMPORTANCE, - Adjustment.KEY_RANKING_SCORE}; + Adjustment.KEY_RANKING_SCORE + }; static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] { RoleManager.ROLE_DIALER, @@ -432,8 +435,6 @@ public class NotificationManagerService extends SystemService { private static final String SCHEME_TIMEOUT = "timeout"; private static final String EXTRA_KEY = "key"; - private static final String FEEDBACK_KEY = "feedback_key"; - private static final int NOTIFICATION_INSTANCE_ID_MAX = (1 << 13); /** @@ -1020,30 +1021,26 @@ public class NotificationManagerService extends SystemService { return; } final long now = System.currentTimeMillis(); - //TODO(b/154257994): remove this when feedback apis are in place - boolean isFeedback = action.getExtras().containsKey(FEEDBACK_KEY); - if (!isFeedback) { - MetricsLogger.action(r.getLogMaker(now) - .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION) - .setType(MetricsEvent.TYPE_ACTION) - .setSubtype(actionIndex) - .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank) - .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count) - .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART, - action.isContextual() ? 1 : 0) - .addTaggedData( - MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED, - generatedByAssistant ? 1 : 0) - .addTaggedData(MetricsEvent.NOTIFICATION_LOCATION, - nv.location.toMetricsEventEnum())); - mNotificationRecordLogger.log( - NotificationRecordLogger.NotificationEvent.fromAction(actionIndex, - generatedByAssistant, action.isContextual()), r); - EventLogTags.writeNotificationActionClicked(key, actionIndex, - r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now), - nv.rank, nv.count); - nv.recycle(); - } + MetricsLogger.action(r.getLogMaker(now) + .setCategory(MetricsEvent.NOTIFICATION_ITEM_ACTION) + .setType(MetricsEvent.TYPE_ACTION) + .setSubtype(actionIndex) + .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank) + .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count) + .addTaggedData(MetricsEvent.NOTIFICATION_ACTION_IS_SMART, + action.isContextual() ? 1 : 0) + .addTaggedData( + MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED, + generatedByAssistant ? 1 : 0) + .addTaggedData(MetricsEvent.NOTIFICATION_LOCATION, + nv.location.toMetricsEventEnum())); + mNotificationRecordLogger.log( + NotificationRecordLogger.NotificationEvent.fromAction(actionIndex, + generatedByAssistant, action.isContextual()), r); + EventLogTags.writeNotificationActionClicked(key, actionIndex, + r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now), + nv.rank, nv.count); + nv.recycle(); reportUserInteraction(r); mAssistants.notifyAssistantActionClicked(r, action, generatedByAssistant); } @@ -1051,15 +1048,19 @@ public class NotificationManagerService extends SystemService { @Override public void onNotificationClear(int callingUid, int callingPid, - String pkg, String tag, int id, int userId, String key, + String pkg, int userId, String key, @NotificationStats.DismissalSurface int dismissalSurface, @NotificationStats.DismissalSentiment int dismissalSentiment, NotificationVisibility nv) { + String tag = null; + int id = 0; synchronized (mNotificationLock) { NotificationRecord r = mNotificationsByKey.get(key); if (r != null) { r.recordDismissalSurface(dismissalSurface); r.recordDismissalSentiment(dismissalSentiment); + tag = r.getSbn().getTag(); + id = r.getSbn().getId(); } } cancelNotification(callingUid, callingPid, pkg, tag, id, 0, @@ -1386,6 +1387,20 @@ public class NotificationManagerService extends SystemService { } } } + + @Override + public void onNotificationFeedbackReceived(String key, Bundle feedback) { + exitIdle(); + synchronized (mNotificationLock) { + NotificationRecord r = mNotificationsByKey.get(key); + if (r == null) { + if (DBG) Slog.w(TAG, "No notification with key: " + key); + return; + } + mAssistants.notifyAssistantFeedbackReceived(r, feedback); + } + } + }; @VisibleForTesting @@ -5990,10 +6005,11 @@ public class NotificationManagerService extends SystemService { for (int i = 0; i < intentCount; i++) { PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i); if (pendingIntent != null) { - am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), + am.setPendingIntentAllowlistDuration(pendingIntent.getTarget(), ALLOWLIST_TOKEN, duration, - BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED - ); + TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + REASON_NOTIFICATION_SERVICE, + "NotificationManagerService"); am.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(), ALLOWLIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER | FLAG_SERVICE_SENDER)); @@ -9505,6 +9521,26 @@ public class NotificationManagerService extends SystemService { }); } + @GuardedBy("mNotificationLock") + void notifyAssistantFeedbackReceived(final NotificationRecord r, Bundle feedback) { + final StatusBarNotification sbn = r.getSbn(); + + for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { + boolean sbnVisible = isVisibleToListener( + sbn, r.getNotificationType(), info) + && info.isSameUser(r.getUserId()); + if (sbnVisible) { + final INotificationListener assistant = (INotificationListener) info.service; + try { + final NotificationRankingUpdate update = makeRankingUpdateLocked(info); + assistant.onNotificationFeedbackReceived(sbn.getKey(), update, feedback); + } catch (RemoteException ex) { + Slog.e(TAG, "unable to notify assistant (feedback): " + assistant, ex); + } + } + } + } + /** * Notifies the assistant something about the specified notification, only assistant * that is visible to the notification will be notified. @@ -9832,33 +9868,54 @@ public class NotificationManagerService extends SystemService { Pair listener = Pair.create(si.getComponentName(), userId); NotificationListenerFilter existingNlf = mRequestedNotificationListeners.get(listener); - if (existingNlf == null) { - // no stored filters for this listener; see if they provided a default - if (si.metaData != null) { - String typeList = si.metaData.getString( - NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES); - if (typeList != null) { - int types = 0; - String[] typeStrings = typeList.split(XML_SEPARATOR); - for (int i = 0; i < typeStrings.length; i++) { - if (TextUtils.isEmpty(typeStrings[i])) { - continue; - } - try { - types |= Integer.parseInt(typeStrings[i]); - } catch (NumberFormatException e) { - // skip - } + if (si.metaData != null) { + if (existingNlf == null) { + // no stored filters for this listener; see if they provided a default + if (si.metaData.containsKey(META_DATA_DEFAULT_FILTER_TYPES)) { + String typeList = + si.metaData.get(META_DATA_DEFAULT_FILTER_TYPES).toString(); + if (typeList != null) { + int types = getTypesFromStringList(typeList); + NotificationListenerFilter nlf = + new NotificationListenerFilter(types, new ArraySet<>()); + mRequestedNotificationListeners.put(listener, nlf); } + } + } - NotificationListenerFilter nlf = - new NotificationListenerFilter(types, new ArraySet<>()); + // also check the types they never want bridged + if (si.metaData.containsKey(META_DATA_DISABLED_FILTER_TYPES)) { + int neverBridge = getTypesFromStringList(si.metaData.get( + META_DATA_DISABLED_FILTER_TYPES).toString()); + if (neverBridge != 0) { + NotificationListenerFilter nlf = + mRequestedNotificationListeners.getOrDefault( + listener, new NotificationListenerFilter()); + nlf.setTypes(nlf.getTypes() & ~neverBridge); mRequestedNotificationListeners.put(listener, nlf); } } } } + private int getTypesFromStringList(String typeList) { + int types = 0; + if (typeList != null) { + String[] typeStrings = typeList.split(XML_SEPARATOR); + for (int i = 0; i < typeStrings.length; i++) { + if (TextUtils.isEmpty(typeStrings[i])) { + continue; + } + try { + types |= Integer.parseInt(typeStrings[i]); + } catch (NumberFormatException e) { + // skip + } + } + } + return types; + } + @GuardedBy("mNotificationLock") public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) { if (trim == TRIM_LIGHT) { diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index f078242a659f..4500bbcd250f 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -39,6 +39,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.util.XmlUtils; +import com.android.server.pm.PackageManagerService; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -465,6 +466,7 @@ public class SnoozeHelper { return PendingIntent.getBroadcast(mContext, REQUEST_CODE_REPOST, new Intent(REPOST_ACTION) + .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME) .setData(new Uri.Builder().scheme(REPOST_SCHEME).appendPath(key).build()) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) .putExtra(EXTRA_KEY, key) diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java index 2d540de69660..c4b6485d2a2f 100644 --- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java +++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java @@ -23,6 +23,7 @@ import android.net.Uri; import android.os.Process; import android.text.TextUtils; import android.util.Pair; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; @@ -42,9 +43,6 @@ import java.util.Map; */ public class OverlayActorEnforcer { - // By default, the reason is not logged to prevent leaks of why it failed - private static final boolean DEBUG_REASON = false; - private final PackageManagerHelper mPackageManager; /** @@ -85,17 +83,18 @@ public class OverlayActorEnforcer { void enforceActor(@NonNull OverlayInfo overlayInfo, @NonNull String methodName, int callingUid, int userId) throws SecurityException { - ActorState actorState = isAllowedActor(methodName, overlayInfo, callingUid, userId); + final ActorState actorState = isAllowedActor(methodName, overlayInfo, callingUid, userId); if (actorState == ActorState.ALLOWED) { return; } - String targetOverlayableName = overlayInfo.targetOverlayableName; - throw new SecurityException("UID" + callingUid + " is not allowed to call " - + methodName + " for " + final String targetOverlayableName = overlayInfo.targetOverlayableName; + final String errorMessage = "UID" + callingUid + " is not allowed to call " + methodName + + " for " + (TextUtils.isEmpty(targetOverlayableName) ? "" : (targetOverlayableName + " in ")) - + overlayInfo.targetPackageName + (DEBUG_REASON ? (" because " + actorState) : "") - ); + + overlayInfo.targetPackageName + " for user " + userId; + Slog.w(OverlayManagerService.TAG, errorMessage + " because " + actorState); + throw new SecurityException(errorMessage); } /** diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java index a83edb75badb..9c4c5101cb6c 100644 --- a/services/core/java/com/android/server/os/NativeTombstoneManager.java +++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java @@ -16,24 +16,38 @@ package com.android.server.os; +import static android.app.ApplicationExitInfo.REASON_CRASH_NATIVE; +import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import android.annotation.AppIdInt; +import android.annotation.CurrentTimeMillisLong; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ActivityManager.RunningAppProcessInfo; +import android.app.ApplicationExitInfo; +import android.app.IParcelFileDescriptorRetriever; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.FileObserver; import android.os.Handler; import android.os.ParcelFileDescriptor; import android.os.UserHandle; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStat; import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoParseException; import com.android.internal.annotations.GuardedBy; import com.android.server.BootReceiver; import com.android.server.ServiceThread; +import com.android.server.os.TombstoneProtos.Cause; import com.android.server.os.TombstoneProtos.Tombstone; import libcore.io.IoUtils; @@ -42,7 +56,11 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; /** * A class to manage native tombstones. @@ -75,6 +93,9 @@ public final class NativeTombstoneManager { } void onSystemReady() { + registerForUserRemoval(); + registerForPackageRemoval(); + // Scan existing tombstones. mHandler.post(() -> { final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles(); @@ -94,8 +115,9 @@ public final class NativeTombstoneManager { if (filename.endsWith(".pb")) { handleProtoTombstone(path); + BootReceiver.addTombstoneToDropBox(mContext, path, true); } else { - BootReceiver.addTombstoneToDropBox(mContext, path); + BootReceiver.addTombstoneToDropBox(mContext, path, false); } } @@ -145,18 +167,164 @@ public final class NativeTombstoneManager { } } + /** + * Remove native tombstones matching a user and/or app. + * + * @param userId user id to filter by, selects all users if empty + * @param appId app id to filter by, selects all users if empty + */ + public void purge(Optional<Integer> userId, Optional<Integer> appId) { + mHandler.post(() -> { + synchronized (mLock) { + for (int i = mTombstones.size() - 1; i >= 0; --i) { + TombstoneFile tombstone = mTombstones.valueAt(i); + if (tombstone.matches(userId, appId)) { + tombstone.purge(); + mTombstones.removeAt(i); + } + } + } + }); + } + + private void purgePackage(int uid, boolean allUsers) { + final int appId = UserHandle.getAppId(uid); + Optional<Integer> userId; + if (allUsers) { + userId = Optional.empty(); + } else { + userId = Optional.of(UserHandle.getUserId(uid)); + } + purge(userId, Optional.of(appId)); + } + + private void purgeUser(int uid) { + purge(Optional.of(uid), Optional.empty()); + } + + private void registerForPackageRemoval() { + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); + filter.addDataScheme("package"); + mContext.registerReceiverForAllUsers(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final int uid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL); + if (uid == UserHandle.USER_NULL) return; + + final boolean allUsers = intent.getBooleanExtra( + Intent.EXTRA_REMOVED_FOR_ALL_USERS, false); + + purgePackage(uid, allUsers); + } + }, filter, null, mHandler); + } + + private void registerForUserRemoval() { + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_REMOVED); + mContext.registerReceiverForAllUsers(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + if (userId < 1) return; + + purgeUser(userId); + } + }, filter, null, mHandler); + } + + /** + * Collect native tombstones. + * + * @param output list to append to + * @param callingUid POSIX uid to filter by + * @param pid pid to filter by, ignored if zero + * @param maxNum maximum number of elements in output + */ + public void collectTombstones(ArrayList<ApplicationExitInfo> output, int callingUid, int pid, + int maxNum) { + CompletableFuture<Object> future = new CompletableFuture<>(); + + if (!UserHandle.isApp(callingUid)) { + return; + } + + final int userId = UserHandle.getUserId(callingUid); + final int appId = UserHandle.getAppId(callingUid); + + mHandler.post(() -> { + boolean appendedTombstones = false; + + synchronized (mLock) { + final int tombstonesSize = mTombstones.size(); + + tombstoneIter: + for (int i = 0; i < tombstonesSize; ++i) { + TombstoneFile tombstone = mTombstones.valueAt(i); + if (tombstone.matches(Optional.of(userId), Optional.of(appId))) { + if (pid != 0 && tombstone.mPid != pid) { + continue; + } + + // Try to attach to an existing REASON_CRASH_NATIVE. + final int outputSize = output.size(); + for (int j = 0; j < outputSize; ++j) { + ApplicationExitInfo exitInfo = output.get(j); + if (tombstone.matches(exitInfo)) { + exitInfo.setNativeTombstoneRetriever(tombstone.getPfdRetriever()); + continue tombstoneIter; + } + } + + if (output.size() < maxNum) { + appendedTombstones = true; + output.add(tombstone.toAppExitInfo()); + } + } + } + } + + if (appendedTombstones) { + Collections.sort(output, (lhs, rhs) -> { + // Reports should be ordered with newest reports first. + long diff = rhs.getTimestamp() - lhs.getTimestamp(); + if (diff < 0) { + return -1; + } else if (diff == 0) { + return 0; + } else { + return 1; + } + }); + } + future.complete(null); + }); + + try { + future.get(); + } catch (ExecutionException | InterruptedException ex) { + throw new RuntimeException(ex); + } + } + static class TombstoneFile { final ParcelFileDescriptor mPfd; - final @UserIdInt int mUserId; - final @AppIdInt int mAppId; + @UserIdInt int mUserId; + @AppIdInt int mAppId; + + int mPid; + int mUid; + String mProcessName; + @CurrentTimeMillisLong long mTimestampMs; + String mCrashReason; boolean mPurged = false; + final IParcelFileDescriptorRetriever mRetriever = new ParcelFileDescriptorRetriever(); - TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) { + TombstoneFile(ParcelFileDescriptor pfd) { mPfd = pfd; - mUserId = userId; - mAppId = appId; } public boolean matches(Optional<Integer> userId, Optional<Integer> appId) { @@ -175,24 +343,90 @@ public final class NativeTombstoneManager { return true; } + public boolean matches(ApplicationExitInfo exitInfo) { + if (exitInfo.getReason() != REASON_CRASH_NATIVE) { + return false; + } + + if (exitInfo.getPid() != mPid) { + return false; + } + + if (exitInfo.getRealUid() != mUid) { + return false; + } + + if (Math.abs(exitInfo.getTimestamp() - mTimestampMs) > 1000) { + return false; + } + + return true; + } + public void dispose() { IoUtils.closeQuietly(mPfd); } + public void purge() { + if (!mPurged) { + // There's no way to atomically unlink a specific file for which we have an fd from + // a path, which means that we can't safely delete a tombstone without coordination + // with tombstoned (which has a risk of deadlock if for example, system_server hangs + // with a flock). Do the next best thing, and just truncate the file. + // + // We don't have to worry about inflicting a SIGBUS on a process that has the + // tombstone mmaped, because we only clear if the package has been removed, which + // means no one with access to the tombstone should be left. + try { + Os.ftruncate(mPfd.getFileDescriptor(), 0); + } catch (ErrnoException ex) { + Slog.e(TAG, "Failed to truncate tombstone", ex); + } + mPurged = true; + } + } + static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) { final FileInputStream is = new FileInputStream(pfd.getFileDescriptor()); final ProtoInputStream stream = new ProtoInputStream(is); + int pid = 0; int uid = 0; + String processName = ""; + String crashReason = ""; String selinuxLabel = ""; try { while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (stream.getFieldNumber()) { + case (int) Tombstone.PID: + pid = stream.readInt(Tombstone.PID); + break; + case (int) Tombstone.UID: uid = stream.readInt(Tombstone.UID); break; + case (int) Tombstone.PROCESS_NAME: + processName = stream.readString(Tombstone.PROCESS_NAME); + break; + + case (int) Tombstone.CAUSE: + long token = stream.start(Tombstone.CAUSE); + cause: + while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (stream.getFieldNumber()) { + case (int) Cause.HUMAN_READABLE: + crashReason = stream.readString(Cause.HUMAN_READABLE); + break cause; + + default: + break; + } + } + stream.end(token); + break; + case (int) Tombstone.SELINUX_LABEL: selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL); break; @@ -201,7 +435,7 @@ public final class NativeTombstoneManager { break; } } - } catch (IOException ex) { + } catch (IOException | ProtoParseException ex) { Slog.e(TAG, "Failed to parse tombstone", ex); return Optional.empty(); } @@ -211,6 +445,14 @@ public final class NativeTombstoneManager { return Optional.empty(); } + long timestampMs = 0; + try { + StructStat stat = Os.fstat(pfd.getFileDescriptor()); + timestampMs = stat.st_atim.tv_sec * 1000 + stat.st_atim.tv_nsec / 1000000; + } catch (ErrnoException ex) { + Slog.e(TAG, "Failed to get timestamp of tombstone", ex); + } + final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); @@ -219,7 +461,74 @@ public final class NativeTombstoneManager { return Optional.empty(); } - return Optional.of(new TombstoneFile(pfd, userId, appId)); + TombstoneFile result = new TombstoneFile(pfd); + + result.mUserId = userId; + result.mAppId = appId; + result.mPid = pid; + result.mUid = uid; + result.mProcessName = processName; + result.mTimestampMs = timestampMs; + result.mCrashReason = crashReason; + + return Optional.of(result); + } + + public IParcelFileDescriptorRetriever getPfdRetriever() { + return mRetriever; + } + + public ApplicationExitInfo toAppExitInfo() { + ApplicationExitInfo info = new ApplicationExitInfo(); + info.setPid(mPid); + info.setRealUid(mUid); + info.setPackageUid(mUid); + info.setDefiningUid(mUid); + info.setProcessName(mProcessName); + info.setReason(ApplicationExitInfo.REASON_CRASH_NATIVE); + + // Signal numbers are architecture-specific! + // We choose to provide nothing here, to avoid leading users astray. + info.setStatus(0); + + // No way for us to find out. + info.setImportance(RunningAppProcessInfo.IMPORTANCE_GONE); + info.setPackageName(""); + info.setProcessStateSummary(null); + + // We could find out, but they didn't get OOM-killed... + info.setPss(0); + info.setRss(0); + + info.setTimestamp(mTimestampMs); + info.setDescription(mCrashReason); + + info.setSubReason(ApplicationExitInfo.SUBREASON_UNKNOWN); + info.setNativeTombstoneRetriever(mRetriever); + + return info; + } + + + class ParcelFileDescriptorRetriever extends IParcelFileDescriptorRetriever.Stub { + ParcelFileDescriptorRetriever() {} + + public @Nullable ParcelFileDescriptor getPfd() { + if (mPurged) { + return null; + } + + // Reopen the file descriptor as read-only. + try { + final String path = "/proc/self/fd/" + mPfd.getFd(); + ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(path), + MODE_READ_ONLY); + return pfd; + } catch (FileNotFoundException ex) { + Slog.e(TAG, "failed to reopen file descriptor as read-only", ex); + return null; + } + } } } diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 5d7c41c7b08f..1acbabda9e19 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -69,6 +69,7 @@ import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; import java.util.concurrent.Executor; +import java.util.function.Function; /** * The entity responsible for filtering visibility between apps based on declarations in their @@ -1354,14 +1355,13 @@ public class AppsFilter implements Watchable, Snappable { } public void dumpQueries( - PrintWriter pw, PackageManagerService pms, @Nullable Integer filteringAppId, - DumpState dumpState, - int[] users) { + PrintWriter pw, @Nullable Integer filteringAppId, DumpState dumpState, int[] users, + Function<Integer, String[]> getPackagesForUid) { final SparseArray<String> cache = new SparseArray<>(); ToString<Integer> expandPackages = input -> { String cachedValue = cache.get(input); if (cachedValue == null) { - final String[] packagesForUid = pms.getPackagesForUid(input); + final String[] packagesForUid = getPackagesForUid.apply(input); if (packagesForUid == null) { cachedValue = "[unknown app id " + input + "]"; } else { diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java index 308e815d7659..9a9b14c31314 100644 --- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java +++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java @@ -171,7 +171,7 @@ public class DataLoaderManagerService extends SystemService { } } - private class DataLoaderServiceConnection implements ServiceConnection { + private class DataLoaderServiceConnection implements ServiceConnection, IBinder.DeathRecipient { final int mId; final IDataLoaderStatusListener mListener; IDataLoader mDataLoader; @@ -194,6 +194,13 @@ public class DataLoaderManagerService extends SystemService { mContext.unbindService(this); return; } + try { + service.linkToDeath(this, /*flags=*/0); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to link to DataLoader's death: " + mId, e); + onBindingDied(className); + return; + } callListener(IDataLoaderStatusListener.DATA_LOADER_BOUND); } @@ -218,6 +225,13 @@ public class DataLoaderManagerService extends SystemService { destroy(); } + @Override + public void binderDied() { + Slog.i(TAG, "DataLoader " + mId + " died"); + callListener(IDataLoaderStatusListener.DATA_LOADER_DESTROYED); + destroy(); + } + IDataLoader getDataLoader() { return mDataLoader; } diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java index 2a1fc87fc29f..6875b8a5abeb 100644 --- a/services/core/java/com/android/server/pm/DumpState.java +++ b/services/core/java/com/android/server/pm/DumpState.java @@ -55,6 +55,10 @@ public final class DumpState { private int mOptions; private boolean mTitlePrinted; + private boolean mFullPreferred; + private boolean mCheckIn; + + private String mTargetPackageName; private SharedUserSetting mSharedUser; @@ -99,4 +103,28 @@ public final class DumpState { public void setSharedUser(SharedUserSetting user) { mSharedUser = user; } + + public String getTargetPackageName() { + return mTargetPackageName; + } + + public void setTargetPackageName(String packageName) { + mTargetPackageName = packageName; + } + + public boolean isFullPreferred() { + return mFullPreferred; + } + + public void setFullPreferred(boolean fullPreferred) { + mFullPreferred = fullPreferred; + } + + public boolean isCheckIn() { + return mCheckIn; + } + + public void setCheckIn(boolean checkIn) { + mCheckIn = checkIn; + } } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index b9e3e0f4450b..0a443f3fd7f9 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -780,7 +780,8 @@ public class PackageDexOptimizer { return getOatDir(codePath).getAbsolutePath(); } - static File getOatDir(File codePath) { + /** Returns the oat dir for the given code path */ + public static File getOatDir(File codePath) { return new File(codePath, OAT_DIR_NAME); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 281283048a95..a5e28f164bc2 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -530,14 +530,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements throw new SecurityException("User restriction prevents installing"); } - if (params.dataLoaderParams != null - && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("You need the " - + "com.android.permission.USE_INSTALLER_V2 permission " - + "to use a data loader"); - } - // INSTALL_REASON_ROLLBACK allows an app to be rolled back without requiring the ROLLBACK // capability; ensure if this is set as the install reason the app has one of the necessary // signature permissions to perform the rollback. diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 0ce26739b51c..460b2f2bf5c6 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -70,6 +70,7 @@ import android.content.pm.IDataLoaderStatusListener; import android.content.pm.IPackageInstallObserver2; import android.content.pm.IPackageInstallerSession; import android.content.pm.IPackageInstallerSessionFileSystemConnector; +import android.content.pm.IPackageLoadingProgressCallback; import android.content.pm.InstallationFile; import android.content.pm.InstallationFileParcel; import android.content.pm.PackageInfo; @@ -321,6 +322,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private float mProgress = 0; @GuardedBy("mLock") private float mReportedProgress = -1; + @GuardedBy("mLock") + private float mIncrementalProgress = 0; /** State of the session. */ @GuardedBy("mLock") @@ -778,7 +781,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private File mInheritedFilesBase; @GuardedBy("mLock") - private boolean mVerityFound; + private boolean mVerityFoundForApks; /** * Both flags should be guarded with mLock whenever changes need to be in lockstep. @@ -1007,9 +1010,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new IllegalArgumentException( "DataLoader installation of APEX modules is not allowed."); } + if (this.params.dataLoaderParams.getComponentName().getPackageName() - == SYSTEM_DATA_LOADER_PACKAGE) { - assertShellOrSystemCalling("System data loaders"); + == SYSTEM_DATA_LOADER_PACKAGE && mContext.checkCallingOrSelfPermission( + Manifest.permission.USE_SYSTEM_DATA_LOADERS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("You need the " + + "com.android.permission.USE_SYSTEM_DATA_LOADERS permission " + + "to use system data loaders"); } } @@ -1199,8 +1207,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private void computeProgressLocked(boolean forcePublish) { - mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) - + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); + if (!mCommitted) { + mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) + + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); + } else { + // For incremental installs, continue publishing the install progress during committing. + mProgress = mIncrementalProgress; + } // Only publish when meaningful change if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) { @@ -1936,9 +1949,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); } - // Client staging is fully done at this point - mClientProgress = 1f; - computeProgressLocked(true); + if (!isIncrementalInstallation()) { + // For non-incremental installs, client staging is fully done at this point + mClientProgress = 1f; + computeProgressLocked(true); + } // This ongoing commit should keep session active, even though client // will probably close their end. @@ -2714,8 +2729,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Missing existing base package"); } - // Default to require only if existing base has fs-verity. - mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled() + // Default to require only if existing base apk has fs-verity. + mVerityFoundForApks = PackageManagerServiceUtils.isApkVerityEnabled() && params.mode == SessionParams.MODE_INHERIT_EXISTING && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath()); @@ -3010,34 +3025,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @GuardedBy("mLock") - private void maybeStageFsveritySignatureLocked(File origFile, File targetFile) - throws PackageManagerException { + private void maybeStageFsveritySignatureLocked(File origFile, File targetFile, + boolean fsVerityRequired) throws PackageManagerException { final File originalSignature = new File( VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); - // Make sure .fsv_sig exists when it should, then resolve and stage it. if (originalSignature.exists()) { - // mVerityFound can only change from false to true here during the staging loop. Since - // all or none of files should have .fsv_sig, this should only happen in the first time - // (or never), otherwise bail out. - if (!mVerityFound) { - mVerityFound = true; - if (mResolvedStagedFiles.size() > 1) { - throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, - "Some file is missing fs-verity signature"); - } - } - } else { - if (!mVerityFound) { - return; - } + final File stagedSignature = new File( + VerityUtils.getFsveritySignatureFilePath(targetFile.getPath())); + stageFileLocked(originalSignature, stagedSignature); + } else if (fsVerityRequired) { throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, "Missing corresponding fs-verity signature to " + origFile); } - - final File stagedSignature = new File( - VerityUtils.getFsveritySignatureFilePath(targetFile.getPath())); - - stageFileLocked(originalSignature, stagedSignature); } @GuardedBy("mLock") @@ -3056,7 +3055,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { DexMetadataHelper.buildDexMetadataPathForApk(targetFile.getName())); stageFileLocked(dexMetadataFile, targetDexMetadataFile); - maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile); + + // Also stage .dm.fsv_sig. .dm may be required to install with fs-verity signature on + // supported on older devices. + maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile, + VerityUtils.isFsVeritySupported() && DexMetadataHelper.isFsVerityRequired()); } private void storeBytesToInstallationFile(final String localPath, final String absolutePath, @@ -3118,13 +3121,45 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @GuardedBy("mLock") + private boolean isFsVerityRequiredForApk(File origFile, File targetFile) + throws PackageManagerException { + if (mVerityFoundForApks) { + return true; + } + + // We haven't seen .fsv_sig for any APKs. Treat it as not required until we see one. + final File originalSignature = new File( + VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); + if (!originalSignature.exists()) { + return false; + } + mVerityFoundForApks = true; + + // When a signature is found, also check any previous staged APKs since they also need to + // have fs-verity signature consistently. + for (File file : mResolvedStagedFiles) { + if (!file.getName().endsWith(".apk")) { + continue; + } + // Ignore the current targeting file. + if (targetFile.getName().equals(file.getName())) { + continue; + } + throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, + "Previously staged apk is missing fs-verity signature"); + } + return true; + } + + @GuardedBy("mLock") private void resolveAndStageFileLocked(File origFile, File targetFile, String splitName) throws PackageManagerException { stageFileLocked(origFile, targetFile); - // Stage fsverity signature if present. - maybeStageFsveritySignatureLocked(origFile, targetFile); - // Stage dex metadata (.dm) if present. + // Stage APK's fs-verity signature if present. + maybeStageFsveritySignatureLocked(origFile, targetFile, + isFsVerityRequiredForApk(origFile, targetFile)); + // Stage dex metadata (.dm) and corresponding fs-verity signature if present. maybeStageDexMetadataLocked(origFile, targetFile); // Stage checksums (.digests) if present. maybeStageDigestsLocked(origFile, targetFile, splitName); @@ -3512,14 +3547,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public DataLoaderParamsParcel getDataLoaderParams() { - mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null; } @Override public void addFile(int location, String name, long lengthBytes, byte[] metadata, byte[] signature) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); if (!isDataLoaderInstallation()) { throw new IllegalStateException( "Cannot add files to non-data loader installation session."); @@ -3552,7 +3585,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void removeFile(int location, String name) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); if (!isDataLoaderInstallation()) { throw new IllegalStateException( "Cannot add files to non-data loader installation session."); @@ -3770,7 +3802,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir, inheritedDir, params, statusListener, healthCheckParams, healthListener, - addedFiles, perUidReadTimeouts); + addedFiles, perUidReadTimeouts, + new IPackageLoadingProgressCallback.Stub() { + @Override + public void onPackageLoadingProgressChanged(float progress) { + synchronized (mLock) { + mIncrementalProgress = progress; + computeProgressLocked(true); + } + } + }); return false; } catch (IOException e) { throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(), diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index e6789d4ba8ac..af2fdf70ca50 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -97,6 +97,10 @@ import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE; import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4; import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile; +import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED; +import static android.os.PowerWhitelistManager.REASON_PACKAGE_REPLACED; +import static android.os.PowerWhitelistManager.REASON_PACKAGE_VERIFIER; +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; @@ -270,6 +274,7 @@ import android.os.Parcel; import android.os.ParcelableException; import android.os.PatternMatcher; import android.os.PersistableBundle; +import android.os.PowerWhitelistManager; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -316,7 +321,6 @@ import android.util.ExceptionUtils; import android.util.IntArray; import android.util.Log; import android.util.LogPrinter; -import android.util.LongSparseArray; import android.util.LongSparseLongArray; import android.util.MathUtils; import android.util.PackageUtils; @@ -370,6 +374,7 @@ import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.Settings.DatabaseVersion; import com.android.server.pm.Settings.VersionInfo; import com.android.server.pm.dex.ArtManagerService; +import com.android.server.pm.dex.ArtUtils; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.PackageDexUsage; @@ -389,6 +394,7 @@ import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; import com.android.server.pm.verify.domain.DomainVerificationService; +import com.android.server.pm.verify.domain.DomainVerificationUtils; import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1; import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2; @@ -400,6 +406,7 @@ import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.utils.Watchable; import com.android.server.utils.Watched; import com.android.server.utils.WatchedArrayMap; +import com.android.server.utils.WatchedLongSparseArray; import com.android.server.utils.WatchedSparseBooleanArray; import com.android.server.utils.Watcher; import com.android.server.wm.ActivityTaskManagerInternal; @@ -1401,10 +1408,10 @@ public class PackageManagerService extends IPackageManager.Stub // Currently known shared libraries. @Watched - final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries = - new WatchedArrayMap<>(); + final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> + mSharedLibraries = new WatchedArrayMap<>(); @Watched - final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>> + final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> mStaticLibsByDeclaringPackage = new WatchedArrayMap<>(); // Mapping from instrumentation class names to info about them. @@ -1755,6 +1762,11 @@ public class PackageManagerService extends IPackageManager.Stub public boolean filterAppAccess(String packageName, int callingUid, int userId) { return mPmInternal.filterAppAccess(packageName, callingUid, userId); } + + @Override + public int[] getAllUserIds() { + return mUserManager.getUserIds(); + } } /** @@ -1785,8 +1797,8 @@ public class PackageManagerService extends IPackageManager.Stub public final Settings settings; public final SparseIntArray isolatedOwners; public final WatchedArrayMap<String, AndroidPackage> packages; - public final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>> sharedLibs; - public final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>> staticLibs; + public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibs; + public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> staticLibs; public final WatchedArrayMap<ComponentName, ParsedInstrumentation> instrumentation; public final WatchedSparseBooleanArray webInstantAppsDisabled; public final ComponentName resolveComponentName; @@ -1954,9 +1966,10 @@ public class PackageManagerService extends IPackageManager.Stub boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid); boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId, int callingUid); boolean isInstantAppResolutionAllowed(Intent intent, List<ResolveInfo> resolvedActivities, - int userId, boolean skipPackageCheck); + int userId, boolean skipPackageCheck, int flags); boolean isInstantAppResolutionAllowedBody(Intent intent, - List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck); + List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck, + int flags); boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId, String resolvedType, int flags); boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId); @@ -1988,6 +2001,7 @@ public class PackageManagerService extends IPackageManager.Stub SigningDetails getSigningDetails(int uid); boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId); boolean filterAppAccess(String packageName, int callingUid, int userId); + void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState); } /** @@ -2004,9 +2018,9 @@ public class PackageManagerService extends IPackageManager.Stub private final WatchedArrayMap<String, AndroidPackage> mPackages; private final WatchedArrayMap<ComponentName, ParsedInstrumentation> mInstrumentation; - private final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>> + private final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> mStaticLibsByDeclaringPackage; - private final WatchedArrayMap<String, LongSparseArray<SharedLibraryInfo>> + private final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> mSharedLibraries; private final ComponentName mLocalResolveComponentName; private final ActivityInfo mResolveActivity; @@ -2031,6 +2045,9 @@ public class PackageManagerService extends IPackageManager.Stub private final InstantAppResolverConnection mInstantAppResolverConnection; private final DefaultAppProvider mDefaultAppProvider; private final DomainVerificationManagerInternal mDomainVerificationManager; + private final PackageDexOptimizer mPackageDexOptimizer; + private final DexManager mDexManager; + private final CompilerStats mCompilerStats; // PackageManagerService attributes that are primitives are referenced through the // pms object directly. Primitives are the only attributes so referenced. @@ -2077,6 +2094,9 @@ public class PackageManagerService extends IPackageManager.Stub mInstantAppResolverConnection = args.service.mInstantAppResolverConnection; mDefaultAppProvider = args.service.mDefaultAppProvider; mDomainVerificationManager = args.service.mDomainVerificationManager; + mPackageDexOptimizer = args.service.mPackageDexOptimizer; + mDexManager = args.service.mDexManager; + mCompilerStats = args.service.mCompilerStats; // Used to reference PMS attributes that are primitives and which are not // updated under control of the PMS lock. @@ -2308,7 +2328,7 @@ public class PackageManagerService extends IPackageManager.Stub result = filterIfNotSystemUser(mComponentResolver.queryActivities( intent, resolvedType, flags, userId), userId); addInstant = isInstantAppResolutionAllowed(intent, result, userId, - false /*skipPackageCheck*/); + false /*skipPackageCheck*/, flags); // Check for cross profile results. boolean hasNonNegativePriorityResult = hasNonNegativePriority(result); xpResolveInfo = queryCrossProfileIntents( @@ -2373,8 +2393,8 @@ public class PackageManagerService extends IPackageManager.Stub if (result == null || result.size() == 0) { // the caller wants to resolve for a particular package; however, there // were no installed results, so, try to find an ephemeral result - addInstant = isInstantAppResolutionAllowed( - intent, null /*result*/, userId, true /*skipPackageCheck*/); + addInstant = isInstantAppResolutionAllowed(intent, null /*result*/, userId, + true /*skipPackageCheck*/, flags); if (result == null) { result = new ArrayList<>(); } @@ -2587,40 +2607,68 @@ public class PackageManagerService extends IPackageManager.Stub CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) { final ArrayList<ResolveInfo> result = new ArrayList<>(); final ArrayList<ResolveInfo> matchAllList = new ArrayList<>(); + final ArrayList<ResolveInfo> undefinedList = new ArrayList<>(); + + // Blocking instant apps is usually done in applyPostResolutionFilter, but since + // domain verification can resolve to a single result, which can be an instant app, + // it will then be filtered to an empty list in that method. Instead, do blocking + // here so that instant apps can be ignored for approval filtering and a lower + // priority result chosen instead. + final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId); final int count = candidates.size(); // First, try to use approved apps. for (int n = 0; n < count; n++) { ResolveInfo info = candidates.get(n); + if (blockInstant && (info.isInstantAppAvailable + || isInstantApp(info.activityInfo.packageName, userId))) { + continue; + } + // Add to the special match all list (Browser use case) if (info.handleAllWebDataURI) { matchAllList.add(info); + } else { + undefinedList.add(info); } } - Pair<List<ResolveInfo>, Integer> infosAndLevel = mDomainVerificationManager - .filterToApprovedApp(intent, candidates, userId, mSettings::getPackageLPr); - List<ResolveInfo> approvedInfos = infosAndLevel.first; - Integer highestApproval = infosAndLevel.second; - // We'll want to include browser possibilities in a few cases boolean includeBrowser = false; - // If no apps are approved for the domain, resolve only to browsers - if (approvedInfos.isEmpty()) { - // If the other profile has a result, include that and delegate to ResolveActivity + if (!DomainVerificationUtils.isDomainVerificationIntent(intent, matchFlags)) { + result.addAll(undefinedList); + // Maybe add one for the other profile. if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) { result.add(xpDomainInfo.resolveInfo); - } else { - includeBrowser = true; } + includeBrowser = true; } else { - result.addAll(approvedInfos); + Pair<List<ResolveInfo>, Integer> infosAndLevel = mDomainVerificationManager + .filterToApprovedApp(intent, undefinedList, userId, + mSettings::getPackageLPr); + List<ResolveInfo> approvedInfos = infosAndLevel.first; + Integer highestApproval = infosAndLevel.second; + + // If no apps are approved for the domain, resolve only to browsers + if (approvedInfos.isEmpty()) { + // If the other profile has a result, include that and delegate to + // ResolveActivity + if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel + > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) { + result.add(xpDomainInfo.resolveInfo); + } else { + includeBrowser = true; + } + } else { + result.addAll(approvedInfos); - // If the other profile has an app that's of equal or higher approval, add it - if (xpDomainInfo != null && xpDomainInfo.highestApprovalLevel >= highestApproval) { - result.add(xpDomainInfo.resolveInfo); + // If the other profile has an app that's of equal or higher approval, add it + if (xpDomainInfo != null + && xpDomainInfo.highestApprovalLevel >= highestApproval) { + result.add(xpDomainInfo.resolveInfo); + } } } @@ -2772,7 +2820,7 @@ public class PackageManagerService extends IPackageManager.Stub } result.highestApprovalLevel = Math.max(mDomainVerificationManager - .approvalLevelForDomain(ps, intent, riTargetUser.targetUserId), + .approvalLevelForDomain(ps, intent, flags, riTargetUser.targetUserId), result.highestApprovalLevel); } if (result != null && result.highestApprovalLevel @@ -3019,7 +3067,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = info.activityInfo.packageName; final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps.getInstantApp(userId)) { - if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, + if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags, userId)) { if (DEBUG_INSTANT) { Slog.v(TAG, "Instant app approved for intent; pkg: " @@ -3529,7 +3577,7 @@ public class PackageManagerService extends IPackageManager.Stub packageName = normalizedPackageName != null ? normalizedPackageName : packageName; // Is this a static library? - LongSparseArray<SharedLibraryInfo> versionedLib = + WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mStaticLibsByDeclaringPackage.get(packageName); if (versionedLib == null || versionedLib.size() <= 0) { return packageName; @@ -3898,7 +3946,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean isInstantAppResolutionAllowed( Intent intent, List<ResolveInfo> resolvedActivities, int userId, - boolean skipPackageCheck) { + boolean skipPackageCheck, int flags) { if (mInstantAppResolverConnection == null) { return false; } @@ -3931,14 +3979,14 @@ public class PackageManagerService extends IPackageManager.Stub // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. // Or if there's already an ephemeral app installed that handles the action return isInstantAppResolutionAllowedBody(intent, resolvedActivities, userId, - skipPackageCheck); + skipPackageCheck, flags); } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. // Or if there's already an ephemeral app installed that handles the action public boolean isInstantAppResolutionAllowedBody( Intent intent, List<ResolveInfo> resolvedActivities, int userId, - boolean skipPackageCheck) { + boolean skipPackageCheck, int flags) { final int count = (resolvedActivities == null ? 0 : resolvedActivities.size()); for (int n = 0; n < count; n++) { final ResolveInfo info = resolvedActivities.get(n); @@ -3947,7 +3995,7 @@ public class PackageManagerService extends IPackageManager.Stub if (ps != null) { // only check domain verification status if the app is not a browser if (!info.handleAllWebDataURI) { - if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, + if (hasAnyDomainApproval(mDomainVerificationManager, ps, intent, flags, userId)) { if (DEBUG_INSTANT) { Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName @@ -4371,6 +4419,194 @@ public class PackageManagerService extends IPackageManager.Stub userId); } + public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) { + final String packageName = dumpState.getTargetPackageName(); + final boolean checkin = dumpState.isCheckIn(); + + switch (type) { + case DumpState.DUMP_VERSION: + { + if (dumpState.onTitlePrinted()) { + pw.println(); + } + pw.println("Database versions:"); + mSettings.dumpVersionLPr(new IndentingPrintWriter(pw, " ")); + break; + } + + case DumpState.DUMP_LIBS: + { + boolean printedHeader = false; + final int numSharedLibraries = mSharedLibraries.size(); + for (int index = 0; index < numSharedLibraries; index++) { + final String libName = mSharedLibraries.keyAt(index); + final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = + mSharedLibraries.get(libName); + if (versionedLib == null) { + continue; + } + final int versionCount = versionedLib.size(); + for (int i = 0; i < versionCount; i++) { + SharedLibraryInfo libraryInfo = versionedLib.valueAt(i); + if (!checkin) { + if (!printedHeader) { + if (dumpState.onTitlePrinted()) { + pw.println(); + } + pw.println("Libraries:"); + printedHeader = true; + } + pw.print(" "); + } else { + pw.print("lib,"); + } + pw.print(libraryInfo.getName()); + if (libraryInfo.isStatic()) { + pw.print(" version=" + libraryInfo.getLongVersion()); + } + if (!checkin) { + pw.print(" -> "); + } + if (libraryInfo.getPath() != null) { + if (libraryInfo.isNative()) { + pw.print(" (so) "); + } else { + pw.print(" (jar) "); + } + pw.print(libraryInfo.getPath()); + } else { + pw.print(" (apk) "); + pw.print(libraryInfo.getPackageName()); + } + pw.println(); + } + } + break; + } + + case DumpState.DUMP_PREFERRED_XML: + { + pw.flush(); + FileOutputStream fout = new FileOutputStream(fd); + BufferedOutputStream str = new BufferedOutputStream(fout); + TypedXmlSerializer serializer = Xml.newFastSerializer(); + try { + serializer.setOutput(str, StandardCharsets.UTF_8.name()); + serializer.startDocument(null, true); + serializer.setFeature( + "http://xmlpull.org/v1/doc/features.html#indent-output", true); + mSettings.writePreferredActivitiesLPr(serializer, 0, + dumpState.isFullPreferred()); + serializer.endDocument(); + serializer.flush(); + } catch (IllegalArgumentException e) { + pw.println("Failed writing: " + e); + } catch (IllegalStateException e) { + pw.println("Failed writing: " + e); + } catch (IOException e) { + pw.println("Failed writing: " + e); + } + break; + } + + case DumpState.DUMP_QUERIES: + { + final PackageSetting setting = mSettings.getPackageLPr(packageName); + Integer filteringAppId = setting == null ? null : setting.appId; + mAppsFilter.dumpQueries( + pw, filteringAppId, dumpState, mUserManager.getUserIds(), + this::getPackagesForUid); + break; + } + + case DumpState.DUMP_DOMAIN_PREFERRED: + { + final android.util.IndentingPrintWriter writer = + new android.util.IndentingPrintWriter(pw); + if (dumpState.onTitlePrinted()) pw.println(); + + writer.println("Domain verification status:"); + writer.increaseIndent(); + try { + mDomainVerificationManager.printState(writer, packageName, + UserHandle.USER_ALL, mSettings::getPackageLPr); + } catch (PackageManager.NameNotFoundException e) { + pw.println("Failure printing domain verification information"); + Slog.e(TAG, "Failure printing domain verification information", e); + } + writer.decreaseIndent(); + break; + } + + case DumpState.DUMP_DEXOPT: + { + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + ipw.println(); + ipw.println("Dexopt state:"); + ipw.increaseIndent(); + Collection<PackageSetting> pkgSettings; + if (packageName != null) { + PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName); + if (targetPkgSetting != null) { + pkgSettings = Collections.singletonList(targetPkgSetting); + } else { + ipw.println("Unable to find package: " + packageName); + return; + } + } else { + pkgSettings = mSettings.getPackagesLocked().values(); + } + + for (PackageSetting pkgSetting : pkgSettings) { + final AndroidPackage pkg = pkgSetting.getPkg(); + if (pkg == null) { + continue; + } + ipw.println("[" + pkgSetting.name + "]"); + ipw.increaseIndent(); + mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting, + mDexManager.getPackageUseInfoOrDefault(pkg.getPackageName())); + ipw.decreaseIndent(); + } + break; + } + + case DumpState.DUMP_COMPILER_STATS: + { + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + ipw.println(); + ipw.println("Compiler stats:"); + ipw.increaseIndent(); + Collection<AndroidPackage> packages; + if (packageName != null) { + AndroidPackage targetPackage = mPackages.get(packageName); + if (targetPackage != null) { + packages = Collections.singletonList(targetPackage); + } else { + ipw.println("Unable to find package: " + packageName); + return; + } + } else { + packages = mPackages.values(); + } + + for (AndroidPackage pkg : packages) { + final String pkgName = pkg.getPackageName(); + ipw.println("[" + pkgName + "]"); + ipw.increaseIndent(); + + CompilerStats.PackageStats stats = mCompilerStats.getPackageStats(pkgName); + if (stats == null) { + ipw.println("(No recorded stats)"); + } else { + stats.dump(ipw); + } + ipw.decreaseIndent(); + } + break; + } + } // switch + } } /** @@ -4398,7 +4634,7 @@ public class PackageManagerService extends IPackageManager.Stub * computer engine. This is required because there are no locks taken in * the engine itself. */ - private static class ComputerLocked extends ComputerEngine { + private static class ComputerLocked extends ComputerEngineLive { private final Object mLock; ComputerLocked(Snapshot args) { @@ -4406,16 +4642,6 @@ public class PackageManagerService extends IPackageManager.Stub mLock = mService.mLock; } - protected ComponentName resolveComponentName() { - return mService.mResolveComponentName; - } - protected ActivityInfo instantAppInstallerActivity() { - return mService.mInstantAppInstallerActivity; - } - protected ApplicationInfo androidApplication() { - return mService.mAndroidApplication; - } - public @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent, String resolvedType, int flags, int userId, int callingUid, String instantAppPkgName) { @@ -4509,10 +4735,11 @@ public class PackageManagerService extends IPackageManager.Stub } } public boolean isInstantAppResolutionAllowedBody(Intent intent, - List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck) { + List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck, + int flags) { synchronized (mLock) { return super.isInstantAppResolutionAllowedBody(intent, resolvedActivities, userId, - skipPackageCheck); + skipPackageCheck, flags); } } public int getPackageUidInternal(String packageName, int flags, int userId, @@ -4544,8 +4771,9 @@ public class PackageManagerService extends IPackageManager.Stub } - // Compute read-only functions, based on live data. - private final Computer mLiveComputer; + // Compute read-only functions, based on live data. This attribute may be modified multiple + // times during the PackageManagerService constructor but it should not be modified thereafter. + private Computer mLiveComputer; // A lock-free cache for frequently called functions. private volatile Computer mSnapshotComputer; // If true, the snapshot is invalid (stale). The attribute is static since it may be @@ -4684,6 +4912,10 @@ public class PackageManagerService extends IPackageManager.Stub // and an image with the flag set false does not use snapshots. private static final boolean SNAPSHOT_ENABLED = true; + // The per-instance snapshot disable/enable flag. This is generally set to false in + // test instances and set to SNAPSHOT_ENABLED in operational instances. + private final boolean mSnapshotEnabled; + /** * Return the live computer. */ @@ -4696,7 +4928,7 @@ public class PackageManagerService extends IPackageManager.Stub * The live computer will be returned if snapshots are disabled. */ private Computer snapshotComputer() { - if (!SNAPSHOT_ENABLED) { + if (!mSnapshotEnabled) { return mLiveComputer; } if (Thread.holdsLock(mLock)) { @@ -4868,23 +5100,12 @@ public class PackageManagerService extends IPackageManager.Stub InstallArgs args = data.args; PackageInstalledInfo parentRes = data.res; - final boolean grantPermissions = (args.installFlags - & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0; final boolean killApp = (args.installFlags & PackageManager.INSTALL_DONT_KILL_APP) == 0; final boolean virtualPreload = ((args.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0); - final String[] grantedPermissions = args.installGrantPermissions; - final List<String> whitelistedRestrictedPermissions = ((args.installFlags - & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0 - && parentRes.pkg != null) - ? parentRes.pkg.getRequestedPermissions() - : args.whitelistedRestrictedPermissions; - int autoRevokePermissionsMode = args.autoRevokePermissionsMode; - - handlePackagePostInstall(parentRes, grantPermissions, - killApp, virtualPreload, grantedPermissions, - whitelistedRestrictedPermissions, autoRevokePermissionsMode, + + handlePackagePostInstall(parentRes, killApp, virtualPreload, didRestore, args.installSource.installerPackageName, args.observer, args.mDataLoaderType); @@ -5163,11 +5384,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions, - boolean killApp, boolean virtualPreload, - String[] grantedPermissions, List<String> allowlistedRestrictedPermissions, - int autoRevokePermissionsMode, - boolean launchedForRestore, String installerPackage, + private void handlePackagePostInstall(PackageInstalledInfo res, boolean killApp, + boolean virtualPreload, boolean launchedForRestore, String installerPackage, IPackageInstallObserver2 installObserver, int dataLoaderType) { boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED; final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null; @@ -5198,29 +5416,6 @@ public class PackageManagerService extends IPackageManager.Stub res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/); } - final PermissionManagerServiceInternal.PackageInstalledParams.Builder - permissionParamsBuilder = - new PermissionManagerServiceInternal.PackageInstalledParams.Builder(); - final List<String> grantedPermissionsList; - if (grantPermissions) { - if (grantedPermissions != null) { - permissionParamsBuilder.setGrantedPermissions(Arrays.asList( - grantedPermissions)); - } else { - permissionParamsBuilder.setGrantedPermissions( - res.pkg.getRequestedPermissions()); - } - } - if (allowlistedRestrictedPermissions != null) { - permissionParamsBuilder.setAllowlistedRestrictedPermissions( - allowlistedRestrictedPermissions); - } - permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode); - for (final int userId : res.newUsers) { - mPermissionManager.onPackageInstalled(res.pkg, permissionParamsBuilder.build(), - userId); - } - final String installerPackageName = res.installerPackageName != null ? res.installerPackageName @@ -5353,7 +5548,8 @@ public class PackageManagerService extends IPackageManager.Stub packageName /*targetPackage*/, null /*finishedReceiver*/, updateUserIds, instantUserIds, null /*broadcastAllowList*/, - getTemporaryAppWhitelistBroadcastOptions().toBundle()); + getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED) + .toBundle()); } else if (launchedForRestore && !res.pkg.isSystem()) { // First-install and we did a restore, so we're responsible for the // first-launch broadcast. @@ -6031,15 +6227,12 @@ public class PackageManagerService extends IPackageManager.Stub mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage; mResolveComponentName = testParams.resolveComponentName; - // 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 - // constructor, at which time the invalidation method updates it. The cache is - // corked initially to ensure a cached computer is not built until the end of the - // constructor. - sSnapshotCorked = true; + // Disable snapshots in this instance of PackageManagerService, which is only used + // for testing. The instance still needs a live computer. The snapshot computer + // is set to null since it must never be used by this instance. + mSnapshotEnabled = false; mLiveComputer = createLiveComputer(); - mSnapshotComputer = mLiveComputer; - registerObserver(); + mSnapshotComputer = null; mPackages.putAll(testParams.packages); mEnableFreeCacheV2 = testParams.enableFreeCacheV2; @@ -6052,7 +6245,6 @@ public class PackageManagerService extends IPackageManager.Stub mIncrementalVersion = testParams.incrementalVersion; invalidatePackageInfoCache(); - sSnapshotCorked = false; } public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest, @@ -6198,9 +6390,11 @@ public class PackageManagerService extends IPackageManager.Stub // constructor, at which time the invalidation method updates it. The cache is // corked initially to ensure a cached computer is not built until the end of the // constructor. + mSnapshotEnabled = SNAPSHOT_ENABLED; sSnapshotCorked = true; + sSnapshotInvalid = true; mLiveComputer = createLiveComputer(); - mSnapshotComputer = mLiveComputer; + mSnapshotComputer = null; registerObserver(); // CHECKSTYLE:OFF IndentationCheck @@ -6879,6 +7073,10 @@ public class PackageManagerService extends IPackageManager.Stub BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME, SystemClock.uptimeMillis() - startTime); } + + // Rebild the live computer since some attributes have been rebuilt. + mLiveComputer = createLiveComputer(); + } // synchronized (mLock) } // synchronized (mInstallLock) // CHECKSTYLE:ON IndentationCheck @@ -8021,7 +8219,7 @@ public class PackageManagerService extends IPackageManager.Stub final int[] allUsers = mUserManager.getUserIds(); final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { - final LongSparseArray<SharedLibraryInfo> versionedLib + final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; @@ -8286,7 +8484,8 @@ public class PackageManagerService extends IPackageManager.Stub final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { - LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.valueAt(i); + WatchedLongSparseArray<SharedLibraryInfo> versionedLib = + mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } @@ -8355,7 +8554,8 @@ public class PackageManagerService extends IPackageManager.Stub int libraryCount = mSharedLibraries.size(); for (int i = 0; i < libraryCount; i++) { - LongSparseArray<SharedLibraryInfo> versionedLibrary = mSharedLibraries.valueAt(i); + WatchedLongSparseArray<SharedLibraryInfo> versionedLibrary = + mSharedLibraries.valueAt(i); if (versionedLibrary == null) { continue; } @@ -8516,7 +8716,8 @@ public class PackageManagerService extends IPackageManager.Stub Set<String> libs = null; final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { - LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.valueAt(i); + WatchedLongSparseArray<SharedLibraryInfo> versionedLib = + mSharedLibraries.valueAt(i); if (versionedLib == null) { continue; } @@ -8942,6 +9143,10 @@ public class PackageManagerService extends IPackageManager.Stub @Override public List<String> getAllPackages() { + // Allow iorapd to call this method. + if (Binder.getCallingUid() != Process.IORAPD_UID) { + enforceSystemOrRootOrShell("getAllPackages is limited to privileged callers"); + } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); synchronized (mLock) { @@ -8992,6 +9197,11 @@ public class PackageManagerService extends IPackageManager.Stub */ @Override public String[] getPackagesForUid(int uid) { + final int callingUid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(uid); + enforceCrossUserOrProfilePermission(callingUid, userId, + /* requireFullPermission */ false, + /* checkShell */ false, "getPackagesForUid"); return snapshotComputer().getPackagesForUid(uid); } @@ -9295,20 +9505,20 @@ public class PackageManagerService extends IPackageManager.Stub private boolean isInstantAppResolutionAllowed( Intent intent, List<ResolveInfo> resolvedActivities, int userId, - boolean skipPackageCheck) { + boolean skipPackageCheck, int flags) { return liveComputer().isInstantAppResolutionAllowed( intent, resolvedActivities, userId, - skipPackageCheck); + skipPackageCheck, flags); } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. // Or if there's already an ephemeral app installed that handles the action private boolean isInstantAppResolutionAllowedBody( Intent intent, List<ResolveInfo> resolvedActivities, int userId, - boolean skipPackageCheck) { + boolean skipPackageCheck, int flags) { return liveComputer().isInstantAppResolutionAllowedBody( intent, resolvedActivities, userId, - skipPackageCheck); + skipPackageCheck, flags); } private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, @@ -9365,7 +9575,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = ri.activityInfo.packageName; final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null && hasAnyDomainApproval(mDomainVerificationManager, ps, - intent, userId)) { + intent, flags, userId)) { return ri; } } @@ -9422,8 +9632,9 @@ public class PackageManagerService extends IPackageManager.Stub */ private static boolean hasAnyDomainApproval( @NonNull DomainVerificationManagerInternal manager, @NonNull PackageSetting pkgSetting, - @NonNull Intent intent, @UserIdInt int userId) { - return manager.approvalLevelForDomain(pkgSetting, intent, userId) + @NonNull Intent intent, @PackageManager.ResolveInfoFlags int resolveInfoFlags, + @UserIdInt int userId) { + return manager.approvalLevelForDomain(pkgSetting, intent, resolveInfoFlags, userId) > DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE; } @@ -9820,7 +10031,7 @@ public class PackageManagerService extends IPackageManager.Stub private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int userId) { - return liveComputer().queryIntentActivitiesInternal(intent, + return snapshotComputer().queryIntentActivitiesInternal(intent, resolvedType, flags, userId); } @@ -10307,7 +10518,7 @@ public class PackageManagerService extends IPackageManager.Stub private @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { - return liveComputer().queryIntentServicesInternal(intent, + return snapshotComputer().queryIntentServicesInternal(intent, resolvedType, flags, userId, callingUid, includeInstantApps); } @@ -12181,10 +12392,10 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable private static SharedLibraryInfo getSharedLibraryInfo(String name, long version, - Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries, - @Nullable Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries) { + Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries, + @Nullable Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) { if (newLibraries != null) { - final LongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name); + final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name); SharedLibraryInfo info = null; if (versionedLib != null) { info = versionedLib.get(version); @@ -12193,7 +12404,7 @@ public class PackageManagerService extends IPackageManager.Stub return info; } } - final LongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name); + final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name); if (versionedLib == null) { return null; } @@ -12201,7 +12412,7 @@ public class PackageManagerService extends IPackageManager.Stub } private SharedLibraryInfo getLatestSharedLibraVersionLPr(AndroidPackage pkg) { - LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get( + WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get( pkg.getStaticSharedLibName()); if (versionedLib == null) { return null; @@ -12506,8 +12717,8 @@ public class PackageManagerService extends IPackageManager.Stub private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(AndroidPackage pkg, Map<String, AndroidPackage> availablePackages, - @NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries, - @Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries) + @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries, + @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) throws PackageManagerException { if (pkg == null) { return null; @@ -12604,8 +12815,8 @@ public class PackageManagerService extends IPackageManager.Stub @NonNull String packageName, boolean required, int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries, @NonNull final Map<String, AndroidPackage> availablePackages, - @NonNull final Map<String, LongSparseArray<SharedLibraryInfo>> existingLibraries, - @Nullable final Map<String, LongSparseArray<SharedLibraryInfo>> newLibraries) + @NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries, + @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) throws PackageManagerException { final int libCount = requestedLibraries.size(); for (int i = 0; i < libCount; i++) { @@ -14026,7 +14237,7 @@ public class PackageManagerService extends IPackageManager.Stub long minVersionCode = Long.MIN_VALUE; long maxVersionCode = Long.MAX_VALUE; - LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get( + WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get( pkg.getStaticSharedLibName()); if (versionedLib != null) { final int versionCount = versionedLib.size(); @@ -14254,8 +14465,8 @@ public class PackageManagerService extends IPackageManager.Stub } private static boolean sharedLibExists(final String name, final long version, - Map<String, LongSparseArray<SharedLibraryInfo>> librarySource) { - LongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name); + Map<String, WatchedLongSparseArray<SharedLibraryInfo>> librarySource) { + WatchedLongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name); if (versionedLib != null && versionedLib.indexOfKey(version) >= 0) { return true; } @@ -14265,9 +14476,9 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") private void commitSharedLibraryInfoLocked(SharedLibraryInfo libraryInfo) { final String name = libraryInfo.getName(); - LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name); + WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name); if (versionedLib == null) { - versionedLib = new LongSparseArray<>(); + versionedLib = new WatchedLongSparseArray<>(); mSharedLibraries.put(name, versionedLib); } final String declaringPackageName = libraryInfo.getDeclaringPackage().getPackageName(); @@ -14278,7 +14489,7 @@ public class PackageManagerService extends IPackageManager.Stub } private boolean removeSharedLibraryLPw(String name, long version) { - LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name); + WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name); if (versionedLib == null) { return false; } @@ -14984,7 +15195,8 @@ public class PackageManagerService extends IPackageManager.Stub lockedBcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); } final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED}; - final BroadcastOptions bOptions = getTemporaryAppWhitelistBroadcastOptions(); + final BroadcastOptions bOptions = getTemporaryAppAllowlistBroadcastOptions( + REASON_LOCKED_BOOT_COMPLETED); am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null, requiredPermissions, android.app.AppOpsManager.OP_NONE, bOptions.toBundle(), false, false, @@ -17373,7 +17585,9 @@ public class PackageManagerService extends IPackageManager.Stub mInjector.getLocalService(DeviceIdleInternal.class); final long idleDuration = getVerificationTimeout(); final BroadcastOptions options = BroadcastOptions.makeBasic(); - options.setTemporaryAppWhitelistDuration(idleDuration); + options.setTemporaryAppAllowlist(idleDuration, + TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + REASON_PACKAGE_VERIFIER, ""); /* * If any sufficient verifiers were listed in the package @@ -17389,7 +17603,8 @@ public class PackageManagerService extends IPackageManager.Stub final ComponentName verifierComponent = sufficientVerifiers.get(i); idleController.addPowerSaveTempWhitelistApp(Process.myUid(), verifierComponent.getPackageName(), idleDuration, - verifierUser.getIdentifier(), false, "package verifier"); + verifierUser.getIdentifier(), false, + REASON_PACKAGE_VERIFIER,"package verifier"); final Intent sufficientIntent = new Intent(verification); sufficientIntent.setComponent(verifierComponent); @@ -17411,7 +17626,8 @@ public class PackageManagerService extends IPackageManager.Stub verification.setComponent(requiredVerifierComponent); idleController.addPowerSaveTempWhitelistApp(Process.myUid(), mRequiredVerifierPackage, idleDuration, - verifierUser.getIdentifier(), false, "package verifier"); + verifierUser.getIdentifier(), false, + REASON_PACKAGE_VERIFIER, "package verifier"); mContext.sendOrderedBroadcastAsUser(verification, verifierUser, android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, /* appOp= */ AppOpsManager.OP_NONE, @@ -18211,6 +18427,37 @@ public class PackageManagerService extends IPackageManager.Stub } mSettings.writeKernelMappingLPr(ps); + + final PermissionManagerServiceInternal.PackageInstalledParams.Builder + permissionParamsBuilder = + new PermissionManagerServiceInternal.PackageInstalledParams.Builder(); + final boolean grantPermissions = (installArgs.installFlags + & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0; + if (grantPermissions) { + final List<String> grantedPermissions = + installArgs.installGrantPermissions != null + ? Arrays.asList(installArgs.installGrantPermissions) + : pkg.getRequestedPermissions(); + permissionParamsBuilder.setGrantedPermissions(grantedPermissions); + } + final boolean allowlistAllRestrictedPermissions = + (installArgs.installFlags + & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0; + final List<String> allowlistedRestrictedPermissions = + allowlistAllRestrictedPermissions ? pkg.getRequestedPermissions() + : installArgs.whitelistedRestrictedPermissions; + if (allowlistedRestrictedPermissions != null) { + permissionParamsBuilder.setAllowlistedRestrictedPermissions( + allowlistedRestrictedPermissions); + } + final int autoRevokePermissionsMode = installArgs.autoRevokePermissionsMode; + permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode); + for (int currentUserId : allUsersList) { + if (ps.getInstalled(currentUserId)) { + mPermissionManager.onPackageInstalled(pkg, permissionParamsBuilder.build(), + currentUserId); + } + } } res.name = pkgName; res.uid = pkg.getUid(); @@ -18273,7 +18520,7 @@ public class PackageManagerService extends IPackageManager.Stub public final Map<String, ScanResult> scannedPackages; public final Map<String, AndroidPackage> allPackages; - public final Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource; + public final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource; public final Map<String, InstallArgs> installArgs; public final Map<String, PackageInstalledInfo> installResults; public final Map<String, PrepareResult> preparedPackages; @@ -18284,7 +18531,7 @@ public class PackageManagerService extends IPackageManager.Stub Map<String, InstallArgs> installArgs, Map<String, PackageInstalledInfo> installResults, Map<String, PrepareResult> preparedPackages, - Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource, + Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource, Map<String, AndroidPackage> allPackages, Map<String, VersionInfo> versionInfos, Map<String, PackageSetting> lastStaticSharedLibSettings) { @@ -18299,7 +18546,7 @@ public class PackageManagerService extends IPackageManager.Stub } private ReconcileRequest(Map<String, ScanResult> scannedPackages, - Map<String, LongSparseArray<SharedLibraryInfo>> sharedLibrarySource, + Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource, Map<String, AndroidPackage> allPackages, Map<String, VersionInfo> versionInfos, Map<String, PackageSetting> lastStaticSharedLibSettings) { @@ -18396,7 +18643,7 @@ public class PackageManagerService extends IPackageManager.Stub combinedPackages.putAll(request.allPackages); - final Map<String, LongSparseArray<SharedLibraryInfo>> incomingSharedLibraries = + final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries = new ArrayMap<>(); for (String installPackageName : scannedPackages.keySet()) { @@ -18620,7 +18867,7 @@ public class PackageManagerService extends IPackageManager.Stub */ private static List<SharedLibraryInfo> getAllowedSharedLibInfos( ScanResult scanResult, - Map<String, LongSparseArray<SharedLibraryInfo>> existingSharedLibraries) { + Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingSharedLibraries) { // Let's used the parsed package as scanResult.pkgSetting may be null final ParsedPackage parsedPackage = scanResult.request.parsedPackage; if (scanResult.staticSharedLibraryInfo == null @@ -18690,7 +18937,7 @@ public class PackageManagerService extends IPackageManager.Stub * added. */ private static boolean addSharedLibraryToPackageVersionMap( - Map<String, LongSparseArray<SharedLibraryInfo>> target, + Map<String, WatchedLongSparseArray<SharedLibraryInfo>> target, SharedLibraryInfo library) { final String name = library.getName(); if (target.containsKey(name)) { @@ -18702,7 +18949,7 @@ public class PackageManagerService extends IPackageManager.Stub return false; } } else { - target.put(name, new LongSparseArray<>()); + target.put(name, new WatchedLongSparseArray<>()); } target.get(name).put(library.getLongVersion(), library); return true; @@ -20731,7 +20978,7 @@ public class PackageManagerService extends IPackageManager.Stub extras, 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null); packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0, removedPackage, null, null, null, null /* broadcastAllowList */, - getTemporaryAppWhitelistBroadcastOptions().toBundle()); + getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle()); if (installerPackageName != null) { packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras, 0 /*flags*/, @@ -22439,7 +22686,7 @@ public class PackageManagerService extends IPackageManager.Stub } final Intent intent = getHomeIntent(); final List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null, - PackageManager.GET_META_DATA, userId); + MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId); final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked( intent, null, 0, resolveInfos, 0, true, false, false, userId); final String packageName = preferredResolveInfo != null @@ -23524,10 +23771,6 @@ public class PackageManagerService extends IPackageManager.Stub if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; DumpState dumpState = new DumpState(); - boolean fullPreferred = false; - boolean checkin = false; - - String packageName = null; ArraySet<String> permissionNames = null; int opti = 0; @@ -23577,7 +23820,7 @@ public class PackageManagerService extends IPackageManager.Stub pw.println(" <package.name>: info about given package"); return; } else if ("--checkin".equals(opt)) { - checkin = true; + dumpState.setCheckIn(true); } else if ("--all-components".equals(opt)) { dumpState.setOptionEnabled(DumpState.OPTION_DUMP_ALL_COMPONENTS); } else if ("-f".equals(opt)) { @@ -23596,7 +23839,7 @@ public class PackageManagerService extends IPackageManager.Stub opti++; // Is this a package name? if ("android".equals(cmd) || cmd.contains(".")) { - packageName = cmd; + dumpState.setTargetPackageName(cmd); // When dumping a single package, we always dump all of its // filter information since the amount of data will be reasonable. dumpState.setOptionEnabled(DumpState.OPTION_SHOW_FILTERS); @@ -23677,7 +23920,7 @@ public class PackageManagerService extends IPackageManager.Stub } else if ("preferred-xml".equals(cmd)) { dumpState.setDump(DumpState.DUMP_PREFERRED_XML); if (opti < args.length && "--full".equals(args[opti])) { - fullPreferred = true; + dumpState.setFullPreferred(true); opti++; } } else if ("d".equals(cmd) || "domain-preferred-apps".equals(cmd)) { @@ -23730,257 +23973,205 @@ public class PackageManagerService extends IPackageManager.Stub } } + final String packageName = dumpState.getTargetPackageName(); + final boolean checkin = dumpState.isCheckIn(); if (checkin) { pw.println("vers,1"); } // reader - synchronized (mLock) { - if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) { - if (!checkin) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("Database versions:"); - mSettings.dumpVersionLPr(new IndentingPrintWriter(pw, " ")); - } + if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) { + if (!checkin) { + dump(DumpState.DUMP_VERSION, fd, pw, dumpState); } + } - if (!checkin - && dumpState.isDumping(DumpState.DUMP_KNOWN_PACKAGES) - && packageName == null) { - if (dumpState.onTitlePrinted()) { - pw.println(); - } - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); - ipw.println("Known Packages:"); + if (!checkin + && dumpState.isDumping(DumpState.DUMP_KNOWN_PACKAGES) + && packageName == null) { + if (dumpState.onTitlePrinted()) { + pw.println(); + } + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); + ipw.println("Known Packages:"); + ipw.increaseIndent(); + for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) { + final String knownPackage = mPmInternal.knownPackageToString(i); + ipw.print(knownPackage); + ipw.println(":"); + final String[] pkgNames = mPmInternal.getKnownPackageNames(i, + UserHandle.USER_SYSTEM); ipw.increaseIndent(); - for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) { - final String knownPackage = mPmInternal.knownPackageToString(i); - ipw.print(knownPackage); - ipw.println(":"); - final String[] pkgNames = mPmInternal.getKnownPackageNames(i, - UserHandle.USER_SYSTEM); - ipw.increaseIndent(); - if (ArrayUtils.isEmpty(pkgNames)) { - ipw.println("none"); - } else { - for (String name : pkgNames) { - ipw.println(name); - } + if (ArrayUtils.isEmpty(pkgNames)) { + ipw.println("none"); + } else { + for (String name : pkgNames) { + ipw.println(name); } - ipw.decreaseIndent(); } ipw.decreaseIndent(); } + ipw.decreaseIndent(); + } + + if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) { + final String requiredVerifierPackage = mRequiredVerifierPackage; + if (!checkin) { + if (dumpState.onTitlePrinted()) { + pw.println(); + } + pw.println("Verifiers:"); + pw.print(" Required: "); + pw.print(requiredVerifierPackage); + pw.print(" (uid="); + pw.print(getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING, + UserHandle.USER_SYSTEM)); + pw.println(")"); + } else if (requiredVerifierPackage != null) { + pw.print("vrfy,"); pw.print(requiredVerifierPackage); + pw.print(","); + pw.println(getPackageUid(requiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING, + UserHandle.USER_SYSTEM)); + } + } - if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) { + if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && packageName == null) { + final DomainVerificationProxy proxy = mDomainVerificationManager.getProxy(); + final ComponentName verifierComponent = proxy.getComponentName(); + if (verifierComponent != null) { + String verifierPackageName = verifierComponent.getPackageName(); if (!checkin) { if (dumpState.onTitlePrinted()) pw.println(); - pw.println("Verifiers:"); - pw.print(" Required: "); - pw.print(mRequiredVerifierPackage); + pw.println("Domain Verifier:"); + pw.print(" Using: "); + pw.print(verifierPackageName); pw.print(" (uid="); - pw.print(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING, + pw.print(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM)); pw.println(")"); - } else if (mRequiredVerifierPackage != null) { - pw.print("vrfy,"); pw.print(mRequiredVerifierPackage); + } else if (verifierPackageName != null) { + pw.print("dv,"); pw.print(verifierPackageName); pw.print(","); - pw.println(getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING, + pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM)); } + } else { + pw.println(); + pw.println("No Domain Verifier available!"); } + } - if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && - packageName == null) { - DomainVerificationProxy proxy = mDomainVerificationManager.getProxy(); - ComponentName verifierComponent = proxy.getComponentName(); - if (verifierComponent != null) { - String verifierPackageName = verifierComponent.getPackageName(); - if (!checkin) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("Domain Verifier:"); - pw.print(" Using: "); - pw.print(verifierPackageName); - pw.print(" (uid="); - pw.print(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING, - UserHandle.USER_SYSTEM)); - pw.println(")"); - } else if (verifierPackageName != null) { - pw.print("dv,"); pw.print(verifierPackageName); - pw.print(","); - pw.println(getPackageUid(verifierPackageName, MATCH_DEBUG_TRIAGED_MISSING, - UserHandle.USER_SYSTEM)); - } - } else { - pw.println(); - pw.println("No Domain Verifier available!"); - } - } + if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) { + dump(DumpState.DUMP_LIBS, fd, pw, dumpState); + } - if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) { - boolean printedHeader = false; - final int numSharedLibraries = mSharedLibraries.size(); - for (int index = 0; index < numSharedLibraries; index++) { - final String libName = mSharedLibraries.keyAt(index); - LongSparseArray<SharedLibraryInfo> versionedLib - = mSharedLibraries.get(libName); - if (versionedLib == null) { - continue; - } - final int versionCount = versionedLib.size(); - for (int i = 0; i < versionCount; i++) { - SharedLibraryInfo libraryInfo = versionedLib.valueAt(i); - if (!checkin) { - if (!printedHeader) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("Libraries:"); - printedHeader = true; - } - pw.print(" "); - } else { - pw.print("lib,"); - } - pw.print(libraryInfo.getName()); - if (libraryInfo.isStatic()) { - pw.print(" version=" + libraryInfo.getLongVersion()); - } - if (!checkin) { - pw.print(" -> "); - } - if (libraryInfo.getPath() != null) { - if (libraryInfo.isNative()) { - pw.print(" (so) "); - } else { - pw.print(" (jar) "); - } - pw.print(libraryInfo.getPath()); - } else { - pw.print(" (apk) "); - pw.print(libraryInfo.getPackageName()); - } - pw.println(); - } - } + if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) { + if (dumpState.onTitlePrinted()) { + pw.println(); + } + if (!checkin) { + pw.println("Features:"); } - if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) { - if (dumpState.onTitlePrinted()) - pw.println(); - if (!checkin) { - pw.println("Features:"); - } - - synchronized (mAvailableFeatures) { - for (FeatureInfo feat : mAvailableFeatures.values()) { - if (checkin) { - pw.print("feat,"); - pw.print(feat.name); - pw.print(","); - pw.println(feat.version); - } else { - pw.print(" "); - pw.print(feat.name); - if (feat.version > 0) { - pw.print(" version="); - pw.print(feat.version); - } - pw.println(); + synchronized (mAvailableFeatures) { + for (FeatureInfo feat : mAvailableFeatures.values()) { + if (checkin) { + pw.print("feat,"); + pw.print(feat.name); + pw.print(","); + pw.println(feat.version); + } else { + pw.print(" "); + pw.print(feat.name); + if (feat.version > 0) { + pw.print(" version="); + pw.print(feat.version); } + pw.println(); } } } + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) { + synchronized (mLock) { mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) { + } + if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) { + synchronized (mLock) { mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) { + } + if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) { + synchronized (mLock) { mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName); } - if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) { + } + if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) { + synchronized (mLock) { mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName); } + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) { + // TODO: This cannot be moved to ComputerEngine since some variables with collections + // types in IntentResolver such as mTypeToFilter do not have a copy of `F[]`. + synchronized (mLock) { mSettings.dumpPreferred(pw, dumpState, packageName); } + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) { - pw.flush(); - FileOutputStream fout = new FileOutputStream(fd); - BufferedOutputStream str = new BufferedOutputStream(fout); - TypedXmlSerializer serializer = Xml.newFastSerializer(); - try { - serializer.setOutput(str, StandardCharsets.UTF_8.name()); - serializer.startDocument(null, true); - serializer.setFeature( - "http://xmlpull.org/v1/doc/features.html#indent-output", true); - mSettings.writePreferredActivitiesLPr(serializer, 0, fullPreferred); - serializer.endDocument(); - serializer.flush(); - } catch (IllegalArgumentException e) { - pw.println("Failed writing: " + e); - } catch (IllegalStateException e) { - pw.println("Failed writing: " + e); - } catch (IOException e) { - pw.println("Failed writing: " + e); - } - } - - if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) { - android.util.IndentingPrintWriter writer = - new android.util.IndentingPrintWriter(pw); - if (dumpState.onTitlePrinted()) pw.println(); + if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) { + dump(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState); + } - writer.println("Domain verification status:"); - writer.increaseIndent(); - try { - mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL, - mSettings::getPackageLPr); - } catch (PackageManager.NameNotFoundException e) { - pw.println("Failure printing domain verification information"); - Slog.e(TAG, "Failure printing domain verification information", e); - } - writer.decreaseIndent(); - } + if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) { + dump(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState); + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) { - mSettings.dumpPermissionsLPr(pw, packageName, permissionNames, dumpState); - } + if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) { + mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState); + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) { + synchronized (mLock) { mComponentResolver.dumpContentProviders(pw, dumpState, packageName); } + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) { + synchronized (mLock) { mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState); } + } - if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) { + if (dumpState.isDumping(DumpState.DUMP_PACKAGES)) { + // This cannot be moved to ComputerEngine since some variables of the collections + // in PackageUserState such as suspendParams, disabledComponents and enabledComponents + // do not have a copy. + synchronized (mLock) { mSettings.dumpPackagesLPr(pw, packageName, permissionNames, dumpState, checkin); } + } - if (dumpState.isDumping(DumpState.DUMP_QUERIES)) { - final PackageSetting setting = mSettings.getPackageLPr(packageName); - Integer filteringAppId = setting == null ? null : setting.appId; - mAppsFilter.dumpQueries( - pw, this, filteringAppId, dumpState, - mUserManager.getUserIds()); - } + if (dumpState.isDumping(DumpState.DUMP_QUERIES)) { + dump(DumpState.DUMP_QUERIES, fd, pw, dumpState); + } - if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) { + if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) { + // This cannot be moved to ComputerEngine since the set of packages in the + // SharedUserSetting do not have a copy. + synchronized (mLock) { mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin); } + } - if (dumpState.isDumping(DumpState.DUMP_CHANGES)) { - if (dumpState.onTitlePrinted()) pw.println(); - pw.println("Package Changes:"); + if (dumpState.isDumping(DumpState.DUMP_CHANGES)) { + if (dumpState.onTitlePrinted()) pw.println(); + pw.println("Package Changes:"); + synchronized (mLock) { pw.print(" Sequence number="); pw.println(mChangedPackagesSequenceNumber); final int K = mChangedPackages.size(); for (int i = 0; i < K; i++) { @@ -24002,16 +24193,18 @@ public class PackageManagerService extends IPackageManager.Stub } } } + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) { - // XXX should handle packageName != null by dumping only install data that - // the given package is involved with. - if (dumpState.onTitlePrinted()) pw.println(); + if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) { + // XXX should handle packageName != null by dumping only install data that + // the given package is involved with. + if (dumpState.onTitlePrinted()) pw.println(); - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); - ipw.println(); - ipw.println("Frozen packages:"); - ipw.increaseIndent(); + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); + ipw.println(); + ipw.println("Frozen packages:"); + ipw.increaseIndent(); + synchronized (mLock) { if (mFrozenPackages.size() == 0) { ipw.println("(none)"); } else { @@ -24019,16 +24212,18 @@ public class PackageManagerService extends IPackageManager.Stub ipw.println(mFrozenPackages.valueAt(i)); } } - ipw.decreaseIndent(); } + ipw.decreaseIndent(); + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) { - if (dumpState.onTitlePrinted()) pw.println(); + if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) { + if (dumpState.onTitlePrinted()) pw.println(); - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); - ipw.println(); - ipw.println("Loaded volumes:"); - ipw.increaseIndent(); + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); + ipw.println(); + ipw.println("Loaded volumes:"); + ipw.increaseIndent(); + synchronized (mLoadedVolumes) { if (mLoadedVolumes.size() == 0) { ipw.println("(none)"); } else { @@ -24036,36 +24231,39 @@ public class PackageManagerService extends IPackageManager.Stub ipw.println(mLoadedVolumes.valueAt(i)); } } - ipw.decreaseIndent(); } + ipw.decreaseIndent(); + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS) - && packageName == null) { + if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS) + && packageName == null) { + synchronized (mLock) { mComponentResolver.dumpServicePermissions(pw, dumpState); } + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) { - if (dumpState.onTitlePrinted()) pw.println(); - dumpDexoptStateLPr(pw, packageName); - } + if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) { + if (dumpState.onTitlePrinted()) pw.println(); + dump(DumpState.DUMP_DEXOPT, fd, pw, dumpState); + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) { - if (dumpState.onTitlePrinted()) pw.println(); - dumpCompilerStatsLPr(pw, packageName); - } + if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) { + if (dumpState.onTitlePrinted()) pw.println(); + dump(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState); + } - if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) { - if (dumpState.onTitlePrinted()) pw.println(); + if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) { + if (dumpState.onTitlePrinted()) pw.println(); + synchronized (mLock) { mSettings.dumpReadMessagesLPr(pw, dumpState); - - pw.println(); - pw.println("Package warning messages:"); - dumpCriticalInfo(pw, null); } + pw.println(); + pw.println("Package warning messages:"); + dumpCriticalInfo(pw, null); + } - if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) { - dumpCriticalInfo(pw, "msg,"); - } + if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) { + dumpCriticalInfo(pw, "msg,"); } // PackageInstaller should be called outside of mPackages lock @@ -24100,6 +24298,14 @@ public class PackageManagerService extends IPackageManager.Stub } } + /** + * Dump package manager states to the file according to a given dumping type of + * {@link DumpState}. + */ + private void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) { + snapshotComputer().dump(type, fd, pw, dumpState); + } + //TODO: b/111402650 private void disableSkuSpecificApps() { String apkList[] = mContext.getResources().getStringArray( @@ -24174,7 +24380,7 @@ public class PackageManagerService extends IPackageManager.Stub final int count = mSharedLibraries.size(); for (int i = 0; i < count; i++) { final String libName = mSharedLibraries.keyAt(i); - LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName); + WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName); if (versionedLib == null) { continue; } @@ -24198,72 +24404,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mLock") - @SuppressWarnings("resource") - private void dumpDexoptStateLPr(PrintWriter pw, String packageName) { - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - ipw.println(); - ipw.println("Dexopt state:"); - ipw.increaseIndent(); - Collection<PackageSetting> pkgSettings; - if (packageName != null) { - PackageSetting targetPkgSetting = mSettings.getPackageLPr(packageName); - if (targetPkgSetting != null) { - pkgSettings = Collections.singletonList(targetPkgSetting); - } else { - ipw.println("Unable to find package: " + packageName); - return; - } - } else { - pkgSettings = mSettings.getPackagesLocked().values(); - } - - for (PackageSetting pkgSetting : pkgSettings) { - if (pkgSetting.pkg == null) { - continue; - } - ipw.println("[" + pkgSetting.name + "]"); - ipw.increaseIndent(); - mPackageDexOptimizer.dumpDexoptState(ipw, pkgSetting.pkg, pkgSetting, - mDexManager.getPackageUseInfoOrDefault(pkgSetting.pkg.getPackageName())); - ipw.decreaseIndent(); - } - } - - @GuardedBy("mLock") - @SuppressWarnings("resource") - private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) { - final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - ipw.println(); - ipw.println("Compiler stats:"); - ipw.increaseIndent(); - Collection<AndroidPackage> packages; - if (packageName != null) { - AndroidPackage targetPackage = mPackages.get(packageName); - if (targetPackage != null) { - packages = Collections.singletonList(targetPackage); - } else { - ipw.println("Unable to find package: " + packageName); - return; - } - } else { - packages = mPackages.values(); - } - - for (AndroidPackage pkg : packages) { - ipw.println("[" + pkg.getPackageName() + "]"); - ipw.increaseIndent(); - - CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.getPackageName()); - if (stats == null) { - ipw.println("(No recorded stats)"); - } else { - stats.dump(ipw); - } - ipw.decreaseIndent(); - } - } - // ------- apps on sdcard specific code ------- static final boolean DEBUG_SD_INSTALL = false; @@ -24422,7 +24562,9 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded); sendResourcesChangedBroadcast(true, false, loaded, null); - mLoadedVolumes.add(vol.getId()); + synchronized (mLoadedVolumes) { + mLoadedVolumes.add(vol.getId()); + } } private void unloadPrivatePackages(final VolumeInfo vol) { @@ -24470,7 +24612,9 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded); sendResourcesChangedBroadcast(false, false, unloaded, null); - mLoadedVolumes.remove(vol.getId()); + synchronized (mLoadedVolumes) { + mLoadedVolumes.remove(vol.getId()); + } // Try very hard to release any references to this path so we don't risk // the system server being killed due to open FDs @@ -27402,43 +27546,14 @@ public class PackageManagerService extends IPackageManager.Stub } } - private String getOatDir(AndroidPackage pkg, @NonNull PackageSetting pkgSetting) { - if (!AndroidPackageUtils.canHaveOatDir(pkg, - pkgSetting.getPkgState().isUpdatedSystemApp())) { - return null; - } - File codePath = new File(pkg.getPath()); - if (codePath.isDirectory()) { - return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath(); - } - return null; - } - void deleteOatArtifactsOfPackage(String packageName) { - final String[] instructionSets; - final List<String> codePaths; - final String oatDir; final AndroidPackage pkg; final PackageSetting pkgSetting; synchronized (mLock) { pkg = mPackages.get(packageName); pkgSetting = mSettings.getPackageLPr(packageName); } - instructionSets = getAppDexInstructionSets( - AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting), - AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting)); - codePaths = AndroidPackageUtils.getAllCodePaths(pkg); - oatDir = getOatDir(pkg, pkgSetting); - - for (String codePath : codePaths) { - for (String isa : instructionSets) { - try { - mInstaller.deleteOdex(codePath, isa, oatDir); - } catch (InstallerException e) { - Log.e(TAG, "Failed deleting oat files for " + codePath, e); - } - } - } + mDexManager.deleteOptimizedFiles(ArtUtils.createArtPackageInfo(pkg, pkgSetting)); } Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) { @@ -27710,7 +27825,8 @@ public class PackageManagerService extends IPackageManager.Stub return result.toArray(new PerUidReadTimeouts[result.size()]); } - static @NonNull BroadcastOptions getTemporaryAppWhitelistBroadcastOptions() { + static @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions( + @PowerWhitelistManager.ReasonCode int reasonCode) { long duration = 10_000; final ActivityManagerInternal amInternal = LocalServices.getService(ActivityManagerInternal.class); @@ -27718,9 +27834,9 @@ public class PackageManagerService extends IPackageManager.Stub duration = amInternal.getBootTimeTempAllowListDuration(); } final BroadcastOptions bOptions = BroadcastOptions.makeBasic(); - bOptions.setTemporaryAppWhitelistDuration( - BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED, - duration); + bOptions.setTemporaryAppAllowlist(duration, + TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, + reasonCode, ""); return bOptions; } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index a83a3f81bc00..38e100e80cd3 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -787,6 +787,10 @@ public abstract class PackageSettingBase extends SettingBase { return firstInstallTime; } + public String getName() { + return name; + } + protected PackageSettingBase updateFrom(PackageSettingBase other) { super.copyFrom(other); setPath(other.getPath()); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 2112247650a5..ec7b451c6ec9 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -40,7 +40,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; import android.content.pm.IntentFilterVerificationInfo; -import android.content.pm.overlay.OverlayPaths; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageUserState; @@ -72,6 +71,7 @@ import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; +import android.os.incremental.IncrementalManager; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; import android.service.pm.PackageServiceDumpProto; @@ -1028,6 +1028,9 @@ public final class Settings implements Watchable, Snappable { pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath; } pkgSetting.setPath(codePath); + if (IncrementalManager.isIncrementalPath(codePath.getAbsolutePath())) { + pkgSetting.incrementalStates = new IncrementalStates(); + } } // If what we are scanning is a system (and possibly privileged) package, // then make it so, regardless of whether it was previously installed only @@ -4877,7 +4880,7 @@ public final class Settings implements Watchable, Snappable { } } - void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames, + void dumpPermissions(PrintWriter pw, String packageName, ArraySet<String> permissionNames, DumpState dumpState) { LegacyPermissionSettings.dumpPermissions(pw, packageName, permissionNames, mPermissionDataProvider.getLegacyPermissions(), diff --git a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java index 1c5f0a7fc0f3..f411c98433cf 100644 --- a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java +++ b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java @@ -280,7 +280,8 @@ public class ShortcutBitmapSaver { IoUtils.closeQuietly(out); } - shortcut.setBitmapPath(file.getAbsolutePath()); + final String path = file.getAbsolutePath(); + mService.postValue(shortcut, si -> si.setBitmapPath(path)); } catch (IOException | RuntimeException e) { Slog.e(ShortcutService.TAG, "Unable to write bitmap to file", e); @@ -295,12 +296,14 @@ public class ShortcutBitmapSaver { Slog.d(TAG, "Saved bitmap."); } if (shortcut != null) { - if (shortcut.getBitmapPath() == null) { - removeIcon(shortcut); - } + mService.postValue(shortcut, si -> { + if (si.getBitmapPath() == null) { + removeIcon(si); + } - // Whatever happened, remove this flag. - shortcut.clearFlags(ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE); + // Whatever happened, remove this flag. + si.clearFlags(ShortcutInfo.FLAG_ICON_FILE_PENDING_SAVE); + }); } } return true; diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index bb4ec16be0a8..302e6572a245 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -19,15 +19,22 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.Person; +import android.app.appsearch.AppSearchManager; +import android.app.appsearch.AppSearchSession; +import android.app.appsearch.PackageIdentifier; +import android.app.appsearch.SetSchemaRequest; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.content.LocusId; +import android.content.pm.AppSearchPerson; +import android.content.pm.AppSearchShortcutInfo; import android.content.pm.PackageInfo; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; import android.content.res.Resources; import android.graphics.drawable.Icon; +import android.os.Binder; import android.os.PersistableBundle; import android.text.format.Formatter; import android.util.ArrayMap; @@ -39,9 +46,11 @@ import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; +import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.pm.ShortcutService.DumpFilter; @@ -64,8 +73,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; /** @@ -155,6 +168,16 @@ class ShortcutPackage extends ShortcutPackageItem { private long mLastKnownForegroundElapsedTime; + private final Object mLock = new Object(); + + /** + * All external packages that have gained access to the shortcuts from this package + */ + private final Map<String, PackageIdentifier> mPackageIdentifiers = new ArrayMap<>(0); + + @GuardedBy("mLock") + private AppSearchSession mAppSearchSession; + private ShortcutPackage(ShortcutUser shortcutUser, int packageUserId, String packageName, ShortcutPackageInfo spi) { super(shortcutUser, packageUserId, packageName, @@ -200,12 +223,14 @@ class ShortcutPackage extends ShortcutPackageItem { // - Disable if needed. for (int i = mShortcuts.size() - 1; i >= 0; i--) { ShortcutInfo si = mShortcuts.valueAt(i); - si.clearFlags(ShortcutInfo.FLAG_SHADOW); + mutateShortcut(si.getId(), si, shortcut -> { + shortcut.clearFlags(ShortcutInfo.FLAG_SHADOW); - si.setDisabledReason(restoreBlockReason); - if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) { - si.addFlags(ShortcutInfo.FLAG_DISABLED); - } + shortcut.setDisabledReason(restoreBlockReason); + if (restoreBlockReason != ShortcutInfo.DISABLED_REASON_NOT_DISABLED) { + shortcut.addFlags(ShortcutInfo.FLAG_DISABLED); + } + }); } // Because some launchers may not have been restored (e.g. allowBackup=false), // we need to re-calculate the pinned shortcuts. @@ -437,9 +462,11 @@ class ShortcutPackage extends ShortcutPackageItem { if (si.isDynamic() && (!ignoreInvisible || si.isVisibleToPublisher())) { changed = true; - si.setTimestamp(now); - si.clearFlags(ShortcutInfo.FLAG_DYNAMIC); - si.setRank(0); // It may still be pinned, so clear the rank. + mutateShortcut(si.getId(), si, shortcut -> { + shortcut.setTimestamp(now); + shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC); + shortcut.setRank(0); // It may still be pinned, so clear the rank. + }); } } if (changed) { @@ -483,7 +510,7 @@ class ShortcutPackage extends ShortcutPackageItem { public ShortcutInfo deleteLongLivedWithId(@NonNull String shortcutId, boolean ignoreInvisible) { final ShortcutInfo shortcut = mShortcuts.get(shortcutId); if (shortcut != null) { - shortcut.clearFlags(ShortcutInfo.FLAG_CACHED_ALL); + mutateShortcut(shortcutId, null, si -> si.clearFlags(ShortcutInfo.FLAG_CACHED_ALL)); } return deleteOrDisableWithId( shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible, @@ -504,15 +531,16 @@ class ShortcutPackage extends ShortcutPackageItem { overrideImmutable, ignoreInvisible, disabledReason); // If disabled id still exists, it is pinned and we need to update the disabled message. - final ShortcutInfo disabled = mShortcuts.get(shortcutId); - if (disabled != null) { - if (disabledMessage != null) { - disabled.setDisabledMessage(disabledMessage); - } else if (disabledMessageResId != 0) { - disabled.setDisabledMessageResId(disabledMessageResId); - mShortcutUser.mService.fixUpShortcutResourceNamesAndValues(disabled); + mutateShortcut(shortcutId, null, disabled -> { + if (disabled != null) { + if (disabledMessage != null) { + disabled.setDisabledMessage(disabledMessage); + } else if (disabledMessageResId != 0) { + disabled.setDisabledMessageResId(disabledMessageResId); + mShortcutUser.mService.fixUpShortcutResourceNamesAndValues(disabled); + } } - } + }); return deleted; } @@ -534,21 +562,23 @@ class ShortcutPackage extends ShortcutPackageItem { } if (oldShortcut.isPinned() || oldShortcut.isCached()) { - oldShortcut.setRank(0); - oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST); - if (disable) { - oldShortcut.addFlags(ShortcutInfo.FLAG_DISABLED); - // Do not overwrite the disabled reason if one is alreay set. - if (oldShortcut.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED) { - oldShortcut.setDisabledReason(disabledReason); + mutateShortcut(oldShortcut.getId(), oldShortcut, si -> { + si.setRank(0); + si.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST); + if (disable) { + si.addFlags(ShortcutInfo.FLAG_DISABLED); + // Do not overwrite the disabled reason if one is alreay set. + if (si.getDisabledReason() == ShortcutInfo.DISABLED_REASON_NOT_DISABLED) { + si.setDisabledReason(disabledReason); + } } - } - oldShortcut.setTimestamp(mShortcutUser.mService.injectCurrentTimeMillis()); + si.setTimestamp(mShortcutUser.mService.injectCurrentTimeMillis()); - // See ShortcutRequestPinProcessor.directPinShortcut(). - if (mShortcutUser.mService.isDummyMainActivity(oldShortcut.getActivity())) { - oldShortcut.setActivity(null); - } + // See ShortcutRequestPinProcessor.directPinShortcut(). + if (mShortcutUser.mService.isDummyMainActivity(si.getActivity())) { + si.setActivity(null); + } + }); return null; } else { @@ -558,12 +588,11 @@ class ShortcutPackage extends ShortcutPackageItem { } public void enableWithId(@NonNull String shortcutId) { - final ShortcutInfo shortcut = mShortcuts.get(shortcutId); - if (shortcut != null) { - ensureNotImmutable(shortcut, /*ignoreInvisible=*/ true); - shortcut.clearFlags(ShortcutInfo.FLAG_DISABLED); - shortcut.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED); - } + mutateShortcut(shortcutId, null, si -> { + ensureNotImmutable(si, /*ignoreInvisible=*/ true); + si.clearFlags(ShortcutInfo.FLAG_DISABLED); + si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED); + }); } public void updateInvisibleShortcutForPinRequestWith(@NonNull ShortcutInfo shortcut) { @@ -586,22 +615,25 @@ class ShortcutPackage extends ShortcutPackageItem { * <p>Then remove all shortcuts that are not dynamic and no longer pinned either. */ public void refreshPinnedFlags() { - // First, un-pin all shortcuts - for (int i = mShortcuts.size() - 1; i >= 0; i--) { - mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED); + // TODO: rewrite this function with proper query (i.e. fetch only pinned shortcuts and + // unpin if it's no longer pinned by any launcher and vice versa) + final List<ShortcutInfo> shortcuts = new ArrayList<>(mShortcuts.values()); + final Map<String, ShortcutInfo> shortcutMap = new ArrayMap<>(shortcuts.size()); + for (ShortcutInfo si : shortcuts) { + shortcutMap.put(si.getId(), si); } + final Set<String> pinnedShortcuts = new ArraySet<>(); - // Then, for the pinned set for each launcher, set the pin flag one by one. + // First, for the pinned set for each launcher, keep track of their id one by one. mShortcutUser.forAllLaunchers(launcherShortcuts -> { final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds( getPackageName(), getPackageUserId()); - if (pinned == null || pinned.size() == 0) { return; } for (int i = pinned.size() - 1; i >= 0; i--) { final String id = pinned.valueAt(i); - final ShortcutInfo si = mShortcuts.get(id); + final ShortcutInfo si = shortcutMap.get(id); if (si == null) { // This happens if a launcher pinned shortcuts from this package, then backup& // restored, but this package doesn't allow backing up. @@ -609,9 +641,21 @@ class ShortcutPackage extends ShortcutPackageItem { // That's fine, when the launcher is restored, we'll fix it. continue; } - si.addFlags(ShortcutInfo.FLAG_PINNED); + pinnedShortcuts.add(si.getId()); } }); + // Then, update the pinned state if necessary + for (int i = shortcuts.size() - 1; i >= 0; i--) { + final ShortcutInfo si = shortcuts.get(i); + if (pinnedShortcuts.contains(si.getId()) && !si.isPinned()) { + mutateShortcut(si.getId(), si, + shortcut -> shortcut.addFlags(ShortcutInfo.FLAG_PINNED)); + } + if (!pinnedShortcuts.contains(si.getId()) && si.isPinned()) { + mutateShortcut(si.getId(), si, shortcut -> + shortcut.clearFlags(ShortcutInfo.FLAG_PINNED)); + } + } // Lastly, remove the ones that are no longer pinned, cached nor dynamic. removeOrphans(); @@ -1011,8 +1055,10 @@ class ShortcutPackage extends ShortcutPackageItem { continue; } Slog.i(TAG, String.format("Restoring shortcut: %s", si.getId())); - si.clearFlags(ShortcutInfo.FLAG_DISABLED); - si.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED); + mutateShortcut(si.getId(), si, shortcut -> { + shortcut.clearFlags(ShortcutInfo.FLAG_DISABLED); + shortcut.setDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED); + }); } // For existing shortcuts, update timestamps if they have any resources. @@ -1042,21 +1088,24 @@ class ShortcutPackage extends ShortcutPackageItem { } if (si.hasAnyResources()) { - if (!si.isOriginallyFromManifest()) { + if (publisherRes == null) { + publisherRes = getPackageResources(); if (publisherRes == null) { - publisherRes = getPackageResources(); - if (publisherRes == null) { - break; // Resources couldn't be loaded. - } + break; // Resources couldn't be loaded. + } + } + + final Resources res = publisherRes; + mutateShortcut(si.getId(), si, shortcut -> { + if (!shortcut.isOriginallyFromManifest()) { + shortcut.lookupAndFillInResourceIds(res); } - // TODO: update resource strings in AppSearch // If this shortcut is not from a manifest, then update all resource IDs // from resource names. (We don't allow resource strings for // non-manifest at the moment, but icons can still be resources.) - si.lookupAndFillInResourceIds(publisherRes); - } - si.setTimestamp(s.injectCurrentTimeMillis()); + shortcut.setTimestamp(s.injectCurrentTimeMillis()); + }); } } } @@ -1359,8 +1408,11 @@ class ShortcutPackage extends ShortcutPackageItem { } } - si.resolveResourceStrings(publisherRes); - si.setTimestamp(s.injectCurrentTimeMillis()); + final Resources res = publisherRes; + mutateShortcut(si.getId(), si, shortcut -> { + shortcut.resolveResourceStrings(res); + shortcut.setTimestamp(s.injectCurrentTimeMillis()); + }); if (changedShortcuts == null) { changedShortcuts = new ArrayList<>(1); @@ -1377,7 +1429,7 @@ class ShortcutPackage extends ShortcutPackageItem { public void clearAllImplicitRanks() { for (int i = mShortcuts.size() - 1; i >= 0; i--) { final ShortcutInfo si = mShortcuts.valueAt(i); - si.clearImplicitRankAndRankChangedFlag(); + mutateShortcut(si.getId(), si, ShortcutInfo::clearImplicitRankAndRankChangedFlag); } } @@ -1422,8 +1474,10 @@ class ShortcutPackage extends ShortcutPackageItem { final ShortcutInfo si = mShortcuts.valueAt(i); if (si.isFloating()) { if (si.getRank() != 0) { - si.setTimestamp(now); - si.setRank(0); + mutateShortcut(si.getId(), si, shortcut -> { + shortcut.setTimestamp(now); + shortcut.setRank(0); + }); } } } @@ -1456,8 +1510,10 @@ class ShortcutPackage extends ShortcutPackageItem { } final int thisRank = rank++; if (si.getRank() != thisRank) { - si.setTimestamp(now); - si.setRank(thisRank); + mutateShortcut(si.getId(), si, shortcut -> { + shortcut.setTimestamp(now); + shortcut.setRank(thisRank); + }); } } } @@ -2140,6 +2196,41 @@ class ShortcutPackage extends ShortcutPackageItem { } } + void updateVisibility(String packageName, byte[] certificate, boolean visible) { + if (visible) { + mPackageIdentifiers.put(packageName, new PackageIdentifier(packageName, certificate)); + } else { + mPackageIdentifiers.remove(packageName); + } + resetAppSearch(null); + } + + void mutateShortcut(@NonNull final String id, @Nullable final ShortcutInfo shortcut, + @NonNull final Consumer<ShortcutInfo> transform) { + Objects.requireNonNull(id); + Objects.requireNonNull(transform); + synchronized (mLock) { + if (shortcut != null) { + transform.accept(shortcut); + } else { + transform.accept(findShortcutById(id)); + } + // TODO: Load ShortcutInfo from AppSearch, apply transformation logic and save + } + } + + /** + * Removes shortcuts from AppSearch. + */ + void removeShortcuts() { + } + + /** + * Merge/replace shortcuts parsed from xml file. + */ + void restoreParsedShortcuts(final boolean replace) { + } + private boolean verifyRanksSequential(List<ShortcutInfo> list) { boolean failed = false; @@ -2153,4 +2244,128 @@ class ShortcutPackage extends ShortcutPackageItem { } return failed; } + + private void runInAppSearch( + Function<SearchSessionObservable, Consumer<AppSearchSession>>... observers) { + if (mShortcutUser == null) { + Slog.w(TAG, "shortcut user is null"); + return; + } + synchronized (mLock) { + if (mAppSearchSession != null) { + final CountDownLatch latch = new CountDownLatch(1); + final long callingIdentity = Binder.clearCallingIdentity(); + try { + final SearchSessionObservable upstream = + new SearchSessionObservable(mAppSearchSession, latch); + for (Function<SearchSessionObservable, Consumer<AppSearchSession>> observer + : observers) { + upstream.map(observer); + } + upstream.next(); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + ConcurrentUtils.waitForCountDownNoInterrupt(latch, 500, + "timeout accessing shortcut"); + } else { + resetAppSearch(observers); + } + } + } + + private void resetAppSearch( + Function<SearchSessionObservable, Consumer<AppSearchSession>>... observers) { + final CountDownLatch latch = new CountDownLatch(1); + final AppSearchManager.SearchContext searchContext = + new AppSearchManager.SearchContext.Builder() + .setDatabaseName(getPackageName()).build(); + mShortcutUser.runInAppSearch(searchContext, result -> { + if (!result.isSuccess()) { + Slog.e(TAG, "error getting search session during lazy init, " + + result.getErrorMessage()); + latch.countDown(); + return; + } + // TODO: Flatten callback chain with proper async framework + final SearchSessionObservable upstream = + new SearchSessionObservable(result.getResultValue(), latch) + .map(this::setupSchema); + if (observers != null) { + for (Function<SearchSessionObservable, Consumer<AppSearchSession>> observer + : observers) { + upstream.map(observer); + } + } + upstream.map(observable -> session -> { + mAppSearchSession = session; + observable.next(); + }); + upstream.next(); + }); + ConcurrentUtils.waitForCountDownNoInterrupt(latch, 1500, + "timeout accessing shortcut during lazy initialization"); + } + + /** + * creates the schema for shortcut in the database + */ + private Consumer<AppSearchSession> setupSchema(SearchSessionObservable observable) { + return session -> { + SetSchemaRequest.Builder schemaBuilder = new SetSchemaRequest.Builder() + .addSchemas(AppSearchPerson.SCHEMA, AppSearchShortcutInfo.SCHEMA); + for (PackageIdentifier pi : mPackageIdentifiers.values()) { + schemaBuilder = schemaBuilder + .setSchemaTypeVisibilityForPackage( + AppSearchPerson.SCHEMA_TYPE, true, pi) + .setSchemaTypeVisibilityForPackage( + AppSearchShortcutInfo.SCHEMA_TYPE, true, pi); + } + session.setSchema(schemaBuilder.build(), mShortcutUser.mExecutor, result -> { + if (!result.isSuccess()) { + observable.error("failed to instantiate app search schema: " + + result.getErrorMessage()); + return; + } + observable.next(); + }); + }; + } + + /** + * TODO: Replace this temporary implementation with proper async framework + */ + private class SearchSessionObservable { + + final AppSearchSession mSession; + final CountDownLatch mLatch; + final ArrayList<Consumer<AppSearchSession>> mObservers = new ArrayList<>(1); + + SearchSessionObservable(@NonNull final AppSearchSession session, + @NonNull final CountDownLatch latch) { + mSession = session; + mLatch = latch; + } + + SearchSessionObservable map( + Function<SearchSessionObservable, Consumer<AppSearchSession>> observer) { + mObservers.add(observer.apply(this)); + return this; + } + + void next() { + if (mObservers.isEmpty()) { + mLatch.countDown(); + return; + } + mObservers.remove(0).accept(mSession); + } + + void error(@Nullable final String errorMessage) { + if (errorMessage != null) { + Slog.e(TAG, errorMessage); + } + mLatch.countDown(); + } + } } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 4d8abea8acd4..f84eb4437acf 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -662,7 +662,7 @@ public class ShortcutService extends IShortcutService.Stub { /** lifecycle event */ void handleUnlockUser(int userId) { if (DEBUG) { - Slog.d(TAG, "handleUnlockUser: user=" + userId); + Slog.d(TAG, "handleUnlockUser: user=" + userId); } synchronized (mUnlockedUsers) { mUnlockedUsers.put(userId, true); @@ -1179,6 +1179,14 @@ public class ShortcutService extends IShortcutService.Stub { } } + void postValue(@NonNull final ShortcutInfo shortcutInfo, + @NonNull final Consumer<ShortcutInfo> cb) { + final String pkg = shortcutInfo.getPackage(); + final int userId = shortcutInfo.getUserId(); + final String id = shortcutInfo.getId(); + getPackageShortcutsLocked(pkg, userId).mutateShortcut(id, shortcutInfo, cb); + } + /** Return the last reset time. */ @GuardedBy("mLock") long getLastResetTimeLocked() { @@ -1566,7 +1574,6 @@ public class ShortcutService extends IShortcutService.Stub { * resource-based strings. */ void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) { - // TODO: update resource names in AppSearch final Resources publisherRes = injectGetResourcesForApplicationAsUser( si.getPackage(), si.getUserId()); if (publisherRes != null) { @@ -1947,7 +1954,7 @@ public class ShortcutService extends IShortcutService.Stub { final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission( injectBinderCallingPid(), injectBinderCallingUid()); - List<ShortcutInfo> changedShortcuts = null; + final List<ShortcutInfo> changedShortcuts = new ArrayList<>(1); synchronized (mLock) { throwIfUserLockedL(userId); @@ -1975,59 +1982,57 @@ public class ShortcutService extends IShortcutService.Stub { final ShortcutInfo source = newShortcuts.get(i); fixUpIncomingShortcutInfo(source, /* forUpdate= */ true); - final ShortcutInfo target = ps.findShortcutById(source.getId()); - - // Invisible shortcuts can't be updated. - if (target == null || !target.isVisibleToPublisher()) { - continue; - } + ps.mutateShortcut(source.getId(), null, target -> { + // Invisible shortcuts can't be updated. + if (target == null || !target.isVisibleToPublisher()) { + return; + } - if (target.isEnabled() != source.isEnabled()) { - Slog.w(TAG, - "ShortcutInfo.enabled cannot be changed with updateShortcuts()"); - } + if (target.isEnabled() != source.isEnabled()) { + Slog.w(TAG, + "ShortcutInfo.enabled cannot be changed with updateShortcuts()"); + } - if (target.isLongLived() != source.isLongLived()) { - Slog.w(TAG, - "ShortcutInfo.longLived cannot be changed with updateShortcuts()"); - } + if (target.isLongLived() != source.isLongLived()) { + Slog.w(TAG, + "ShortcutInfo.longLived cannot be changed with updateShortcuts()"); + } - // When updating the rank, we need to insert between existing ranks, so set - // this setRankChanged, and also copy the implicit rank fo adjustRanks(). - if (source.hasRank()) { - target.setRankChanged(); - target.setImplicitRank(source.getImplicitRank()); - } + // When updating the rank, we need to insert between existing ranks, so set + // this setRankChanged, and also copy the implicit rank fo adjustRanks(). + if (source.hasRank()) { + target.setRankChanged(); + target.setImplicitRank(source.getImplicitRank()); + } - final boolean replacingIcon = (source.getIcon() != null); - if (replacingIcon) { - removeIconLocked(target); - } + final boolean replacingIcon = (source.getIcon() != null); + if (replacingIcon) { + removeIconLocked(target); + } - // Note copyNonNullFieldsFrom() does the "updatable with?" check too. - target.copyNonNullFieldsFrom(source); - target.setTimestamp(injectCurrentTimeMillis()); + // Note copyNonNullFieldsFrom() does the "updatable with?" check too. + target.copyNonNullFieldsFrom(source); + target.setTimestamp(injectCurrentTimeMillis()); - if (replacingIcon) { - saveIconAndFixUpShortcutLocked(target); - } + if (replacingIcon) { + saveIconAndFixUpShortcutLocked(target); + } - // When we're updating any resource related fields, re-extract the res names and - // the values. - if (replacingIcon || source.hasStringResources()) { - fixUpShortcutResourceNamesAndValues(target); - } + // When we're updating any resource related fields, re-extract the res names and + // the values. + if (replacingIcon || source.hasStringResources()) { + fixUpShortcutResourceNamesAndValues(target); + } - if (changedShortcuts == null) { - changedShortcuts = new ArrayList<>(1); - } - changedShortcuts.add(target); + changedShortcuts.add(target); + }); } // Lastly, adjust the ranks. ps.adjustRanks(); } - packageShortcutsChanged(packageName, userId, changedShortcuts, null); + packageShortcutsChanged(packageName, userId, + changedShortcuts.isEmpty() ? null : changedShortcuts, null); verifyStates(); @@ -2150,6 +2155,15 @@ public class ShortcutService extends IShortcutService.Stub { } @Override + public void updateShortcutVisibility(String callingPkg, String packageName, byte[] certificate, + boolean visible, int userId) { + synchronized (mLock) { + getPackageShortcutsForPublisherLocked(callingPkg, userId) + .updateVisibility(packageName, certificate, visible); + } + } + + @Override public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut, IntentSender resultIntent, int userId) { Objects.requireNonNull(shortcut); @@ -3105,7 +3119,8 @@ public class ShortcutService extends IShortcutService.Stub { if (doCache) { if (si.isLongLived()) { - si.addFlags(cacheFlags); + sp.mutateShortcut(si.getId(), si, + shortcut -> shortcut.addFlags(cacheFlags)); if (changedShortcuts == null) { changedShortcuts = new ArrayList<>(1); } @@ -3116,7 +3131,8 @@ public class ShortcutService extends IShortcutService.Stub { } } else { ShortcutInfo removed = null; - si.clearFlags(cacheFlags); + sp.mutateShortcut(si.getId(), si, shortcut -> + shortcut.clearFlags(cacheFlags)); if (!si.isDynamic() && !si.isCached()) { removed = sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true); } diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java index 3e3aa677912b..ec784d0211dd 100644 --- a/services/core/java/com/android/server/pm/ShortcutUser.java +++ b/services/core/java/com/android/server/pm/ShortcutUser.java @@ -18,9 +18,14 @@ package com.android.server.pm; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.appsearch.AppSearchManager; +import android.app.appsearch.AppSearchResult; +import android.app.appsearch.AppSearchSession; import android.content.pm.ShortcutManager; import android.metrics.LogMaker; +import android.os.Binder; import android.os.FileUtils; +import android.os.UserHandle; import android.text.TextUtils; import android.text.format.Formatter; import android.util.ArrayMap; @@ -32,6 +37,7 @@ import android.util.TypedXmlSerializer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.server.FgThread; import com.android.server.pm.ShortcutService.DumpFilter; import com.android.server.pm.ShortcutService.InvalidFileFormatException; @@ -45,6 +51,7 @@ import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.Objects; +import java.util.concurrent.Executor; import java.util.function.Consumer; /** @@ -111,6 +118,8 @@ class ShortcutUser { } final ShortcutService mService; + final AppSearchManager mAppSearchManager; + final Executor mExecutor; @UserIdInt private final int mUserId; @@ -132,6 +141,9 @@ class ShortcutUser { public ShortcutUser(ShortcutService service, int userId) { mService = service; mUserId = userId; + mAppSearchManager = service.mContext.createContextAsUser(UserHandle.of(userId), 0) + .getSystemService(AppSearchManager.class); + mExecutor = FgThread.getExecutor(); } public int getUserId() { @@ -173,6 +185,9 @@ class ShortcutUser { public ShortcutPackage removePackage(@NonNull String packageName) { final ShortcutPackage removed = mPackages.remove(packageName); + if (removed != null) { + removed.removeShortcuts(); + } mService.cleanupBitmapsForPackage(mUserId, packageName); return removed; @@ -318,7 +333,10 @@ class ShortcutUser { if (!shortcutPackage.rescanPackageIfNeeded(isNewApp, forceRescan)) { if (isNewApp) { - mPackages.remove(packageName); + final ShortcutPackage sp = mPackages.remove(packageName); + if (sp != null) { + sp.removeShortcuts(); + } } } } @@ -442,6 +460,7 @@ class ShortcutUser { case ShortcutPackage.TAG_ROOT: { final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml( s, ret, parser, fromBackup); + shortcuts.restoreParsedShortcuts(false); // Don't use addShortcut(), we don't need to save the icon. ret.mPackages.put(shortcuts.getPackageName(), shortcuts); @@ -476,6 +495,7 @@ class ShortcutUser { final ShortcutPackage sp = ShortcutPackage.loadFromFile(s, ret, f, fromBackup); if (sp != null) { ret.mPackages.put(sp.getPackageName(), sp); + sp.restoreParsedShortcuts(false); } }); @@ -558,6 +578,7 @@ class ShortcutUser { Log.w(TAG, "Shortcuts for package " + sp.getPackageName() + " are being restored." + " Existing non-manifeset shortcuts will be overwritten."); } + sp.restoreParsedShortcuts(true); addPackage(sp); restoredPackages[0]++; restoredShortcuts[0] += sp.getShortcutCount(); @@ -693,4 +714,18 @@ class ShortcutUser { logger.write(logMaker.setType(MetricsEvent.SHORTCUTS_CHANGED_SHORTCUT_COUNT) .setSubtype(totalSharingShortcutCount)); } + + void runInAppSearch(@NonNull final AppSearchManager.SearchContext searchContext, + @NonNull final Consumer<AppSearchResult<AppSearchSession>> callback) { + if (mAppSearchManager == null) { + Slog.e(TAG, "app search manager is null"); + return; + } + final long callingIdentity = Binder.clearCallingIdentity(); + try { + mAppSearchManager.createSearchSession(searchContext, mExecutor, callback); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } } diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java index c17d2e4b0c62..eb2de6012745 100644 --- a/services/core/java/com/android/server/pm/UserManagerInternal.java +++ b/services/core/java/com/android/server/pm/UserManagerInternal.java @@ -63,8 +63,13 @@ public abstract class UserManagerInternal { */ public interface UserLifecycleListener { - /** Called when a new user is created. */ - default void onUserCreated(UserInfo user) {} + /** + * Called when a new user is created. + * + * @param user new user. + * @param token token passed to the method that created the user. + */ + default void onUserCreated(UserInfo user, @Nullable Object token) {} /** Called when an existing user is removed. */ default void onUserRemoved(UserInfo user) {} @@ -179,10 +184,12 @@ public abstract class UserManagerInternal { * {@link UserManager#DISALLOW_ADD_USER} and {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE} * * <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when - * createAndManageUser is called by the device owner. + * createAndManageUser is called by the device owner; it uses {@code token} to block until + * the user is created (as it will be passed back to it through + * {@link UserLifecycleListener#onUserCreated(UserInfo, Object)}); */ public abstract UserInfo createUserEvenWhenDisallowed(String name, String userType, - int flags, String[] disallowedPackages) + int flags, String[] disallowedPackages, @Nullable Object token) throws UserManager.CheckedUserOperationException; /** diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 24c27bedb9f7..871576e7c795 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -983,7 +983,11 @@ public class UserManagerService extends IUserManager.Stub { @Override public UserInfo getProfileParent(@UserIdInt int userId) { - checkManageUsersPermission("get the profile parent"); + if (!hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) { + throw new SecurityException( + "You need MANAGE_USERS or INTERACT_ACROSS_USERS permission to get the " + + "profile parent"); + } synchronized (mUsersLock) { return getProfileParentLU(userId); } @@ -1522,20 +1526,30 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean isUserForeground() { - int callingUserId = Binder.getCallingUserHandle().getIdentifier(); + public boolean isUserForeground(@UserIdInt int userId) { + final int callingUserId = UserHandle.getCallingUserId(); + if (callingUserId != userId + && !hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) { + throw new SecurityException("Caller from user " + callingUserId + " needs MANAGE_USERS " + + "or INTERACT_ACROSS_USERS permission to check if another user (" + userId + + ") is running in the foreground"); + } + int currentUser = Binder.withCleanCallingIdentity(() -> ActivityManager.getCurrentUser()); // TODO(b/179163496): should return true for profile users of the current user as well - return currentUser == callingUserId; + return currentUser == userId; } @Override public String getUserName() { - if (!hasManageUsersOrPermission(android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED)) { - throw new SecurityException("You need MANAGE_USERS or GET_ACCOUNTS_PRIVILEGED " - + "permissions to: get user name"); + final int callingUid = Binder.getCallingUid(); + if (!hasManageOrCreateUsersPermission() + || hasPermissionGranted( + android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, callingUid)) { + throw new SecurityException("You need MANAGE_USERS or CREATE_USERS or " + + "GET_ACCOUNTS_PRIVILEGED permissions to: get user name"); } - final int userId = UserHandle.getUserId(Binder.getCallingUid()); + final int userId = UserHandle.getUserId(callingUid); synchronized (mUsersLock) { UserInfo userInfo = userWithName(getUserInfoLU(userId)); return userInfo == null ? "" : userInfo.name; @@ -3287,7 +3301,7 @@ public class UserManagerService extends IUserManager.Stub { * as well as for {@link UserManager#USER_TYPE_FULL_RESTRICTED}. */ @Override - public UserInfo createProfileForUserWithThrow(String name, @NonNull String userType, + public UserInfo createProfileForUserWithThrow(@Nullable String name, @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) throws ServiceSpecificException { checkManageOrCreateUsersPermission(flags); @@ -3309,7 +3323,7 @@ public class UserManagerService extends IUserManager.Stub { checkManageOrCreateUsersPermission(flags); try { return createUserInternalUnchecked(name, userType, flags, userId, - /* preCreate= */ false, disallowedPackages); + /* preCreate= */ false, disallowedPackages, /* token= */ null); } catch (UserManager.CheckedUserOperationException e) { throw e.toServiceSpecificException(); } @@ -3342,7 +3356,7 @@ public class UserManagerService extends IUserManager.Stub { try { return createUserInternalUnchecked(/* name= */ null, userType, flags, /* parentId= */ UserHandle.USER_NULL, /* preCreate= */ true, - /* disallowedPackages= */ null); + /* disallowedPackages= */ null, /* token= */ null); } catch (UserManager.CheckedUserOperationException e) { throw e.toServiceSpecificException(); } @@ -3358,12 +3372,13 @@ public class UserManagerService extends IUserManager.Stub { enforceUserRestriction(restriction, UserHandle.getCallingUserId(), "Cannot add user"); return createUserInternalUnchecked(name, userType, flags, parentId, - /* preCreate= */ false, disallowedPackages); + /* preCreate= */ false, disallowedPackages, /* token= */ null); } private UserInfo createUserInternalUnchecked(@Nullable String name, @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId, - boolean preCreate, @Nullable String[] disallowedPackages) + boolean preCreate, @Nullable String[] disallowedPackages, + @Nullable Object token) throws UserManager.CheckedUserOperationException { final int nextProbableUserId = getNextAvailableId(); final TimingsTraceAndSlog t = new TimingsTraceAndSlog(); @@ -3372,7 +3387,7 @@ public class UserManagerService extends IUserManager.Stub { UserInfo newUser = null; try { newUser = createUserInternalUncheckedNoTracing(name, userType, flags, parentId, - preCreate, disallowedPackages, t); + preCreate, disallowedPackages, t, token); return newUser; } finally { logUserCreateJourneyFinish(sessionId, nextProbableUserId, newUser != null); @@ -3383,7 +3398,8 @@ public class UserManagerService extends IUserManager.Stub { private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name, @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId, boolean preCreate, @Nullable String[] disallowedPackages, - @NonNull TimingsTraceAndSlog t) throws UserManager.CheckedUserOperationException { + @NonNull TimingsTraceAndSlog t, @Nullable Object token) + throws UserManager.CheckedUserOperationException { final UserTypeDetails userTypeDetails = mUserTypes.get(userType); if (userTypeDetails == null) { Slog.e(LOG_TAG, "Cannot create user of invalid user type: " + userType); @@ -3409,7 +3425,8 @@ public class UserManagerService extends IUserManager.Stub { // Try to use a pre-created user (if available). if (!preCreate && parentId < 0 && isUserTypeEligibleForPreCreation(userTypeDetails)) { - final UserInfo preCreatedUser = convertPreCreatedUserIfPossible(userType, flags, name); + final UserInfo preCreatedUser = convertPreCreatedUserIfPossible(userType, flags, name, + token); if (preCreatedUser != null) { return preCreatedUser; } @@ -3588,7 +3605,7 @@ public class UserManagerService extends IUserManager.Stub { Slog.w(LOG_TAG, "could not start pre-created user " + userId, e); } } else { - dispatchUserAdded(userInfo); + dispatchUserAdded(userInfo, token); } } finally { @@ -3688,7 +3705,7 @@ public class UserManagerService extends IUserManager.Stub { * @return the converted user, or {@code null} if no pre-created user could be converted. */ private @Nullable UserInfo convertPreCreatedUserIfPossible(String userType, - @UserInfoFlag int flags, String name) { + @UserInfoFlag int flags, String name, @Nullable Object token) { final UserData preCreatedUserData; synchronized (mUsersLock) { preCreatedUserData = getPreCreatedUserLU(userType); @@ -3726,7 +3743,7 @@ public class UserManagerService extends IUserManager.Stub { } updateUserIds(); mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true); - dispatchUserAdded(preCreatedUser); + dispatchUserAdded(preCreatedUser, token); return preCreatedUser; } @@ -3758,11 +3775,11 @@ public class UserManagerService extends IUserManager.Stub { return (now > EPOCH_PLUS_30_YEARS) ? now : 0; } - private void dispatchUserAdded(@NonNull UserInfo userInfo) { + private void dispatchUserAdded(@NonNull UserInfo userInfo, @Nullable Object token) { // Notify internal listeners first... synchronized (mUserLifecycleListeners) { for (int i = 0; i < mUserLifecycleListeners.size(); i++) { - mUserLifecycleListeners.get(i).onUserCreated(userInfo); + mUserLifecycleListeners.get(i).onUserCreated(userInfo, token); } } @@ -3868,7 +3885,7 @@ public class UserManagerService extends IUserManager.Stub { * @hide */ @Override - public UserInfo createRestrictedProfileWithThrow(String name, int parentUserId) { + public UserInfo createRestrictedProfileWithThrow(@Nullable String name, int parentUserId) { checkManageOrCreateUsersPermission("setupRestrictedProfile"); final UserInfo user = createProfileForUserWithThrow( name, UserManager.USER_TYPE_FULL_RESTRICTED, 0, parentUserId, null); @@ -5367,10 +5384,10 @@ public class UserManagerService extends IUserManager.Stub { @Override public UserInfo createUserEvenWhenDisallowed(String name, @NonNull String userType, - @UserInfoFlag int flags, String[] disallowedPackages) + @UserInfoFlag int flags, String[] disallowedPackages, @Nullable Object token) throws UserManager.CheckedUserOperationException { return createUserInternalUnchecked(name, userType, flags, - UserHandle.USER_NULL, /* preCreated= */ false, disallowedPackages); + UserHandle.USER_NULL, /* preCreated= */ false, disallowedPackages, token); } @Override diff --git a/services/core/java/com/android/server/pm/dex/ArtPackageInfo.java b/services/core/java/com/android/server/pm/dex/ArtPackageInfo.java new file mode 100644 index 000000000000..50bf916dceb3 --- /dev/null +++ b/services/core/java/com/android/server/pm/dex/ArtPackageInfo.java @@ -0,0 +1,58 @@ +/* + * 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.dex; + +import java.util.List; + +/** + * Holds package information relevant to ART use cases. + */ +public class ArtPackageInfo { + private final String mPackageName; + private final List<String> mInstructionSets; + private final List<String> mCodePaths; + // TODO: This should be computed on the fly in PackageDexOptimizer / DexManager, but the + // logic is too complicated to do it in a single re-factoring. + private final String mOatDir; + + public ArtPackageInfo( + String packageName, + List<String> instructionSets, + List<String> codePaths, + String oatDir) { + mPackageName = packageName; + mInstructionSets = instructionSets; + mCodePaths = codePaths; + mOatDir = oatDir; + } + + public String getPackageName() { + return mPackageName; + } + + public List<String> getInstructionSets() { + return mInstructionSets; + } + + public List<String> getCodePaths() { + return mCodePaths; + } + + public String getOatDir() { + return mOatDir; + } +} diff --git a/services/core/java/com/android/server/pm/dex/ArtUtils.java b/services/core/java/com/android/server/pm/dex/ArtUtils.java new file mode 100644 index 000000000000..16d7a9a0afd8 --- /dev/null +++ b/services/core/java/com/android/server/pm/dex/ArtUtils.java @@ -0,0 +1,64 @@ +/* + * 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.dex; + +import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; + +import android.annotation.NonNull; + +import com.android.server.pm.PackageDexOptimizer; +import com.android.server.pm.PackageSetting; +import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.parsing.pkg.AndroidPackageUtils; + +import java.io.File; +import java.util.Arrays; + +/** + * Utility class to interface between PM and ART tooling (e.g. DexManager). + */ +public final class ArtUtils { + private ArtUtils() { + } + + /** + * Create the ART-representation of package info. + */ + public static ArtPackageInfo createArtPackageInfo( + AndroidPackage pkg, PackageSetting pkgSetting) { + return new ArtPackageInfo( + pkg.getPackageName(), + Arrays.asList(getAppDexInstructionSets( + AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting), + AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting))), + AndroidPackageUtils.getAllCodePaths(pkg), + getOatDir(pkg, pkgSetting)); + } + + private static String getOatDir(AndroidPackage pkg, @NonNull PackageSetting pkgSetting) { + if (!AndroidPackageUtils.canHaveOatDir(pkg, + pkgSetting.getPkgState().isUpdatedSystemApp())) { + return null; + } + File codePath = new File(pkg.getPath()); + if (codePath.isDirectory()) { + return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath(); + } + return null; + } + +} diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index cc6d80a2aeec..349561d3f1d1 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -215,7 +215,7 @@ public class DexManager { searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT; if (primaryOrSplit && !isUsedByOtherApps - && !PLATFORM_PACKAGE_NAME.equals(searchResult.mOwningPackageName)) { + && !isPlatformPackage(searchResult.mOwningPackageName)) { // If the dex file is the primary apk (or a split) and not isUsedByOtherApps // do not record it. This case does not bring any new usable information // and can be safely skipped. @@ -232,15 +232,24 @@ public class DexManager { } String classLoaderContext = mapping.getValue(); + + // Overwrite the class loader context for system server (instead of merging it). + // We expect system server jars to only change contexts in between OTAs and to + // otherwise be stable. + // Instead of implementing a complex clear-context logic post OTA, it is much + // simpler to always override the context for system server. This way, the context + // will always be up to date and we will avoid merging which could lead to the + // the context being marked as variable and thus making dexopt non-optimal. + boolean overwriteCLC = isPlatformPackage(searchResult.mOwningPackageName); + if (classLoaderContext != null && VMRuntime.isValidClassLoaderContext(classLoaderContext)) { // Record dex file usage. If the current usage is a new pattern (e.g. new // secondary, or UsedByOtherApps), record will return true and we trigger an // async write to disk to make sure we don't loose the data in case of a reboot. - if (mPackageDexUsage.record(searchResult.mOwningPackageName, dexPath, loaderUserId, loaderIsa, primaryOrSplit, - loadingAppInfo.packageName, classLoaderContext)) { + loadingAppInfo.packageName, classLoaderContext, overwriteCLC)) { mPackageDexUsage.maybeWriteAsync(); } } @@ -474,7 +483,7 @@ public class DexManager { * because they don't need to be compiled).. */ public boolean dexoptSecondaryDex(DexoptOptions options) { - if (PLATFORM_PACKAGE_NAME.equals(options.getPackageName())) { + if (isPlatformPackage(options.getPackageName())) { // We could easily redirect to #dexoptSystemServer in this case. But there should be // no-one calling this method directly for system server. // As such we prefer to abort in this case. @@ -534,7 +543,7 @@ public class DexManager { * <p>PackageDexOptimizer.DEX_OPT_PERFORMED if all dexopt operations succeeded. */ public int dexoptSystemServer(DexoptOptions options) { - if (!PLATFORM_PACKAGE_NAME.equals(options.getPackageName())) { + if (!isPlatformPackage(options.getPackageName())) { Slog.wtf(TAG, "Non system server package used when trying to dexopt system server:" + options.getPackageName()); return PackageDexOptimizer.DEX_OPT_FAILED; @@ -662,7 +671,7 @@ public class DexManager { // Special handle system server files. // We don't need an installd call because we have permissions to check if the file // exists. - if (PLATFORM_PACKAGE_NAME.equals(packageName)) { + if (isPlatformPackage(packageName)) { if (!Files.exists(Paths.get(dexPath))) { if (DEBUG) { Slog.w(TAG, "A dex file previously loaded by System Server does not exist " @@ -739,7 +748,8 @@ public class DexManager { boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName, dexPath, userId, isa, /*primaryOrSplit*/ false, loadingPackage, - PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT); + PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT, + /*overwriteCLC*/ false); update |= newUpdate; } if (update) { @@ -809,7 +819,7 @@ public class DexManager { // Note: We don't have any way to detect which code paths are actually // owned by system server. We can only assume that such paths are on // system partitions. - if (PLATFORM_PACKAGE_NAME.equals(loadingAppInfo.packageName)) { + if (isPlatformPackage(loadingAppInfo.packageName)) { if (isSystemServerDexPathSupportedForOdex(dexPath)) { // We record system server dex files as secondary dex files. // The reason is that we only record the class loader context for secondary dex @@ -842,6 +852,11 @@ public class DexManager { return new DexSearchResult(null, DEX_SEARCH_NOT_FOUND); } + /** Returns true if this is the platform package .*/ + private static boolean isPlatformPackage(String packageName) { + return PLATFORM_PACKAGE_NAME.equals(packageName); + } + private static <K,V> V putIfAbsent(Map<K,V> map, K key, V newValue) { V existingValue = map.putIfAbsent(key, newValue); return existingValue == null ? newValue : existingValue; @@ -1000,6 +1015,22 @@ public class DexManager { return isBtmCritical; } + /** + * Deletes all the optimizations files generated by ART. + * @param packageInfo the package information. + */ + public void deleteOptimizedFiles(ArtPackageInfo packageInfo) { + for (String codePath : packageInfo.getCodePaths()) { + for (String isa : packageInfo.getInstructionSets()) { + try { + mInstaller.deleteOdex(codePath, isa, packageInfo.getOatDir()); + } catch (InstallerException e) { + Log.e(TAG, "Failed deleting oat files for " + codePath, e); + } + } + } + } + public static class RegisterDexModuleResult { public RegisterDexModuleResult() { this(false, null); diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java index 10760f52a02b..3d63b75c5da1 100644 --- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java +++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java @@ -111,17 +111,18 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { * @param dexPath the path of the dex files being loaded * @param ownerUserId the user id which runs the code loading the dex files * @param loaderIsa the ISA of the app loading the dex files - * @param isUsedByOtherApps whether or not this dex file was not loaded by its owning package * @param primaryOrSplit whether or not the dex file is a primary/split dex. True indicates * the file is either primary or a split. False indicates the file is secondary dex. * @param loadingPackageName the package performing the load. Recorded only if it is different * than {@param owningPackageName}. + * @param overwriteCLC if true, the class loader context will be overwritten instead of being + * merged * @return true if the dex load constitutes new information, or false if this information * has been seen before. */ /* package */ boolean record(String owningPackageName, String dexPath, int ownerUserId, String loaderIsa, boolean primaryOrSplit, - String loadingPackageName, String classLoaderContext) { + String loadingPackageName, String classLoaderContext, boolean overwriteCLC) { if (!PackageManagerServiceUtils.checkISA(loaderIsa)) { throw new IllegalArgumentException("loaderIsa " + loaderIsa + " is unsupported"); } @@ -193,7 +194,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { } // Merge the information into the existing data. // Returns true if there was an update. - return existingData.merge(newData) || updateLoadingPackages; + return existingData.merge(newData, overwriteCLC) || updateLoadingPackages; } } } @@ -809,14 +810,16 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { mLoadingPackages = new HashSet<>(other.mLoadingPackages); } - private boolean merge(DexUseInfo dexUseInfo) { + private boolean merge(DexUseInfo dexUseInfo, boolean overwriteCLC) { boolean oldIsUsedByOtherApps = mIsUsedByOtherApps; mIsUsedByOtherApps = mIsUsedByOtherApps || dexUseInfo.mIsUsedByOtherApps; boolean updateIsas = mLoaderIsas.addAll(dexUseInfo.mLoaderIsas); boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages); String oldClassLoaderContext = mClassLoaderContext; - if (isUnsupportedContext(mClassLoaderContext)) { + if (overwriteCLC) { + mClassLoaderContext = dexUseInfo.mClassLoaderContext; + } else if (isUnsupportedContext(mClassLoaderContext)) { mClassLoaderContext = dexUseInfo.mClassLoaderContext; } else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) { // We detected a context change. diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 44a2187aed8e..e3ccb7521b58 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -1277,12 +1277,7 @@ final class DefaultPermissionGrantPolicy { newFlags |= (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT); // If we are allowlisting the permission, update the exempt flag before grant. - // If the permission can't be allowlisted by an installer, skip it here because - // this is where the platform takes the role of the installer for exempting - // preinstalled apps. - if (whitelistRestrictedPermissions && pm.isPermissionRestricted(permission) - && !pm.getPermissionInfo(permission).isInstallerExemptIgnored()) { - + if (whitelistRestrictedPermissions && pm.isPermissionRestricted(permission)) { pm.updatePermissionFlags(permission, pkg, PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, user); diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java index 32bee5809b11..ac50f29fbf32 100644 --- a/services/core/java/com/android/server/pm/permission/Permission.java +++ b/services/core/java/com/android/server/pm/permission/Permission.java @@ -239,10 +239,6 @@ public final class Permission { return (mPermissionInfo.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0; } - public boolean isInstallerExemptIgnored() { - return (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED) != 0; - } - public boolean isSignature() { return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) == PermissionInfo.PROTECTION_SIGNATURE; 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 71e53d9f1f40..2dfb6ff3e9a8 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -24,14 +24,12 @@ import static android.app.AppOpsManager.MODE_IGNORED; import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISALLOWED; import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED; import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT; -import static android.content.pm.PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE; import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE; import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; -import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; @@ -68,15 +66,10 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.admin.DevicePolicyManagerInternal; -import android.app.role.RoleManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.PermissionGroupInfoFlags; import android.content.pm.PackageManager.PermissionInfoFlags; @@ -86,7 +79,6 @@ import android.content.pm.PackageParser; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; -import android.content.pm.UserInfo; import android.content.pm.parsing.component.ParsedPermission; import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.permission.SplitPermissionInfoParcelable; @@ -401,105 +393,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { new PermissionManagerServiceInternalImpl(); LocalServices.addService(PermissionManagerServiceInternal.class, localService); LocalServices.addService(PermissionManagerInternal.class, localService); - - context.getMainThreadHandler().post(() -> context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (!Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { - return; - } - - try { - fixBgMicCamera(context); - } catch (Throwable t) { - // Don't crash the system if this fails for any reason. Any intermediate state - // this can leave the permissions in is okay and in the worst case the state is - // the same as before the user rebooted. - Log.e(LOG_TAG, "Unable to fix background permissions", t); - } - } - - - private void fixBgMicCamera(Context context) { - PackageManager pm = context.getPackageManager(); - for (UserInfo userInfo : context.getSystemService(UserManager.class).getUsers()) { - UserHandle user = userInfo.getUserHandle(); - List<String> assistants = context.getSystemService(RoleManager.class) - .getRoleHoldersAsUser(RoleManager.ROLE_ASSISTANT, user); - List<PackageInfo> packages = - pm.getInstalledPackagesAsUser(PackageManager.MATCH_SYSTEM_ONLY - | PackageManager.GET_PERMISSIONS, user.getIdentifier()); - for (PackageInfo packageInfo : packages) { - String[] requestedPermissions = packageInfo.requestedPermissions; - if (requestedPermissions == null) { - continue; - } - for (String permName : requestedPermissions) { - String pkg = packageInfo.packageName; - switch (permName) { - case Manifest.permission.BACKGROUND_CAMERA: - removeFromAllowlistsAndRevoke(pm, pkg, permName, user); - break; - case Manifest.permission.RECORD_BACKGROUND_AUDIO: - if (assistants.contains(pkg)) { - removeFromAllowlistsAndRevokeForAssistant(pm, pkg, permName, - user); - } else { - removeFromAllowlistsAndRevoke(pm, pkg, permName, user); - } - break; - } - } - } - } - } - - private void removeFromAllowlistsAndRevoke(PackageManager pm, String pkg, - String permName, UserHandle user) { - if ((pm.getPermissionFlags(permName, pkg, user) - & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0) { - Slog.i(LOG_TAG, "removing " + pkg + " " + permName + " from all allowlists"); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_WHITELIST_UPGRADE); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_WHITELIST_SYSTEM); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_WHITELIST_INSTALLER); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_ALLOWLIST_ROLE); - } - if (pm.checkPermission(permName, pkg) == PackageManager.PERMISSION_GRANTED) { - Slog.i(LOG_TAG, "revoking " + pkg + " " + permName); - pm.revokeRuntimePermission(pkg, permName, user); - } - } - - private void removeFromAllowlistsAndRevokeForAssistant(PackageManager pm, String pkg, - String permName, UserHandle user) { - int anyNonRoleExempt = - FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT - | FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT - | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - - if ((pm.getPermissionFlags(permName, pkg, user) & anyNonRoleExempt) != 0) { - Slog.i(LOG_TAG, "removing " + pkg + " " + permName - + " from all allowlists except role"); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_WHITELIST_UPGRADE); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_WHITELIST_SYSTEM); - pm.removeWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_WHITELIST_INSTALLER); - } - if ((pm.getPermissionFlags(permName, pkg, user) - & FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT) == 0) { - Slog.i(LOG_TAG, "adding " + pkg + " " + permName - + " to role allowlist"); - pm.addWhitelistedRestrictedPermission(pkg, permName, - FLAG_PERMISSION_ALLOWLIST_ROLE); - } - } - }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED))); } @Override @@ -876,10 +769,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { isRuntimePermission = bp.isRuntime(); - if (bp.isInstallerExemptIgnored()) { - flagValues &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - } - final UidPermissionState uidState = getUidStateLocked(pkg, userId); if (uidState == null) { Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); @@ -1123,8 +1012,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { Preconditions.checkFlagsArgument(flags, PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM - | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER - | PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE); + | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); Preconditions.checkArgumentNonNegative(userId, null); if (UserHandle.getCallingUserId() != userId) { @@ -1148,9 +1036,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { final boolean isCallerInstallerOnRecord = mPackageManagerInt.isCallerInstallerOfRecord(pkg, callingUid); - if ((flags & (PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM - | PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE)) != 0 && !isCallerPrivileged) { - throw new SecurityException("Querying system or role allowlist requires " + if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 + && !isCallerPrivileged) { + throw new SecurityException("Querying system allowlist requires " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } @@ -1192,9 +1080,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; } - if ((flags & PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE) != 0) { - queryFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; - } ArrayList<String> allowlistedPermissions = null; @@ -1287,8 +1172,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { Preconditions.checkFlagsArgument(flags, PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM - | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER - | PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE); + | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); Preconditions.checkArgument(Integer.bitCount(flags) == 1); Preconditions.checkArgumentNonNegative(userId, null); @@ -1314,10 +1198,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { final boolean isCallerInstallerOnRecord = mPackageManagerInt.isCallerInstallerOfRecord(pkg, callingUid); - if ((flags & (PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM - | PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE)) != 0 - && !isCallerPrivileged) { - throw new SecurityException("Modifying system or role allowlist requires " + if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 && !isCallerPrivileged) { + throw new SecurityException("Modifying system allowlist requires " + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); } @@ -3823,15 +3705,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } break; - case FLAG_PERMISSION_ALLOWLIST_ROLE: { - mask |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; - if (permissions != null && permissions.contains(permissionName)) { - newFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; - } else { - newFlags &= ~FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; - } - } - break; } } diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java index 080de73ff933..e3cf67c34dad 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java @@ -42,6 +42,8 @@ public class DomainVerificationCollector { private static final Pattern DOMAIN_NAME_WITH_WILDCARD = Pattern.compile("(\\*\\.)?" + Patterns.DOMAIN_NAME.pattern()); + private static final int MAX_DOMAINS_BYTE_SIZE = 1024 * 1024; + @NonNull private final PlatformCompat mPlatformCompat; @@ -71,7 +73,7 @@ public class DomainVerificationCollector { * <li>- Only IntentFilter.SCHEME_HTTP and/or IntentFilter.SCHEME_HTTPS, * with no other schemes</li> * </ul> - * + * <p> * On prior versions of Android, Intent.CATEGORY_BROWSABLE was not a requirement, other * schemes were allowed, and setting autoVerify to true in any intent filter would implicitly * pretend that all intent filters were set to autoVerify="true". @@ -86,8 +88,8 @@ public class DomainVerificationCollector { } /** - * Effectively {@link #collectAllWebDomains(AndroidPackage)}, but requires - * {@link IntentFilter#getAutoVerify()} == true. + * Effectively {@link #collectAllWebDomains(AndroidPackage)}, but requires {@link + * IntentFilter#getAutoVerify()} == true. */ @NonNull public ArraySet<String> collectAutoVerifyDomains(@NonNull AndroidPackage pkg) { @@ -100,24 +102,21 @@ public class DomainVerificationCollector { boolean restrictDomains = DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, RESTRICT_DOMAINS); - ArraySet<String> domains = new ArraySet<>(); - if (restrictDomains) { - collectDomains(domains, pkg, checkAutoVerify); + return collectDomainsInternal(pkg, checkAutoVerify); } else { - collectDomainsLegacy(domains, pkg, checkAutoVerify); + return collectDomainsLegacy(pkg, checkAutoVerify); } - - return domains; } - /** @see #RESTRICT_DOMAINS */ - private void collectDomainsLegacy(@NonNull Set<String> domains, - @NonNull AndroidPackage pkg, boolean checkAutoVerify) { + /** + * @see #RESTRICT_DOMAINS + */ + private ArraySet<String> collectDomainsLegacy(@NonNull AndroidPackage pkg, + boolean checkAutoVerify) { if (!checkAutoVerify) { // Per-domain user selection state doesn't have a V1 equivalent on S, so just use V2 - collectDomains(domains, pkg, false); - return; + return collectDomainsInternal(pkg, false); } List<ParsedActivity> activities = pkg.getActivities(); @@ -140,39 +139,54 @@ public class DomainVerificationCollector { } if (!needsAutoVerify) { - return; + return new ArraySet<>(); } } - for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) { + ArraySet<String> domains = new ArraySet<>(); + int totalSize = 0; + boolean underMaxSize = true; + for (int activityIndex = 0; activityIndex < activitiesSize && underMaxSize; + activityIndex++) { ParsedActivity activity = activities.get(activityIndex); List<ParsedIntentInfo> intents = activity.getIntents(); int intentsSize = intents.size(); - for (int intentIndex = 0; intentIndex < intentsSize; intentIndex++) { + for (int intentIndex = 0; intentIndex < intentsSize && underMaxSize; intentIndex++) { ParsedIntentInfo intent = intents.get(intentIndex); if (intent.handlesWebUris(false)) { int authorityCount = intent.countDataAuthorities(); for (int index = 0; index < authorityCount; index++) { String host = intent.getDataAuthority(index).getHost(); if (isValidHost(host)) { + totalSize += byteSizeOf(host); + underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE; domains.add(host); } } } } } + + return domains; } - /** @see #RESTRICT_DOMAINS */ - private void collectDomains(@NonNull Set<String> domains, - @NonNull AndroidPackage pkg, boolean checkAutoVerify) { + /** + * @see #RESTRICT_DOMAINS + */ + private ArraySet<String> collectDomainsInternal(@NonNull AndroidPackage pkg, + boolean checkAutoVerify) { + ArraySet<String> domains = new ArraySet<>(); + int totalSize = 0; + boolean underMaxSize = true; + List<ParsedActivity> activities = pkg.getActivities(); int activitiesSize = activities.size(); - for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) { + for (int activityIndex = 0; activityIndex < activitiesSize && underMaxSize; + activityIndex++) { ParsedActivity activity = activities.get(activityIndex); List<ParsedIntentInfo> intents = activity.getIntents(); int intentsSize = intents.size(); - for (int intentIndex = 0; intentIndex < intentsSize; intentIndex++) { + for (int intentIndex = 0; intentIndex < intentsSize && underMaxSize; intentIndex++) { ParsedIntentInfo intent = intents.get(intentIndex); if (checkAutoVerify && !intent.getAutoVerify()) { continue; @@ -198,14 +212,27 @@ public class DomainVerificationCollector { // app developer by declaring a separate intent-filter. This may not be worth // fixing. int authorityCount = intent.countDataAuthorities(); - for (int index = 0; index < authorityCount; index++) { + for (int index = 0; index < authorityCount && underMaxSize; index++) { String host = intent.getDataAuthority(index).getHost(); if (isValidHost(host)) { + totalSize += byteSizeOf(host); + underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE; domains.add(host); } } } } + + return domains; + } + + /** + * Ballpark the size of domains to avoid a ridiculous amount of domains that could slow + * down client-server communication. + */ + private int byteSizeOf(String string) { + // Use the same method from core for the data objects so that restrictions are consistent + return android.content.pm.verify.domain.DomainVerificationUtils.estimatedByteSizeOf(string); } /** diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java index 275dd053fdde..ed37fa0da01f 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java @@ -185,6 +185,29 @@ public class DomainVerificationEnforcer { return !mCallback.filterAppAccess(packageName, callingUid, targetUserId); } + /** + * Querying for the owners of a domain. Because this API cannot filter the returned list of + * packages, enforces {@link android.Manifest.permission.QUERY_ALL_PACKAGES}, but also enforces + * {@link android.Manifest.permission.INTERACT_ACROSS_USERS} because each user has a different + * state. + */ + public void assertOwnerQuerent(int callingUid, @UserIdInt int callingUserId, + @UserIdInt int targetUserId) { + final int callingPid = Binder.getCallingPid(); + if (callingUserId != targetUserId) { + mContext.enforcePermission(android.Manifest.permission.INTERACT_ACROSS_USERS, + callingPid, callingUid, "Caller is not allowed to query other users"); + } + + mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES, + callingPid, callingUid, "Caller " + callingUid + " does not hold " + + android.Manifest.permission.QUERY_ALL_PACKAGES); + + mContext.enforcePermission( + android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION, + callingPid, callingUid, "Caller is not allowed to query user selections"); + } + public interface Callback { /** * @return true if access to the given package should be filtered and the method failed as diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java index 5d4370ae5dd4..9e22d82910df 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java @@ -310,7 +310,7 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan */ @ApprovalLevel int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent, - @UserIdInt int userId); + @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId); /** * @return the domain verification set ID for the given package, or null if the ID is @@ -358,5 +358,8 @@ public interface DomainVerificationManagerInternal extends DomainVerificationMan @Nullable AndroidPackage getPackageLocked(@NonNull String pkgName); + + @UserIdInt + int[] getAllUserIds(); } } diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java index 8aa63372b826..6f2810785c60 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java @@ -20,13 +20,14 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.verify.domain.DomainOwner; +import android.content.pm.verify.domain.DomainSet; +import android.content.pm.verify.domain.DomainVerificationInfo; import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException; import android.content.pm.verify.domain.DomainVerificationManagerImpl; -import android.content.pm.verify.domain.DomainVerificationInfo; import android.content.pm.verify.domain.DomainVerificationUserSelection; import android.content.pm.verify.domain.IDomainVerificationManager; import android.os.ServiceSpecificException; -import android.util.ArraySet; import java.util.List; import java.util.UUID; @@ -61,11 +62,11 @@ class DomainVerificationManagerStub extends IDomainVerificationManager.Stub { } @Override - public void setDomainVerificationStatus(String domainSetId, List<String> domains, + public void setDomainVerificationStatus(String domainSetId, @NonNull DomainSet domainSet, int state) { try { mService.setDomainVerificationStatus(UUID.fromString(domainSetId), - new ArraySet<>(domains), state); + domainSet.getDomains(), state); } catch (Exception e) { throw rethrow(e); } @@ -82,11 +83,11 @@ class DomainVerificationManagerStub extends IDomainVerificationManager.Stub { } @Override - public void setDomainVerificationUserSelection(String domainSetId, List<String> domains, + public void setDomainVerificationUserSelection(String domainSetId, @NonNull DomainSet domainSet, boolean enabled, @UserIdInt int userId) { try { mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId), - new ArraySet<>(domains), enabled, userId); + domainSet.getDomains(), enabled, userId); } catch (Exception e) { throw rethrow(e); } @@ -103,6 +104,17 @@ class DomainVerificationManagerStub extends IDomainVerificationManager.Stub { } } + @Nullable + @Override + public List<DomainOwner> getOwnersForDomain(@NonNull String domain, + @UserIdInt int userId) { + try { + return mService.getOwnersForDomain(domain, userId); + } catch (Exception e) { + throw rethrow(e); + } + } + private RuntimeException rethrow(Exception exception) throws RuntimeException { if (exception instanceof InvalidDomainSetException) { int packedErrorCode = DomainVerificationManagerImpl.ERROR_INVALID_DOMAIN_SET; 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 e5ed774fcaa8..b58c1ff374d5 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 @@ -17,6 +17,7 @@ package com.android.server.pm.verify.domain; import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; import android.annotation.NonNull; import android.annotation.Nullable; @@ -30,6 +31,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.parsing.component.ParsedActivity; +import android.content.pm.verify.domain.DomainOwner; import android.content.pm.verify.domain.DomainVerificationInfo; import android.content.pm.verify.domain.DomainVerificationManager; import android.content.pm.verify.domain.DomainVerificationState; @@ -291,6 +293,8 @@ public class DomainVerificationService extends SystemService throws InvalidDomainSetException, NameNotFoundException { mEnforcer.assertApprovedVerifier(callingUid, mProxy); synchronized (mLock) { + List<String> verifiedDomains = new ArrayList<>(); + DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains, true /* forAutoVerify */, callingUid, null /* userId */); ArrayMap<String, Integer> stateMap = pkgState.getStateMap(); @@ -301,8 +305,17 @@ public class DomainVerificationService extends SystemService continue; } + if (DomainVerificationManager.isStateVerified(state)) { + verifiedDomains.add(domain); + } + stateMap.put(domain, state); } + + int size = verifiedDomains.size(); + for (int index = 0; index < size; index++) { + removeUserSelectionsForDomain(verifiedDomains.get(index)); + } } mConnection.scheduleWriteSettings(); @@ -387,6 +400,20 @@ public class DomainVerificationService extends SystemService } } + private void removeUserSelectionsForDomain(@NonNull String domain) { + synchronized (mLock) { + final int size = mAttachedPkgStates.size(); + for (int index = 0; index < size; index++) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); + SparseArray<DomainVerificationUserState> array = pkgState.getUserSelectionStates(); + int arraySize = array.size(); + for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { + array.valueAt(arrayIndex).removeHost(domain); + } + } + } + } + @Override public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName, boolean allowed) throws NameNotFoundException { @@ -423,12 +450,8 @@ public class DomainVerificationService extends SystemService for (int pkgStateIndex = 0; pkgStateIndex < pkgStateSize; pkgStateIndex++) { DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(pkgStateIndex); if (userId == UserHandle.USER_ALL) { - SparseArray<DomainVerificationUserState> userStates = - pkgState.getUserSelectionStates(); - int userStatesSize = userStates.size(); - for (int userStateIndex = 0; userStateIndex < userStatesSize; - userStateIndex++) { - userStates.valueAt(userStateIndex) + for (int aUserId : mConnection.getAllUserIds()) { + pkgState.getOrCreateUserSelectionState(aUserId) .setLinkHandlingAllowed(allowed); } } else { @@ -436,7 +459,6 @@ public class DomainVerificationService extends SystemService .setLinkHandlingAllowed(allowed); } } - } } else { synchronized (mLock) { @@ -475,19 +497,59 @@ public class DomainVerificationService extends SystemService InvalidDomainSetException.REASON_ID_INVALID); } + DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains, + false /* forAutoVerify */, callingUid, userId); + DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId); + + // Disable other packages if approving this one. Note that this check is only done for + // enabling. This allows an escape hatch in case multiple packages somehow get selected. + // They can be disabled without blocking in a circular dependency. if (enabled) { + // Cache the approved packages from the 1st pass because the search is expensive + ArrayMap<String, List<String>> domainToApprovedPackages = new ArrayMap<>(); + for (String domain : domains) { - if (!getApprovedPackages(domain, userId, APPROVAL_LEVEL_LEGACY_ALWAYS + 1, - mConnection::getPackageSettingLocked).first.isEmpty()) { + if (userState.getEnabledHosts().contains(domain)) { + continue; + } + + Pair<List<String>, Integer> packagesToLevel = getApprovedPackages(domain, + userId, APPROVAL_LEVEL_NONE + 1, mConnection::getPackageSettingLocked); + int highestApproval = packagesToLevel.second; + if (highestApproval > APPROVAL_LEVEL_SELECTION) { throw new InvalidDomainSetException(domainSetId, null, InvalidDomainSetException.REASON_UNABLE_TO_APPROVE); } + + domainToApprovedPackages.put(domain, packagesToLevel.first); + } + + // The removal for other packages must be done in a 2nd pass after it's determined + // that no higher priority owners exist for all of the domains in the set. + int mapSize = domainToApprovedPackages.size(); + for (int mapIndex = 0; mapIndex < mapSize; mapIndex++) { + String domain = domainToApprovedPackages.keyAt(mapIndex); + List<String> approvedPackages = domainToApprovedPackages.valueAt(mapIndex); + int approvedSize = approvedPackages.size(); + for (int approvedIndex = 0; approvedIndex < approvedSize; approvedIndex++) { + String approvedPackage = approvedPackages.get(approvedIndex); + DomainVerificationPkgState approvedPkgState = + mAttachedPkgStates.get(approvedPackage); + if (approvedPkgState == null) { + continue; + } + + DomainVerificationUserState approvedUserState = + approvedPkgState.getUserSelectionState(userId); + if (approvedUserState == null) { + continue; + } + + approvedUserState.removeHost(domain); + } } } - DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains, - false /* forAutoVerify */, callingUid, userId); - DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId); if (enabled) { userState.addHosts(domains); } else { @@ -500,28 +562,33 @@ public class DomainVerificationService extends SystemService @Override public void setDomainVerificationUserSelectionInternal(@UserIdInt int userId, - @Nullable String packageName, boolean enabled, @NonNull ArraySet<String> domains) + @Nullable String packageName, boolean enabled, @Nullable ArraySet<String> domains) throws NameNotFoundException { mEnforcer.assertInternal(mConnection.getCallingUid()); + if (packageName == null) { synchronized (mLock) { Set<String> validDomains = new ArraySet<>(); - int size = mAttachedPkgStates.size(); for (int index = 0; index < size; index++) { DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); String pkgName = pkgState.getPackageName(); PackageSetting pkgSetting = mConnection.getPackageSettingLocked(pkgName); - if (pkgSetting == null || pkgSetting.getPkg() == null) { + AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg(); + if (pkg == null) { continue; } - validDomains.clear(); - validDomains.addAll(domains); + if (domains == null) { + validDomains = mCollector.collectAllWebDomains(pkg); + } else { + validDomains.clear(); + validDomains.addAll(domains); + } setDomainVerificationUserSelectionInternal(userId, pkgState, - pkgSetting.getPkg(), enabled, validDomains); + pkg, enabled, validDomains); } } } else { @@ -532,12 +599,16 @@ public class DomainVerificationService extends SystemService } PackageSetting pkgSetting = mConnection.getPackageSettingLocked(packageName); - if (pkgSetting == null || pkgSetting.getPkg() == null) { + AndroidPackage pkg = pkgSetting == null ? null : pkgSetting.getPkg(); + if (pkg == null) { throw DomainVerificationUtils.throwPackageUnavailable(packageName); } + Set<String> validDomains = + domains == null ? mCollector.collectAllWebDomains(pkg) : domains; + setDomainVerificationUserSelectionInternal(userId, pkgState, pkgSetting.getPkg(), - enabled, domains); + enabled, validDomains); } } @@ -549,12 +620,10 @@ public class DomainVerificationService extends SystemService boolean enabled, Set<String> domains) { domains.retainAll(mCollector.collectAllWebDomains(pkg)); - SparseArray<DomainVerificationUserState> userStates = - pkgState.getUserSelectionStates(); if (userId == UserHandle.USER_ALL) { - int size = userStates.size(); - for (int index = 0; index < size; index++) { - DomainVerificationUserState userState = userStates.valueAt(index); + for (int aUserId : mConnection.getAllUserIds()) { + DomainVerificationUserState userState = + pkgState.getOrCreateUserSelectionState(aUserId); if (enabled) { userState.addHosts(domains); } else { @@ -598,28 +667,109 @@ public class DomainVerificationService extends SystemService throw DomainVerificationUtils.throwPackageUnavailable(packageName); } - ArrayMap<String, Boolean> hostToUserSelectionMap = new ArrayMap<>(); - - ArraySet<String> domains = mCollector.collectAllWebDomains(pkg); - int domainsSize = domains.size(); - for (int index = 0; index < domainsSize; index++) { - hostToUserSelectionMap.put(domains.valueAt(index), false); - } + ArraySet<String> webDomains = mCollector.collectAllWebDomains(pkg); + int webDomainsSize = webDomains.size(); + Map<String, Integer> domains = new ArrayMap<>(webDomainsSize); + ArrayMap<String, Integer> stateMap = pkgState.getStateMap(); DomainVerificationUserState userState = pkgState.getUserSelectionState(userId); - boolean linkHandlingAllowed = true; - if (userState != null) { - linkHandlingAllowed = userState.isLinkHandlingAllowed(); - ArraySet<String> enabledHosts = userState.getEnabledHosts(); - int hostsSize = enabledHosts.size(); - for (int index = 0; index < hostsSize; index++) { - hostToUserSelectionMap.put(enabledHosts.valueAt(index), true); + Set<String> enabledHosts = userState == null ? emptySet() : userState.getEnabledHosts(); + + for (int index = 0; index < webDomainsSize; index++) { + String host = webDomains.valueAt(index); + Integer state = stateMap.get(host); + + int domainState; + if (state != null && DomainVerificationManager.isStateVerified(state)) { + domainState = DomainVerificationUserSelection.DOMAIN_STATE_VERIFIED; + } else if (enabledHosts.contains(host)) { + domainState = DomainVerificationUserSelection.DOMAIN_STATE_SELECTED; + } else { + domainState = DomainVerificationUserSelection.DOMAIN_STATE_NONE; } + + domains.put(host, domainState); } + boolean linkHandlingAllowed = userState == null || userState.isLinkHandlingAllowed(); + return new DomainVerificationUserSelection(pkgState.getId(), packageName, - UserHandle.of(userId), linkHandlingAllowed, hostToUserSelectionMap); + UserHandle.of(userId), linkHandlingAllowed, domains); + } + } + + @NonNull + @Override + public List<DomainOwner> getOwnersForDomain(@NonNull String domain) { + return getOwnersForDomain(domain, mConnection.getCallingUserId()); + } + + public List<DomainOwner> getOwnersForDomain(@NonNull String domain, @UserIdInt int userId) { + mEnforcer.assertOwnerQuerent(mConnection.getCallingUid(), mConnection.getCallingUserId(), + userId); + + SparseArray<List<String>> levelToPackages = new SparseArray<>(); + + // First, collect the raw approval level values + synchronized (mLock) { + final int size = mAttachedPkgStates.size(); + for (int index = 0; index < size; index++) { + DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index); + String packageName = pkgState.getPackageName(); + PackageSetting pkgSetting = mConnection.getPackageSettingLocked(packageName); + if (pkgSetting == null) { + continue; + } + + int level = approvalLevelForDomain(pkgSetting, domain, userId, domain); + if (level <= APPROVAL_LEVEL_NONE) { + continue; + } + List<String> list = levelToPackages.get(level); + if (list == null) { + list = new ArrayList<>(); + levelToPackages.put(level, list); + } + list.add(packageName); + } } + + final int size = levelToPackages.size(); + if (size == 0) { + return emptyList(); + } + + // Then sort them ascending by first installed time, with package name as the tie breaker + for (int index = 0; index < size; index++) { + levelToPackages.valueAt(index).sort((first, second) -> { + PackageSetting firstPkgSetting = mConnection.getPackageSettingLocked(first); + PackageSetting secondPkgSetting = mConnection.getPackageSettingLocked(second); + + long firstInstallTime = + firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime(); + long secondInstallTime = + secondPkgSetting == null ? -1L : secondPkgSetting.getFirstInstallTime(); + + if (firstInstallTime != secondInstallTime) { + return (int) (firstInstallTime - secondInstallTime); + } + + return first.compareToIgnoreCase(second); + }); + } + + List<DomainOwner> owners = new ArrayList<>(); + for (int index = 0; index < size; index++) { + int level = levelToPackages.keyAt(index); + boolean overrideable = level <= APPROVAL_LEVEL_SELECTION; + List<String> packages = levelToPackages.valueAt(index); + int packagesSize = packages.size(); + for (int packageIndex = 0; packageIndex < packagesSize; packageIndex++) { + owners.add(new DomainOwner(packages.get(packageIndex), overrideable)); + } + } + + return owners; } @NonNull @@ -632,7 +782,7 @@ public class DomainVerificationService extends SystemService @Override public void migrateState(@NonNull PackageSetting oldPkgSetting, @NonNull PackageSetting newPkgSetting) { - String pkgName = newPkgSetting.name; + String pkgName = newPkgSetting.getName(); boolean sendBroadcast; synchronized (mLock) { @@ -728,7 +878,7 @@ public class DomainVerificationService extends SystemService // gains or loses all domains. UUID domainSetId = newPkgSetting.getDomainSetId(); - String pkgName = newPkgSetting.name; + String pkgName = newPkgSetting.getName(); boolean sendBroadcast = true; @@ -1344,9 +1494,9 @@ public class DomainVerificationService extends SystemService @Override public int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent, - @UserIdInt int userId) { - String packageName = pkgSetting.name; - if (!DomainVerificationUtils.isDomainVerificationIntent(intent)) { + @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) { + String packageName = pkgSetting.getName(); + if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) { if (DEBUG_APPROVAL) { debugApproval(packageName, intent, userId, false, "not valid intent"); } @@ -1362,7 +1512,7 @@ public class DomainVerificationService extends SystemService */ private int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull String host, @UserIdInt int userId, @NonNull Object debugObject) { - String packageName = pkgSetting.name; + String packageName = pkgSetting.getName(); final AndroidPackage pkg = pkgSetting.getPkg(); // Should never be null, but if it is, skip this and assume that v2 is enabled diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java index 7f9e75aa2926..d083d11cb2e2 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationShell.java @@ -250,6 +250,10 @@ public class DomainVerificationShell { return false; } + if (domains.size() == 1 && domains.contains("all")) { + domains = null; + } + try { mCallback.setDomainVerificationUserSelectionInternal(userId, packageName, enabled, domains); @@ -446,10 +450,10 @@ public class DomainVerificationShell { * @param packageName the package whose state to change, or all packages if non is * specified * @param enabled whether the domain is now approved by the user - * @param domains the set of domains to change + * @param domains the set of domains to change, or null to affect all domains */ void setDomainVerificationUserSelectionInternal(@UserIdInt int userId, - @Nullable String packageName, boolean enabled, @NonNull ArraySet<String> domains) + @Nullable String packageName, boolean enabled, @Nullable ArraySet<String> domains) throws PackageManager.NameNotFoundException; /** diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java index 474f822d6a73..783aff6ccb55 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationUtils.java @@ -28,7 +28,7 @@ import com.android.server.compat.PlatformCompat; import com.android.server.pm.PackageManagerService; import com.android.server.pm.parsing.pkg.AndroidPackage; -final class DomainVerificationUtils { +public final class DomainVerificationUtils { /** * Consolidates package exception messages. A generic unavailable message is included since the @@ -40,10 +40,13 @@ final class DomainVerificationUtils { throw new NameNotFoundException("Package " + packageName + " unavailable"); } - static boolean isDomainVerificationIntent(Intent intent) { - return intent.isWebIntent() - && intent.hasCategory(Intent.CATEGORY_BROWSABLE) - && intent.hasCategory(Intent.CATEGORY_DEFAULT); + public static boolean isDomainVerificationIntent(Intent intent, int resolveInfoFlags) { + if (!intent.isWebIntent() || !intent.hasCategory(Intent.CATEGORY_BROWSABLE)) { + return false; + } + + return ((resolveInfoFlags & PackageManager.MATCH_DEFAULT_ONLY) != 0) + || intent.hasCategory(Intent.CATEGORY_DEFAULT); } static boolean isChangeEnabled(PlatformCompat platformCompat, AndroidPackage pkg, diff --git a/services/core/java/com/android/server/pm/verify/domain/OWNERS b/services/core/java/com/android/server/pm/verify/domain/OWNERS new file mode 100644 index 000000000000..c669112e0512 --- /dev/null +++ b/services/core/java/com/android/server/pm/verify/domain/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 36137 + +chiuwinson@google.com +patb@google.com +toddke@google.com
\ No newline at end of file diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java index 22468640800e..8fbb33afb6ca 100644 --- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java +++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java @@ -58,6 +58,11 @@ public class DomainVerificationUserState { return this; } + public DomainVerificationUserState removeHost(String host) { + mEnabledHosts.remove(host); + return this; + } + public DomainVerificationUserState removeHosts(@NonNull ArraySet<String> newHosts) { mEnabledHosts.removeAll(newHosts); return this; diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java new file mode 100644 index 000000000000..c9653909adb6 --- /dev/null +++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java @@ -0,0 +1,151 @@ +/* + * 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.policy; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.AppOpsManager; +import android.app.AppOpsManagerInternal; +import android.location.LocationManagerInternal; +import android.util.ArrayMap; +import android.util.ArraySet; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.function.HeptFunction; +import com.android.internal.util.function.QuadFunction; +import com.android.server.LocalServices; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * This class defines policy for special behaviors around app ops. + */ +public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegate { + @NonNull + private final Object mLock = new Object(); + + /** + * The locking policy around the location tags is a bit special. Since we want to + * avoid grabbing the lock on every op note we are taking the approach where the + * read and write are being done via a thread-safe data structure such that the + * lookup/insert are single thread-safe calls. When we update the cached state we + * use a lock to ensure the update's lookup and store calls are done atomically, + * so multiple writers would not interleave. The tradeoff is we make is that the + * concurrent data structure would use boxing/unboxing of integers but this is + * preferred to locking. + */ + @GuardedBy("mLock - writes only - see above") + @NonNull + private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags = + new ConcurrentHashMap(); + + public AppOpsPolicy() { + final LocationManagerInternal locationManagerInternal = LocalServices.getService( + LocationManagerInternal.class); + locationManagerInternal.setOnProviderLocationTagsChangeListener((providerTagInfo) -> { + synchronized (mLock) { + final int uid = providerTagInfo.getUid(); + // We make a copy of the per UID state to limit our mutation to one + // operation in the underlying concurrent data structure. + ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid); + if (uidTags != null) { + uidTags = new ArrayMap<>(uidTags); + } + + final String packageName = providerTagInfo.getPackageName(); + ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null; + if (packageTags != null) { + packageTags = new ArraySet<>(packageTags); + } + + final Set<String> providerTags = providerTagInfo.getTags(); + if (providerTags != null && !providerTags.isEmpty()) { + if (packageTags != null) { + packageTags.clear(); + packageTags.addAll(providerTags); + } else { + packageTags = new ArraySet<>(providerTags); + } + if (uidTags == null) { + uidTags = new ArrayMap<>(); + } + uidTags.put(packageName, packageTags); + mLocationTags.put(uid, uidTags); + } else if (uidTags != null) { + uidTags.remove(packageName); + if (!uidTags.isEmpty()) { + mLocationTags.put(uid, uidTags); + } else { + mLocationTags.remove(uid); + } + } + } + }); + } + + @Override + public int checkOperation(int code, int uid, String packageName, boolean raw, + QuadFunction<Integer, Integer, String, Boolean, Integer> superImpl) { + return superImpl.apply(code, uid, packageName, raw); + } + + @Override + public int checkAudioOperation(int code, int usage, int uid, String packageName, + QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) { + return superImpl.apply(code, usage, uid, packageName); + } + + @Override + public int noteOperation(int code, int uid, @Nullable String packageName, + @Nullable String featureId, boolean shouldCollectAsyncNotedOp, @Nullable String message, + boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer, String, String, + Boolean, String, Boolean, Integer> superImpl) { + if (isHandledOp(code)) { + // Only a single lookup from the underlying concurrent data structure + final ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid); + if (uidTags != null) { + final ArraySet<String> packageTags = uidTags.get(packageName); + if (packageTags != null && packageTags.contains(featureId)) { + return superImpl.apply(resolveLocationOp(code), uid, packageName, featureId, + shouldCollectAsyncNotedOp, message, shouldCollectMessage); + } + } + } + return superImpl.apply(code, uid, packageName, featureId, shouldCollectAsyncNotedOp, + message, shouldCollectMessage); + } + + private static boolean isHandledOp(int code) { + switch (code) { + case AppOpsManager.OP_FINE_LOCATION: + case AppOpsManager.OP_COARSE_LOCATION: + return true; + } + return false; + } + + private static int resolveLocationOp(int code) { + switch (code) { + case AppOpsManager.OP_FINE_LOCATION: + return AppOpsManager.OP_FINE_LOCATION_SOURCE; + case AppOpsManager.OP_COARSE_LOCATION: + return AppOpsManager.OP_COARSE_LOCATION_SOURCE; + } + return code; + } +} diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java index 82fc22c51afc..0e12584f80a1 100644 --- a/services/core/java/com/android/server/policy/DisplayFoldController.java +++ b/services/core/java/com/android/server/policy/DisplayFoldController.java @@ -74,7 +74,7 @@ class DisplayFoldController { mHandler = handler; DeviceStateManager deviceStateManager = context.getSystemService(DeviceStateManager.class); - deviceStateManager.addDeviceStateListener(new HandlerExecutor(handler), + deviceStateManager.registerCallback(new HandlerExecutor(handler), new DeviceStateListener(context)); } @@ -208,7 +208,7 @@ class DisplayFoldController { * matches the value in the {@link com.android.internal.R.integer.config_foldedDeviceState} * resource. */ - private class DeviceStateListener implements DeviceStateManager.DeviceStateListener { + private class DeviceStateListener implements DeviceStateManager.DeviceStateCallback { private final int[] mFoldedDeviceStates; DeviceStateListener(Context context) { @@ -217,7 +217,7 @@ class DisplayFoldController { } @Override - public void onDeviceStateChanged(int deviceState) { + public void onStateChanged(int deviceState) { boolean folded = false; for (int i = 0; i < mFoldedDeviceStates.length; i++) { if (deviceState == mFoldedDeviceStates[i]) { diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java index 84ac12497e71..7f55723cda0b 100644 --- a/services/core/java/com/android/server/policy/KeyCombinationManager.java +++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java @@ -102,9 +102,11 @@ public class KeyCombinationManager { } /** - * Check if the key event could be triggered by combine key rule before dispatching to a window. + * Check if the key event could be intercepted by combination key rule before it is dispatched + * to a window. + * Return true if any active rule could be triggered by the key event, otherwise false. */ - void interceptKey(KeyEvent event, boolean interactive) { + boolean interceptKey(KeyEvent event, boolean interactive) { final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; final int keyCode = event.getKeyCode(); final int count = mActiveRules.size(); @@ -117,9 +119,9 @@ public class KeyCombinationManager { // exceed time from first key down. forAllRules(mActiveRules, (rule)-> rule.cancel()); mActiveRules.clear(); - return; + return false; } else if (count == 0) { // has some key down but no active rule exist. - return; + return false; } } @@ -127,7 +129,7 @@ public class KeyCombinationManager { mDownTimes.put(keyCode, eventTime); } else { // ignore old key, maybe a repeat key. - return; + return false; } if (mDownTimes.size() == 1) { @@ -141,7 +143,7 @@ public class KeyCombinationManager { } else { // Ignore if rule already triggered. if (mTriggeredRule != null) { - return; + return true; } // check if second key can trigger rule, or remove the non-match rule. @@ -156,6 +158,7 @@ public class KeyCombinationManager { mActiveRules.clear(); if (mTriggeredRule != null) { mActiveRules.add(mTriggeredRule); + return true; } } } else { @@ -168,6 +171,7 @@ public class KeyCombinationManager { } } } + return false; } /** diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 6a441f1830d1..1b192e43c7b8 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -56,13 +56,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; -import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG; -import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; @@ -75,6 +73,8 @@ import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY; +import static com.android.server.policy.SingleKeyGestureDetector.KEY_LONGPRESS; +import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; @@ -458,7 +458,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { volatile boolean mPowerKeyHandled; volatile boolean mBackKeyHandled; volatile boolean mBeganFromNonInteractive; - volatile int mPowerKeyPressCounter; volatile boolean mEndCallKeyHandled; volatile boolean mCameraGestureTriggeredDuringGoingToSleep; volatile boolean mGoingToSleep; @@ -499,7 +498,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mHasSoftInput = false; boolean mHapticTextHandleEnabled; boolean mUseTvRouting; - int mVeryLongPressTimeout; boolean mAllowStartActivityForLongPressOnPowerDuringSetup; MetricsLogger mLogger; boolean mWakeOnDpadKeyPress; @@ -522,8 +520,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mConsumeSearchKeyUp; boolean mPendingMetaAction; boolean mPendingCapsLockToggle; - int mMetaState; - int mInitialMetaState; // support for activating the lock screen while the screen is on private HashSet<Integer> mAllowLockscreenWhenOnDisplays = new HashSet<>(); @@ -599,14 +595,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { private final com.android.internal.policy.LogDecelerateInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100, 0); - private final MutableBoolean mTmpBoolean = new MutableBoolean(false); - private boolean mPerDisplayFocusEnabled = false; private volatile int mTopFocusedDisplayId = INVALID_DISPLAY; private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS; private KeyCombinationManager mKeyCombinationManager; + private SingleKeyGestureDetector mSingleKeyGestureDetector; private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3; private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4; @@ -617,10 +612,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_DISPATCH_SHOW_GLOBAL_ACTIONS = 10; private static final int MSG_HIDE_BOOT_MESSAGE = 11; private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12; - private static final int MSG_POWER_DELAYED_PRESS = 13; - private static final int MSG_POWER_LONG_PRESS = 14; private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 15; - private static final int MSG_BACK_LONG_PRESS = 16; private static final int MSG_ACCESSIBILITY_SHORTCUT = 17; private static final int MSG_BUGREPORT_TV = 18; private static final int MSG_ACCESSIBILITY_TV = 19; @@ -628,8 +620,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_SYSTEM_KEY_PRESS = 21; private static final int MSG_HANDLE_ALL_APPS = 22; private static final int MSG_LAUNCH_ASSIST = 23; - private static final int MSG_POWER_VERY_LONG_PRESS = 25; - private static final int MSG_RINGER_TOGGLE_CHORD = 26; + private static final int MSG_RINGER_TOGGLE_CHORD = 24; private class PolicyHandler extends Handler { @Override @@ -670,22 +661,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK: launchVoiceAssistWithWakeLock(); break; - case MSG_POWER_DELAYED_PRESS: - powerPress((Long) msg.obj, msg.arg1 != 0, msg.arg2); - finishPowerKeyPress(); - break; - case MSG_POWER_LONG_PRESS: - powerLongPress((Long) msg.obj /* eventTime */); - break; - case MSG_POWER_VERY_LONG_PRESS: - powerVeryLongPress(); - break; case MSG_SHOW_PICTURE_IN_PICTURE_MENU: showPictureInPictureMenuInternal(); break; - case MSG_BACK_LONG_PRESS: - backLongPress(); - break; case MSG_ACCESSIBILITY_SHORTCUT: accessibilityShortcutActivated(); break; @@ -796,13 +774,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; - private Runnable mPossibleVeryLongPressReboot = new Runnable() { - @Override - public void run() { - mActivityManagerInternal.prepareForPossibleShutdown(); - } - }; - private void handleRingerChordGesture() { if (mRingerToggleChord == VOLUME_HUSH_OFF) { return; @@ -842,28 +813,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private void interceptBackKeyDown() { - mLogger.count("key_back_down", 1); - // Reset back key state for long press - mBackKeyHandled = false; - - if (hasLongPressOnBackBehavior()) { - Message msg = mHandler.obtainMessage(MSG_BACK_LONG_PRESS); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, - ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); - } - } // returns true if the key was handled and should not be passed to the user - private boolean interceptBackKeyUp(KeyEvent event) { - mLogger.count("key_back_up", 1); + private boolean backKeyPress() { + mLogger.count("key_back_press", 1); // Cache handled state boolean handled = mBackKeyHandled; - // Reset back long press state - cancelPendingBackKeyAction(); - if (mHasFeatureWatch) { TelecomManager telecomManager = getTelecommService(); @@ -885,10 +841,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - if (mAutofillManagerInternal != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { + if (mAutofillManagerInternal != null) { mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_BACK_KEY_TO_AUTOFILL)); } - return handled; } @@ -898,11 +853,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mPowerKeyWakeLock.acquire(); } - // Cancel multi-press detection timeout. - if (mPowerKeyPressCounter != 0) { - mHandler.removeMessages(MSG_POWER_DELAYED_PRESS); - } - mWindowManagerFuncs.onPowerKeyDown(interactive); // Stop ringing or end call if configured to do so when power is pressed. @@ -924,71 +874,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event); - GestureLauncherService gestureService = LocalServices.getService( - GestureLauncherService.class); - boolean gesturedServiceIntercepted = false; - if (gestureService != null) { - gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive, - mTmpBoolean); - if (mTmpBoolean.value && mRequestedOrGoingToSleep) { - mCameraGestureTriggeredDuringGoingToSleep = true; - } - } - // Inform the StatusBar; but do not allow it to consume the event. sendSystemKeyToStatusBarAsync(event.getKeyCode()); - schedulePossibleVeryLongPressReboot(); - // If the power key has still not yet been handled, then detect short // press, long press, or multi press and decide what to do. - mPowerKeyHandled = hungUp || gesturedServiceIntercepted + mPowerKeyHandled = mPowerKeyHandled || hungUp || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted(); if (!mPowerKeyHandled) { - if (interactive) { - // When interactive, we're already awake. - // Wait for a long press or for the button to be released to decide what to do. - if (hasLongPressOnPowerBehavior()) { - if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { - powerLongPress(event.getEventTime()); - } else { - Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS, - event.getEventTime()); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, - ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); - - if (hasVeryLongPressOnPowerBehavior()) { - Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS); - longMsg.setAsynchronous(true); - mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout); - } - } - } - } else { + if (!interactive) { wakeUpFromPowerKey(event.getDownTime()); - if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) { - if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { - powerLongPress(event.getEventTime()); - } else { - Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS, - event.getEventTime()); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, - ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); - - if (hasVeryLongPressOnPowerBehavior()) { - Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS); - longMsg.setAsynchronous(true); - mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout); - } - } - mBeganFromNonInteractive = true; } else { final int maxCount = getMaxMultiPressPowerCount(); - if (maxCount <= 1) { mPowerKeyHandled = true; } else { @@ -996,68 +895,38 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } } + } else { + // handled by another power key policy. + if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) { + mSingleKeyGestureDetector.reset(); + } } } private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) { final boolean handled = canceled || mPowerKeyHandled; - cancelPendingPowerKeyAction(); if (!handled) { if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) == 0) { // Abort possibly stuck animations only when power key up without long press case. mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe); } - - // Figure out how to handle the key now that it has been released. - mPowerKeyPressCounter += 1; - - final int maxCount = getMaxMultiPressPowerCount(); - final long eventTime = event.getDownTime(); - if (mPowerKeyPressCounter < maxCount) { - // This could be a multi-press. Wait a little bit longer to confirm. - // Continue holding the wake lock. - Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS, - interactive ? 1 : 0, mPowerKeyPressCounter, eventTime); - msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, ViewConfiguration.getMultiPressTimeout()); - return; - } - - // No other actions. Handle it immediately. - powerPress(eventTime, interactive, mPowerKeyPressCounter); + } else { + // handled by single key or another power key policy. + mSingleKeyGestureDetector.reset(); + finishPowerKeyPress(); } - // Done. Reset our state. - finishPowerKeyPress(); } private void finishPowerKeyPress() { mBeganFromNonInteractive = false; - mPowerKeyPressCounter = 0; + mPowerKeyHandled = false; if (mPowerKeyWakeLock.isHeld()) { mPowerKeyWakeLock.release(); } } - private void cancelPendingPowerKeyAction() { - if (!mPowerKeyHandled) { - mPowerKeyHandled = true; - mHandler.removeMessages(MSG_POWER_LONG_PRESS); - } - if (hasVeryLongPressOnPowerBehavior()) { - mHandler.removeMessages(MSG_POWER_VERY_LONG_PRESS); - } - cancelPossibleVeryLongPressReboot(); - } - - private void cancelPendingBackKeyAction() { - if (!mBackKeyHandled) { - mBackKeyHandled = true; - mHandler.removeMessages(MSG_BACK_LONG_PRESS); - } - } - private void powerPress(long eventTime, boolean interactive, int count) { if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) { Slog.i(TAG, "Suppressed redundant power key press while " @@ -1208,6 +1077,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void powerLongPress(long eventTime) { final int behavior = getResolvedLongPressOnPowerBehavior(); + switch (behavior) { case LONG_PRESS_POWER_NOTHING: break; @@ -1846,8 +1716,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.integer.config_triplePressOnPowerBehavior); mShortPressOnSleepBehavior = mContext.getResources().getInteger( com.android.internal.R.integer.config_shortPressOnSleepBehavior); - mVeryLongPressTimeout = mContext.getResources().getInteger( - com.android.internal.R.integer.config_veryLongPressTimeout); mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup); @@ -1941,6 +1809,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }); initKeyCombinationRules(); + initSingleKeyGestureRules(); } private void initKeyCombinationRules() { @@ -1953,7 +1822,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) { @Override void execute() { - cancelPendingPowerKeyAction(); + mPowerKeyHandled = true; interceptScreenshotChord(); } @Override @@ -1988,7 +1857,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override void execute() { - cancelPendingPowerKeyAction(); + mPowerKeyHandled = true; interceptRingerToggleChord(); } @Override @@ -2002,7 +1871,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) { @Override void execute() { - cancelPendingBackKeyAction(); + mBackKeyHandled = true; interceptAccessibilityGestureTv(); } @@ -2016,7 +1885,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) { @Override void execute() { - cancelPendingBackKeyAction(); + mBackKeyHandled = true; interceptBugreportGestureTv(); } @@ -2029,6 +1898,84 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** + * Rule for single power key gesture. + */ + private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule { + PowerKeyRule(int gestures) { + super(KEYCODE_POWER, gestures); + } + + @Override + int getMaxMultiPressCount() { + return getMaxMultiPressPowerCount(); + } + + @Override + void onPress(long downTime) { + powerPress(downTime, true, 1 /*count*/); + finishPowerKeyPress(); + } + + @Override + void onLongPress(long downTime) { + powerLongPress(downTime); + } + + @Override + void onVeryLongPress(long downTime) { + mActivityManagerInternal.prepareForPossibleShutdown(); + powerVeryLongPress(); + } + + @Override + void onMultiPress(long downTime, int count) { + powerPress(downTime, true, count); + finishPowerKeyPress(); + } + } + + /** + * Rule for single back key gesture. + */ + private final class BackKeyRule extends SingleKeyGestureDetector.SingleKeyRule { + BackKeyRule(int gestures) { + super(KEYCODE_BACK, gestures); + } + + @Override + int getMaxMultiPressCount() { + return 1; + } + + @Override + void onPress(long downTime) { + mBackKeyHandled |= backKeyPress(); + } + + @Override + void onLongPress(long downTime) { + backLongPress(); + } + } + + private void initSingleKeyGestureRules() { + mSingleKeyGestureDetector = new SingleKeyGestureDetector(mContext); + + int powerKeyGestures = 0; + if (hasVeryLongPressOnPowerBehavior()) { + powerKeyGestures |= KEY_VERYLONGPRESS; + } + if (hasLongPressOnPowerBehavior()) { + powerKeyGestures |= KEY_LONGPRESS; + } + mSingleKeyGestureDetector.addRule(new PowerKeyRule(powerKeyGestures)); + + if (hasLongPressOnBackBehavior()) { + mSingleKeyGestureDetector.addRule(new BackKeyRule(KEY_LONGPRESS)); + } + } + + /** * Read values from config.xml that may be overridden depending on * the configuration of the device. * eg. Disable long press on home goes to recents on sw600dp. @@ -2292,25 +2239,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { return attrs.type == TYPE_NOTIFICATION_SHADE; } - @Override - public boolean canBeHiddenByKeyguardLw(WindowState win) { - - // Keyguard visibility of window from activities are determined over activity visibility. - if (win.getAppToken() != null) { - return false; - } - switch (win.getAttrs().type) { - case TYPE_NOTIFICATION_SHADE: - case TYPE_STATUS_BAR: - case TYPE_NAVIGATION_BAR: - case TYPE_WALLPAPER: - return false; - default: - // Hide only windows below the keyguard host window. - return getWindowLayerLw(win) < getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE); - } - } - /** {@inheritDoc} */ @Override public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme, @@ -2568,6 +2496,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; final boolean canceled = event.isCanceled(); final int displayId = event.getDisplayId(); + final long key_consumed = -1; if (DEBUG_INPUT) { Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount=" @@ -2575,7 +2504,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (mKeyCombinationManager.isKeyConsumed(event)) { - return -1; + return key_consumed; } if ((flags & KeyEvent.FLAG_FALLBACK) == 0) { @@ -2596,205 +2525,250 @@ public class PhoneWindowManager implements WindowManagerPolicy { mPendingCapsLockToggle = false; } - // First we always handle the home key here, so applications - // can never break it, although if keyguard is on, we do let - // it handle it, because that gives us the correct 5 second - // timeout. - if (keyCode == KEYCODE_HOME) { - DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId); - if (handler == null) { - handler = new DisplayHomeButtonHandler(displayId); - mDisplayHomeButtonHandlers.put(displayId, handler); - } - return handler.handleHomeButton(focusedToken, event); - } else if (keyCode == KeyEvent.KEYCODE_MENU) { - // Hijack modified menu keys for debugging features - final int chordBug = KeyEvent.META_SHIFT_ON; - - if (down && repeatCount == 0) { - if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) { - Intent intent = new Intent(Intent.ACTION_BUG_REPORT); - mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, - null, null, null, 0, null, null); - return -1; + switch(keyCode) { + case KeyEvent.KEYCODE_HOME: + // First we always handle the home key here, so applications + // can never break it, although if keyguard is on, we do let + // it handle it, because that gives us the correct 5 second + // timeout. + DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId); + if (handler == null) { + handler = new DisplayHomeButtonHandler(displayId); + mDisplayHomeButtonHandlers.put(displayId, handler); } - } - } else if (keyCode == KeyEvent.KEYCODE_SEARCH) { - if (down) { - if (repeatCount == 0) { - mSearchKeyShortcutPending = true; - mConsumeSearchKeyUp = false; + return handler.handleHomeButton(focusedToken, event); + case KeyEvent.KEYCODE_MENU: + // Hijack modified menu keys for debugging features + final int chordBug = KeyEvent.META_SHIFT_ON; + + if (down && repeatCount == 0) { + if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) { + Intent intent = new Intent(Intent.ACTION_BUG_REPORT); + mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, + null, null, null, 0, null, null); + return key_consumed; + } } - } else { - mSearchKeyShortcutPending = false; - if (mConsumeSearchKeyUp) { - mConsumeSearchKeyUp = false; - return -1; + break; + case KeyEvent.KEYCODE_SEARCH: + if (down) { + if (repeatCount == 0) { + mSearchKeyShortcutPending = true; + mConsumeSearchKeyUp = false; + } + } else { + mSearchKeyShortcutPending = false; + if (mConsumeSearchKeyUp) { + mConsumeSearchKeyUp = false; + return key_consumed; + } } - } - return 0; - } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) { - if (!keyguardOn) { - if (down && repeatCount == 0) { - preloadRecentApps(); - } else if (!down) { - toggleRecentApps(); + return 0; + case KeyEvent.KEYCODE_APP_SWITCH: + if (!keyguardOn) { + if (down && repeatCount == 0) { + preloadRecentApps(); + } else if (!down) { + toggleRecentApps(); + } } - } - return -1; - } else if (keyCode == KeyEvent.KEYCODE_N && event.isMetaPressed()) { - if (down) { - IStatusBarService service = getStatusBarService(); - if (service != null) { - try { - service.expandNotificationsPanel(); - } catch (RemoteException e) { - // do nothing. + return key_consumed; + case KeyEvent.KEYCODE_N: + if (down && event.isMetaPressed()) { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.expandNotificationsPanel(); + } catch (RemoteException e) { + // do nothing. + } + return key_consumed; } } - } - } else if (keyCode == KeyEvent.KEYCODE_S && event.isMetaPressed() - && event.isCtrlPressed()) { - if (down && repeatCount == 0) { - int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION - : TAKE_SCREENSHOT_FULLSCREEN; - mScreenshotRunnable.setScreenshotType(type); - mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER); - mHandler.post(mScreenshotRunnable); - return -1; - } - } else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) { - if (down && repeatCount == 0 && !isKeyguardLocked()) { - toggleKeyboardShortcutsMenu(event.getDeviceId()); - } - } else if (keyCode == KeyEvent.KEYCODE_ASSIST) { - Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing"); - return -1; - } else if (keyCode == KeyEvent.KEYCODE_VOICE_ASSIST) { - Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in interceptKeyBeforeQueueing"); - return -1; - } else if (keyCode == KeyEvent.KEYCODE_SYSRQ) { - if (down && repeatCount == 0) { - mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); - mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER); - mHandler.post(mScreenshotRunnable); - } - return -1; - } else if (keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP - || keyCode == KeyEvent.KEYCODE_BRIGHTNESS_DOWN) { - if (down) { - int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1; - - // Disable autobrightness if it's on - int auto = Settings.System.getIntForUser( - mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_MODE, - Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, - UserHandle.USER_CURRENT_OR_SELF); - if (auto != 0) { - Settings.System.putIntForUser(mContext.getContentResolver(), + break; + case KeyEvent.KEYCODE_S: + if (down && event.isMetaPressed() && event.isCtrlPressed() && repeatCount == 0) { + int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION + : TAKE_SCREENSHOT_FULLSCREEN; + mScreenshotRunnable.setScreenshotType(type); + mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER); + mHandler.post(mScreenshotRunnable); + return key_consumed; + } + break; + case KeyEvent.KEYCODE_SLASH: + if (down && repeatCount == 0 && event.isMetaPressed() && !keyguardOn) { + toggleKeyboardShortcutsMenu(event.getDeviceId()); + return key_consumed; + } + break; + case KeyEvent.KEYCODE_ASSIST: + Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing"); + return key_consumed; + case KeyEvent.KEYCODE_VOICE_ASSIST: + Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in" + + " interceptKeyBeforeQueueing"); + return key_consumed; + case KeyEvent.KEYCODE_SYSRQ: + if (down && repeatCount == 0) { + mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); + mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER); + mHandler.post(mScreenshotRunnable); + } + return key_consumed; + case KeyEvent.KEYCODE_BRIGHTNESS_UP: + case KeyEvent.KEYCODE_BRIGHTNESS_DOWN: + if (down) { + int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1; + + // Disable autobrightness if it's on + int auto = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT_OR_SELF); + if (auto != 0) { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, + UserHandle.USER_CURRENT_OR_SELF); + } + float minFloat = mPowerManager.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); + float maxFloat = mPowerManager.getBrightnessConstraint( + PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); + float stepFloat = (maxFloat - minFloat) / BRIGHTNESS_STEPS * direction; + float brightnessFloat = Settings.System.getFloatForUser( + mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FLOAT, + mContext.getDisplay().getBrightnessDefault(), + UserHandle.USER_CURRENT_OR_SELF); + brightnessFloat += stepFloat; + // Make sure we don't go beyond the limits. + brightnessFloat = Math.min(maxFloat, brightnessFloat); + brightnessFloat = Math.max(minFloat, brightnessFloat); + + Settings.System.putFloatForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessFloat, + UserHandle.USER_CURRENT_OR_SELF); + startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG), + UserHandle.CURRENT_OR_SELF); + } + return key_consumed; + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_MUTE: + if (mUseTvRouting || mHandleVolumeKeysInWM) { + // On TVs or when the configuration is enabled, volume keys never + // go to the foreground app. + dispatchDirectAudioEvent(event); + return key_consumed; } - float minFloat = mPowerManager.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); - float maxFloat = mPowerManager.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); - float stepFloat = (maxFloat - minFloat) / BRIGHTNESS_STEPS * direction; - float brightnessFloat = Settings.System.getFloatForUser( - mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FLOAT, - mContext.getDisplay().getBrightnessDefault(), - UserHandle.USER_CURRENT_OR_SELF); - brightnessFloat += stepFloat; - // Make sure we don't go beyond the limits. - brightnessFloat = Math.min(maxFloat, brightnessFloat); - brightnessFloat = Math.max(minFloat, brightnessFloat); - - Settings.System.putFloatForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightnessFloat, - UserHandle.USER_CURRENT_OR_SELF); - startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG), - UserHandle.CURRENT_OR_SELF); - } - return -1; - } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP - || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) { - if (mUseTvRouting || mHandleVolumeKeysInWM) { - // On TVs or when the configuration is enabled, volume keys never - // go to the foreground app. - dispatchDirectAudioEvent(event); - return -1; - } - // If the device is in VR mode and keys are "internal" (e.g. on the side of the - // device), then drop the volume keys and don't forward it to the application/dispatch - // the audio event. - if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) { - final InputDevice d = event.getDevice(); - if (d != null && !d.isExternal()) { - return -1; + // If the device is in VR mode and keys are "internal" (e.g. on the side of the + // device), then drop the volume keys and don't forward it to the + // application/dispatch the audio event. + if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) { + final InputDevice d = event.getDevice(); + if (d != null && !d.isExternal()) { + return key_consumed; + } } - } - } else if (keyCode == KeyEvent.KEYCODE_TAB && event.isMetaPressed()) { - // Pass through keyboard navigation keys. - return 0; - } else if (keyCode == KeyEvent.KEYCODE_ALL_APPS) { - if (!down) { - mHandler.removeMessages(MSG_HANDLE_ALL_APPS); - Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS); - msg.setAsynchronous(true); - msg.sendToTarget(); - } - return -1; - } else if (keyCode == KeyEvent.KEYCODE_NOTIFICATION) { - if (!down) { - toggleNotificationPanel(); - } - return -1; - } + break; + case KeyEvent.KEYCODE_TAB: + if (event.isMetaPressed()) { + // Pass through keyboard navigation keys. + return 0; + } + // Display task switcher for ALT-TAB. + if (down && repeatCount == 0) { + if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) { + final int shiftlessModifiers = + event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; + if (KeyEvent.metaStateHasModifiers( + shiftlessModifiers, KeyEvent.META_ALT_ON)) { + mRecentAppsHeldModifiers = shiftlessModifiers; + showRecentApps(true); + return key_consumed; + } + } + } + break; + case KeyEvent.KEYCODE_ALL_APPS: + if (!down) { + mHandler.removeMessages(MSG_HANDLE_ALL_APPS); + Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS); + msg.setAsynchronous(true); + msg.sendToTarget(); + } + return key_consumed; + case KeyEvent.KEYCODE_NOTIFICATION: + if (!down) { + toggleNotificationPanel(); + } + return key_consumed; - // Toggle Caps Lock on META-ALT. - boolean actionTriggered = false; - if (KeyEvent.isModifierKey(keyCode)) { - if (!mPendingCapsLockToggle) { - // Start tracking meta state for combo. - mInitialMetaState = mMetaState; - mPendingCapsLockToggle = true; - } else if (event.getAction() == KeyEvent.ACTION_UP) { - int altOnMask = mMetaState & KeyEvent.META_ALT_MASK; - int metaOnMask = mMetaState & KeyEvent.META_META_MASK; - - // Check for Caps Lock toggle - if ((metaOnMask != 0) && (altOnMask != 0)) { - // Check if nothing else is pressed - if (mInitialMetaState == (mMetaState ^ (altOnMask | metaOnMask))) { - // Handle Caps Lock Toggle + case KeyEvent.KEYCODE_SPACE: + // Handle keyboard layout switching. + if ((metaState & (KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK)) == 0) { + return 0; + } + // Share the same behavior with KEYCODE_LANGUAGE_SWITCH. + case KeyEvent.KEYCODE_LANGUAGE_SWITCH: + if (down && repeatCount == 0) { + int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; + mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); + return key_consumed; + } + break; + case KeyEvent.KEYCODE_META_LEFT: + case KeyEvent.KEYCODE_META_RIGHT: + if (down) { + if (event.isAltPressed()) { + mPendingCapsLockToggle = true; + mPendingMetaAction = false; + } else { + mPendingCapsLockToggle = false; + mPendingMetaAction = true; + } + } else { + // Toggle Caps Lock on META-ALT. + if (mPendingCapsLockToggle) { mInputManagerInternal.toggleCapsLock(event.getDeviceId()); - actionTriggered = true; + mPendingCapsLockToggle = false; + } else if (mPendingMetaAction) { + launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, + event.getDeviceId(), + event.getEventTime()); + mPendingMetaAction = false; } } + return key_consumed; + case KeyEvent.KEYCODE_ALT_LEFT: + case KeyEvent.KEYCODE_ALT_RIGHT: + if (down) { + if (event.isMetaPressed()) { + mPendingCapsLockToggle = true; + mPendingMetaAction = false; + } else { + mPendingCapsLockToggle = false; + } + } else { + // hide recent if triggered by ALT-TAB. + if (mRecentAppsHeldModifiers != 0 + && (metaState & mRecentAppsHeldModifiers) == 0) { + mRecentAppsHeldModifiers = 0; + hideRecentApps(true, false); + return key_consumed; + } - // Always stop tracking when key goes up. - mPendingCapsLockToggle = false; - } - } - // Store current meta state to be able to evaluate it later. - mMetaState = metaState; - - if (actionTriggered) { - return -1; - } - - if (KeyEvent.isMetaKey(keyCode)) { - if (down) { - mPendingMetaAction = true; - } else if (mPendingMetaAction) { - launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, event.getDeviceId(), - event.getEventTime()); - } - return -1; + // Toggle Caps Lock on META-ALT. + if (mPendingCapsLockToggle) { + mInputManagerInternal.toggleCapsLock(event.getDeviceId()); + mPendingCapsLockToggle = false; + return key_consumed; + } + } + break; } // Shortcuts are invoked through Search+key, so intercept those here @@ -2824,7 +2798,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { + "SEARCH+" + KeyEvent.keyCodeToString(keyCode)); } } - return -1; + return key_consumed; } } @@ -2846,7 +2820,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { + "the activity to which it is registered was not found: " + "META+" + KeyEvent.keyCodeToString(keyCode), ex); } - return -1; + return key_consumed; } } } @@ -2865,39 +2839,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { + "the activity to which it is registered was not found: " + "keyCode=" + keyCode + ", category=" + category, ex); } - return -1; - } - } - - // Display task switcher for ALT-TAB. - if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) { - if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) { - final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; - if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) { - mRecentAppsHeldModifiers = shiftlessModifiers; - showRecentApps(true); - return -1; - } + return key_consumed; } - } else if (!down && mRecentAppsHeldModifiers != 0 - && (metaState & mRecentAppsHeldModifiers) == 0) { - mRecentAppsHeldModifiers = 0; - hideRecentApps(true, false); - } - - // Handle keyboard language switching. - final boolean isCtrlOrMetaSpace = keyCode == KeyEvent.KEYCODE_SPACE - && (metaState & (KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK)) != 0; - if (down && repeatCount == 0 - && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH || isCtrlOrMetaSpace)) { - int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; - mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); - return -1; } if (isValidGlobalKey(keyCode) && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) { - return -1; + return key_consumed; } if (down) { @@ -2927,13 +2875,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { } catch (RemoteException e) { mShortcutKeyServices.delete(shortcutCode); } - return -1; + return key_consumed; } } // Reserve all the META modifier combos for system behavior if ((metaState & KeyEvent.META_META_ON) != 0) { - return -1; + return key_consumed; } // Let the application handle the key. @@ -3571,8 +3519,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { return result; } + // Alternate TV power to power key for Android TV device. + final HdmiControlManager hdmiControlManager = getHdmiControlManager(); + if (keyCode == KeyEvent.KEYCODE_TV_POWER && mHasFeatureLeanback + && (hdmiControlManager == null || !hdmiControlManager.shouldHandleTvPowerKey())) { + event = KeyEvent.obtain( + event.getDownTime(), event.getEventTime(), + event.getAction(), KeyEvent.KEYCODE_POWER, + event.getRepeatCount(), event.getMetaState(), + event.getDeviceId(), event.getScanCode(), + event.getFlags(), event.getSource(), event.getDisplayId(), null); + return interceptKeyBeforeQueueing(event, policyFlags); + } + if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { - mKeyCombinationManager.interceptKey(event, interactive); + handleKeyGesture(event, interactive); } // Enable haptics if down and virtual key without multiple repetitions. If this is a hard @@ -3587,12 +3548,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { switch (keyCode) { case KeyEvent.KEYCODE_BACK: { if (down) { - interceptBackKeyDown(); + mBackKeyHandled = false; } else { - boolean handled = interceptBackKeyUp(event); - + if (!hasLongPressOnBackBehavior()) { + mBackKeyHandled |= backKeyPress(); + } // Don't pass back press to app if we've already handled it via long press - if (handled) { + if (mBackKeyHandled) { result &= ~ACTION_PASS_TO_USER; } } @@ -3704,33 +3666,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_TV_POWER: { result &= ~ACTION_PASS_TO_USER; isWakeKey = false; // wake-up will be handled separately - HdmiControlManager hdmiControlManager = getHdmiControlManager(); - if (hdmiControlManager != null && hdmiControlManager.shouldHandleTvPowerKey()) { - if (down) { - hdmiControlManager.toggleAndFollowTvPower(); - } - } else if (mHasFeatureLeanback) { - KeyEvent fallbackEvent = KeyEvent.obtain( - event.getDownTime(), event.getEventTime(), - event.getAction(), KeyEvent.KEYCODE_POWER, - event.getRepeatCount(), event.getMetaState(), - event.getDeviceId(), event.getScanCode(), - event.getFlags(), event.getSource(), event.getDisplayId(), null); - if (down) { - interceptPowerKeyDown(fallbackEvent, interactive); - } else { - interceptPowerKeyUp(fallbackEvent, interactive, canceled); - } + if (down && hdmiControlManager != null) { + hdmiControlManager.toggleAndFollowTvPower(); } - // Ignore this key for any device that is not connected to a TV via HDMI and - // not an Android TV device. break; } case KeyEvent.KEYCODE_POWER: { EventLogTags.writeInterceptPower( KeyEvent.actionToString(event.getAction()), - mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter); + mPowerKeyHandled ? 1 : 0, + mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER)); // Any activity on the power button stops the accessibility shortcut result &= ~ACTION_PASS_TO_USER; isWakeKey = false; // wake-up will be handled separately @@ -3911,6 +3857,43 @@ public class PhoneWindowManager implements WindowManagerPolicy { return result; } + private void handleKeyGesture(KeyEvent event, boolean interactive) { + if (mKeyCombinationManager.interceptKey(event, interactive)) { + // handled by combo keys manager. + mSingleKeyGestureDetector.reset(); + return; + } + + if (event.getKeyCode() == KEYCODE_POWER && event.getAction() == KeyEvent.ACTION_DOWN) { + mPowerKeyHandled = handleCameraGesture(event, interactive); + if (mPowerKeyHandled) { + // handled by camera gesture. + mSingleKeyGestureDetector.reset(); + return; + } + } + + mSingleKeyGestureDetector.interceptKey(event); + } + + // The camera gesture will be detected by GestureLauncherService. + private boolean handleCameraGesture(KeyEvent event, boolean interactive) { + // camera gesture. + GestureLauncherService gestureService = LocalServices.getService( + GestureLauncherService.class); + if (gestureService == null) { + return false; + } + + final MutableBoolean outLaunched = new MutableBoolean(false); + final boolean gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, + interactive, outLaunched); + if (outLaunched.value && mRequestedOrGoingToSleep) { + mCameraGestureTriggeredDuringGoingToSleep = true; + } + return gesturedServiceIntercepted; + } + /** * Handle statusbar expansion events. * @param event @@ -4910,15 +4893,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private void schedulePossibleVeryLongPressReboot() { - mHandler.removeCallbacks(mPossibleVeryLongPressReboot); - mHandler.postDelayed(mPossibleVeryLongPressReboot, mVeryLongPressTimeout); - } - - private void cancelPossibleVeryLongPressReboot() { - mHandler.removeCallbacks(mPossibleVeryLongPressReboot); - } - // TODO (multidisplay): Support multiple displays in WindowManagerPolicy. private void updateScreenOffSleepToken(boolean acquire) { if (acquire) { diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java new file mode 100644 index 000000000000..3dafb0ce21ef --- /dev/null +++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java @@ -0,0 +1,356 @@ +/* + * 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.policy; + +import android.annotation.IntDef; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.view.KeyEvent; +import android.view.ViewConfiguration; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; + +/** + * Detect single key gesture: press, long press, very long press and multi press. + * + * Call {@link #reset} if current {@link KeyEvent} has been handled by another policy + */ + +public final class SingleKeyGestureDetector { + private static final String TAG = "SingleKeyGesture"; + private static final boolean DEBUG = false; + + private static final int MSG_KEY_LONG_PRESS = 0; + private static final int MSG_KEY_VERY_LONG_PRESS = 1; + private static final int MSG_KEY_DELAYED_PRESS = 2; + + private final long mLongPressTimeout; + private final long mVeryLongPressTimeout; + + private volatile int mKeyPressCounter; + + private final ArrayList<SingleKeyRule> mRules = new ArrayList(); + private SingleKeyRule mActiveRule = null; + + // Key code of current key down event, reset when key up. + private int mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN; + private volatile boolean mHandledByLongPress = false; + private final Handler mHandler; + private static final long MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout(); + + + /** Supported gesture flags */ + public static final int KEY_LONGPRESS = 1 << 1; + public static final int KEY_VERYLONGPRESS = 1 << 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "KEY_" }, value = { + KEY_LONGPRESS, + KEY_VERYLONGPRESS, + }) + public @interface KeyGestureFlag {} + + /** + * Rule definition for single keys gesture. + * E.g : define power key. + * <pre class="prettyprint"> + * SingleKeyRule rule = + * new SingleKeyRule(KEYCODE_POWER, KEY_LONGPRESS|KEY_VERYLONGPRESS) { + * int getMaxMultiPressCount() { // maximum multi press count. } + * void onPress(long downTime) { // short press behavior. } + * void onLongPress() { // long press behavior. } + * void onVeryLongPress() { // very long press behavior. } + * void onMultiPress(long downTime, int count) { // multi press behavior. } + * }; + * </pre> + */ + abstract static class SingleKeyRule { + private final int mKeyCode; + private final int mSupportedGestures; + + SingleKeyRule(int keyCode, @KeyGestureFlag int supportedGestures) { + mKeyCode = keyCode; + mSupportedGestures = supportedGestures; + } + + /** + * True if the rule could intercept the key. + */ + private boolean shouldInterceptKey(int keyCode) { + return keyCode == mKeyCode; + } + + /** + * True if the rule support long press. + */ + private boolean supportLongPress() { + return (mSupportedGestures & KEY_LONGPRESS) != 0; + } + + /** + * True if the rule support very long press. + */ + private boolean supportVeryLongPress() { + return (mSupportedGestures & KEY_VERYLONGPRESS) != 0; + } + + /** + * Maximum count of multi presses. + * Return 1 will trigger onPress immediately when {@link KeyEvent.ACTION_UP}. + * Otherwise trigger onMultiPress immediately when reach max count when + * {@link KeyEvent.ACTION_DOWN}. + */ + int getMaxMultiPressCount() { + return 1; + } + + /** + * Called when short press has been detected. + */ + abstract void onPress(long downTime); + /** + * Callback when multi press (>= 2) has been detected. + */ + void onMultiPress(long downTime, int count) {} + /** + * Callback when long press has been detected. + */ + void onLongPress(long downTime) {} + /** + * Callback when very long press has been detected. + */ + void onVeryLongPress(long downTime) {} + + @Override + public String toString() { + return "KeyCode = " + KeyEvent.keyCodeToString(mKeyCode) + + ", long press : " + supportLongPress() + + ", very Long press : " + supportVeryLongPress() + + ", max multi press count : " + getMaxMultiPressCount(); + } + } + + public SingleKeyGestureDetector(Context context) { + mLongPressTimeout = ViewConfiguration.get(context).getDeviceGlobalActionKeyTimeout(); + mVeryLongPressTimeout = context.getResources().getInteger( + com.android.internal.R.integer.config_veryLongPressTimeout); + mHandler = new KeyHandler(); + } + + void addRule(SingleKeyRule rule) { + mRules.add(rule); + } + + void interceptKey(KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + interceptKeyDown(event); + } else { + interceptKeyUp(event); + } + } + + private void interceptKeyDown(KeyEvent event) { + final int keyCode = event.getKeyCode(); + // same key down. + if (mDownKeyCode == keyCode) { + if (mActiveRule != null && (event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0 + && !mHandledByLongPress) { + if (DEBUG) { + Log.i(TAG, "Long press Key " + KeyEvent.keyCodeToString(keyCode)); + } + mHandledByLongPress = true; + mHandler.removeMessages(MSG_KEY_LONG_PRESS); + mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS); + mActiveRule.onLongPress(event.getEventTime()); + } + return; + } + + // When a different key is pressed, stop processing gestures for the currently active key. + if (mDownKeyCode != KeyEvent.KEYCODE_UNKNOWN + || (mActiveRule != null && !mActiveRule.shouldInterceptKey(keyCode))) { + if (DEBUG) { + Log.i(TAG, "Press another key " + KeyEvent.keyCodeToString(keyCode)); + } + + reset(); + } + mDownKeyCode = keyCode; + + // Picks a new rule, return if no rule picked. + if (mActiveRule == null) { + final int count = mRules.size(); + for (int index = 0; index < count; index++) { + final SingleKeyRule rule = mRules.get(index); + if (rule.shouldInterceptKey(keyCode)) { + mActiveRule = rule; + break; + } + } + } + if (mActiveRule == null) { + return; + } + + final long eventTime = event.getEventTime(); + if (mKeyPressCounter == 0) { + if (mActiveRule.supportLongPress()) { + final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0, + eventTime); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, mLongPressTimeout); + } + + if (mActiveRule.supportVeryLongPress()) { + final Message msg = mHandler.obtainMessage(MSG_KEY_VERY_LONG_PRESS, keyCode, 0, + eventTime); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, mVeryLongPressTimeout); + } + } else { + mHandler.removeMessages(MSG_KEY_LONG_PRESS); + mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS); + mHandler.removeMessages(MSG_KEY_DELAYED_PRESS); + + // Trigger multi press immediately when reach max count.( > 1) + if (mKeyPressCounter == mActiveRule.getMaxMultiPressCount() - 1) { + if (DEBUG) { + Log.i(TAG, "Trigger multi press " + mActiveRule.toString() + " for it" + + " reach the max count " + mKeyPressCounter); + } + mActiveRule.onMultiPress(eventTime, mKeyPressCounter + 1); + mKeyPressCounter = 0; + } + } + } + + private boolean interceptKeyUp(KeyEvent event) { + mHandler.removeMessages(MSG_KEY_LONG_PRESS); + mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS); + mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN; + if (mActiveRule == null) { + return false; + } + + if (mHandledByLongPress) { + mHandledByLongPress = false; + return true; + } + + final long downTime = event.getDownTime(); + if (event.getKeyCode() == mActiveRule.mKeyCode) { + // Directly trigger short press when max count is 1. + if (mActiveRule.getMaxMultiPressCount() == 1) { + mActiveRule.onPress(downTime); + return true; + } + + // This could be a multi-press. Wait a little bit longer to confirm. + mKeyPressCounter++; + Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode, + mKeyPressCounter, downTime); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT); + return true; + } + reset(); + return false; + } + + int getKeyPressCounter(int keyCode) { + if (mActiveRule != null && mActiveRule.mKeyCode == keyCode) { + return mKeyPressCounter; + } else { + return 0; + } + } + + void reset() { + if (mActiveRule != null) { + if (mDownKeyCode != KeyEvent.KEYCODE_UNKNOWN) { + mHandler.removeMessages(MSG_KEY_LONG_PRESS); + mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS); + } + + if (mKeyPressCounter > 0) { + mHandler.removeMessages(MSG_KEY_DELAYED_PRESS); + mKeyPressCounter = 0; + } + mActiveRule = null; + } + + mHandledByLongPress = false; + mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN; + } + + boolean isKeyIntercepted(int keyCode) { + if (mActiveRule != null && mActiveRule.shouldInterceptKey(keyCode)) { + return mHandledByLongPress; + } + return false; + } + + private class KeyHandler extends Handler { + KeyHandler() { + super(Looper.getMainLooper()); + } + + @Override + public void handleMessage(Message msg) { + if (mActiveRule == null) { + return; + } + final int keyCode = msg.arg1; + final long eventTime = (long) msg.obj; + switch(msg.what) { + case MSG_KEY_LONG_PRESS: + if (DEBUG) { + Log.i(TAG, "Detect long press " + KeyEvent.keyCodeToString(keyCode)); + } + mHandledByLongPress = true; + mActiveRule.onLongPress(eventTime); + break; + case MSG_KEY_VERY_LONG_PRESS: + if (DEBUG) { + Log.i(TAG, "Detect very long press " + + KeyEvent.keyCodeToString(keyCode)); + } + mHandledByLongPress = true; + mActiveRule.onVeryLongPress(eventTime); + break; + case MSG_KEY_DELAYED_PRESS: + if (DEBUG) { + Log.i(TAG, "Detect press " + KeyEvent.keyCodeToString(keyCode) + + ", count " + mKeyPressCounter); + } + if (mKeyPressCounter == 1) { + mActiveRule.onPress(eventTime); + } else { + mActiveRule.onMultiPress(eventTime, mKeyPressCounter); + } + mKeyPressCounter = 0; + break; + } + } + } +} diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index db33e750d803..b5a9acacec83 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -672,7 +672,22 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { /** * @return whether {@param win} can be hidden by Keyguard */ - public boolean canBeHiddenByKeyguardLw(WindowState win); + default boolean canBeHiddenByKeyguardLw(WindowState win) { + // Keyguard visibility of window from activities are determined over activity visibility. + if (win.getAppToken() != null) { + return false; + } + switch (win.getAttrs().type) { + case TYPE_NOTIFICATION_SHADE: + case TYPE_STATUS_BAR: + case TYPE_NAVIGATION_BAR: + case TYPE_WALLPAPER: + return false; + default: + // Hide only windows below the keyguard host window. + return getWindowLayerLw(win) < getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE); + } + } /** * Called when the system would like to show a UI to indicate that an diff --git a/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java new file mode 100644 index 000000000000..2fcd178c3d15 --- /dev/null +++ b/services/core/java/com/android/server/power/DisplayGroupPowerStateMapper.java @@ -0,0 +1,280 @@ +/* + * 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.power; + +import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; +import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; +import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING; +import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING; + +import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_ADDED; +import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_CHANGED; +import static com.android.server.power.DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener.DISPLAY_GROUP_REMOVED; + +import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; +import android.os.PowerManagerInternal; +import android.util.Slog; +import android.util.SparseArray; +import android.view.Display; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; +import com.android.server.display.DisplayGroup; + +/** + * Responsible for creating {@link DisplayPowerRequest}s and associating them with + * {@link com.android.server.display.DisplayGroup}s. + * + * Each {@link com.android.server.display.DisplayGroup} has a single {@link DisplayPowerRequest} + * which is used to request power state changes to every display in the group. + */ +public class DisplayGroupPowerStateMapper { + + private static final String TAG = "DisplayPowerRequestMapper"; + + /** Lock obtained from {@link PowerManagerService}. */ + private final Object mLock; + + /** Listener to inform of changes to display groups. */ + private final DisplayGroupPowerChangeListener mListener; + + /** A mapping from DisplayGroup Id to DisplayGroup information. */ + @GuardedBy("mLock") + private final SparseArray<DisplayGroupInfo> mDisplayGroupInfos = new SparseArray<>(); + + /** A cached array of DisplayGroup Ids. */ + @GuardedBy("mLock") + private int[] mDisplayGroupIds; + + private final DisplayManagerInternal.DisplayGroupListener mDisplayGroupListener = + new DisplayManagerInternal.DisplayGroupListener() { + @Override + public void onDisplayGroupAdded(int groupId) { + synchronized (mLock) { + if (mDisplayGroupInfos.contains(groupId)) { + Slog.e(TAG, "Tried to add already existing group:" + groupId); + return; + } + // For now, only the default group supports sandman (dream/AOD). + final boolean supportsSandman = groupId == Display.DEFAULT_DISPLAY_GROUP; + final DisplayGroupInfo displayGroupInfo = new DisplayGroupInfo( + new DisplayPowerRequest(), + getGlobalWakefulnessLocked(), + /* ready= */ false, + supportsSandman); + mDisplayGroupInfos.append(groupId, displayGroupInfo); + mDisplayGroupIds = ArrayUtils.appendInt(mDisplayGroupIds, groupId); + mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_ADDED, groupId); + } + } + + @Override + public void onDisplayGroupRemoved(int groupId) { + synchronized (mLock) { + if (!mDisplayGroupInfos.contains(groupId)) { + Slog.e(TAG, "Tried to remove non-existent group:" + groupId); + return; + } + mDisplayGroupInfos.delete(groupId); + mDisplayGroupIds = ArrayUtils.removeInt(mDisplayGroupIds, groupId); + mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_REMOVED, groupId); + } + } + + @Override + public void onDisplayGroupChanged(int groupId) { + synchronized (mLock) { + mListener.onDisplayGroupEventLocked(DISPLAY_GROUP_CHANGED, groupId); + } + } + }; + + DisplayGroupPowerStateMapper(Object lock, DisplayManagerInternal displayManagerInternal, + DisplayGroupPowerChangeListener listener) { + mLock = lock; + mListener = listener; + displayManagerInternal.registerDisplayGroupListener(mDisplayGroupListener); + + final DisplayGroupInfo displayGroupInfo = new DisplayGroupInfo( + new DisplayPowerRequest(), WAKEFULNESS_AWAKE, /* ready= */ + false, /* supportsSandman= */ true); + mDisplayGroupInfos.append(Display.DEFAULT_DISPLAY_GROUP, displayGroupInfo); + mDisplayGroupIds = new int[]{Display.DEFAULT_DISPLAY_GROUP}; + } + + DisplayPowerRequest getPowerRequestLocked(int groupId) { + return mDisplayGroupInfos.get(groupId).displayPowerRequest; + } + + int[] getDisplayGroupIdsLocked() { + return mDisplayGroupIds; + } + + int getWakefulnessLocked(int groupId) { + return mDisplayGroupInfos.get(groupId).wakefulness; + } + + void setLastPowerOnTimeLocked(int groupId, long eventTime) { + mDisplayGroupInfos.get(groupId).lastPowerOnTime = eventTime; + } + + long getLastPowerOnTimeLocked(int groupId) { + return mDisplayGroupInfos.get(groupId).lastPowerOnTime; + } + + /** + * Returns the amalgamated wakefulness of all {@link DisplayGroup DisplayGroups}. + * + * <p>This will be the highest wakeful state of all {@link DisplayGroup DisplayGroups}; ordered + * from highest to lowest: + * <ol> + * <li>{@link PowerManagerInternal#WAKEFULNESS_AWAKE} + * <li>{@link PowerManagerInternal#WAKEFULNESS_DREAMING} + * <li>{@link PowerManagerInternal#WAKEFULNESS_DOZING} + * <li>{@link PowerManagerInternal#WAKEFULNESS_ASLEEP} + * </ol> + */ + int getGlobalWakefulnessLocked() { + final int size = mDisplayGroupInfos.size(); + int deviceWakefulness = WAKEFULNESS_ASLEEP; + for (int i = 0; i < size; i++) { + final int wakefulness = mDisplayGroupInfos.valueAt(i).wakefulness; + if (wakefulness == WAKEFULNESS_AWAKE) { + return WAKEFULNESS_AWAKE; + } else if (wakefulness == WAKEFULNESS_DREAMING + && (deviceWakefulness == WAKEFULNESS_ASLEEP + || deviceWakefulness == WAKEFULNESS_DOZING)) { + deviceWakefulness = WAKEFULNESS_DREAMING; + } else if (wakefulness == WAKEFULNESS_DOZING + && deviceWakefulness == WAKEFULNESS_ASLEEP) { + deviceWakefulness = WAKEFULNESS_DOZING; + } + } + + return deviceWakefulness; + } + + /** + * Sets the {@code wakefulness} value for the {@link DisplayGroup} specified by the provided + * {@code groupId}. + * + * @return {@code true} if the wakefulness value was changed; {@code false} otherwise. + */ + boolean setWakefulnessLocked(int groupId, int wakefulness) { + final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId); + if (displayGroupInfo.wakefulness != wakefulness) { + displayGroupInfo.wakefulness = wakefulness; + return true; + } + + return false; + } + + boolean isSandmanSummoned(int groupId) { + return mDisplayGroupInfos.get(groupId).sandmanSummoned; + } + + boolean isSandmanSupported(int groupId) { + return mDisplayGroupInfos.get(groupId).supportsSandman; + } + + /** + * Sets whether or not the sandman is summoned for the given {@code groupId}. + * + * @param groupId Signifies the DisplayGroup for which to summon or unsummon the + * sandman. + * @param sandmanSummoned {@code true} to summon the sandman; {@code false} to unsummon. + */ + void setSandmanSummoned(int groupId, boolean sandmanSummoned) { + final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId); + displayGroupInfo.sandmanSummoned = displayGroupInfo.supportsSandman && sandmanSummoned; + } + + /** + * Returns {@code true} if every display in the specified group has its requested state matching + * its actual state. + * + * @param groupId The identifier for the display group to check for readiness. + */ + boolean isReady(int groupId) { + return mDisplayGroupInfos.get(groupId).ready; + } + + /** Returns {@code true} if every display has its requested state matching its actual state. */ + boolean areAllDisplaysReadyLocked() { + final int size = mDisplayGroupInfos.size(); + for (int i = 0; i < size; i++) { + if (!mDisplayGroupInfos.valueAt(i).ready) { + return false; + } + } + + return true; + } + + /** + * Sets whether the displays specified by the provided {@code groupId} are all ready. + * + * <p>A display is ready if its reported + * {@link DisplayManagerInternal.DisplayPowerCallbacks#onStateChanged() actual state} matches + * its {@link DisplayManagerInternal#requestPowerState requested state}. + * + * @param groupId The identifier for the display group. + * @param ready {@code true} if every display in the group is ready; otherwise {@code false}. + * @return {@code true} if the ready state changed; otherwise {@code false}. + */ + boolean setDisplayGroupReadyLocked(int groupId, boolean ready) { + final DisplayGroupInfo displayGroupInfo = mDisplayGroupInfos.get(groupId); + if (displayGroupInfo.ready != ready) { + displayGroupInfo.ready = ready; + return true; + } + + return false; + } + + /** + * Interface through which an interested party may be informed of {@link DisplayGroup} events. + */ + interface DisplayGroupPowerChangeListener { + int DISPLAY_GROUP_ADDED = 0; + int DISPLAY_GROUP_REMOVED = 1; + int DISPLAY_GROUP_CHANGED = 2; + + void onDisplayGroupEventLocked(int event, int groupId); + } + + private static final class DisplayGroupInfo { + public final DisplayPowerRequest displayPowerRequest; + public int wakefulness; + public boolean ready; + public long lastPowerOnTime; + public boolean sandmanSummoned; + + /** {@code true} if this DisplayGroup supports dreaming; otherwise {@code false}. */ + public boolean supportsSandman; + + DisplayGroupInfo(DisplayPowerRequest displayPowerRequest, int wakefulness, boolean ready, + boolean supportsSandman) { + this.displayPowerRequest = displayPowerRequest; + this.wakefulness = wakefulness; + this.ready = ready; + this.supportsSandman = supportsSandman; + } + } +} diff --git a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java deleted file mode 100644 index 2fc3e40acd4d..000000000000 --- a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java +++ /dev/null @@ -1,121 +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.power; - -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManagerInternal; -import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; -import android.os.Handler; -import android.util.SparseArray; -import android.util.SparseIntArray; -import android.view.Display; - -import com.android.internal.annotations.GuardedBy; - -/** - * Responsible for creating {@link DisplayPowerRequest}s and associating them with - * {@link com.android.server.display.DisplayGroup}s. - * - * Each {@link com.android.server.display.DisplayGroup} has a single {@link DisplayPowerRequest} - * which is used to request power state changes to every display in the group. - */ -class DisplayPowerRequestMapper { - - private final Object mLock = new Object(); - - /** A mapping from LogicalDisplay Id to DisplayGroup Id. */ - @GuardedBy("mLock") - private final SparseIntArray mDisplayGroupIds = new SparseIntArray(); - - /** A mapping from DisplayGroup Id to DisplayPowerRequest. */ - @GuardedBy("mLock") - private final SparseArray<DisplayPowerRequest> mDisplayPowerRequests = new SparseArray<>(); - - private final DisplayManagerInternal mDisplayManagerInternal; - - private final DisplayManager.DisplayListener mDisplayListener = - new DisplayManager.DisplayListener() { - - @Override - public void onDisplayAdded(int displayId) { - synchronized (mLock) { - if (mDisplayGroupIds.indexOfKey(displayId) >= 0) { - return; - } - final int displayGroupId = mDisplayManagerInternal.getDisplayGroupId( - displayId); - if (!mDisplayPowerRequests.contains(displayGroupId)) { - // A new DisplayGroup was created; create a new DisplayPowerRequest. - mDisplayPowerRequests.append(displayGroupId, new DisplayPowerRequest()); - } - mDisplayGroupIds.append(displayId, displayGroupId); - } - } - - @Override - public void onDisplayRemoved(int displayId) { - synchronized (mLock) { - final int index = mDisplayGroupIds.indexOfKey(displayId); - if (index < 0) { - return; - } - final int displayGroupId = mDisplayGroupIds.valueAt(index); - mDisplayGroupIds.removeAt(index); - - if (mDisplayGroupIds.indexOfValue(displayGroupId) < 0) { - // The DisplayGroup no longer exists; delete the DisplayPowerRequest. - mDisplayPowerRequests.delete(displayGroupId); - } - } - } - - @Override - public void onDisplayChanged(int displayId) { - synchronized (mLock) { - final int newDisplayGroupId = mDisplayManagerInternal.getDisplayGroupId( - displayId); - final int oldDisplayGroupId = mDisplayGroupIds.get(displayId); - - if (!mDisplayPowerRequests.contains(newDisplayGroupId)) { - // A new DisplayGroup was created; create a new DisplayPowerRequest. - mDisplayPowerRequests.append(newDisplayGroupId, - new DisplayPowerRequest()); - } - mDisplayGroupIds.put(displayId, newDisplayGroupId); - - if (mDisplayGroupIds.indexOfValue(oldDisplayGroupId) < 0) { - // The DisplayGroup no longer exists; delete the DisplayPowerRequest. - mDisplayPowerRequests.delete(oldDisplayGroupId); - } - } - } - }; - - DisplayPowerRequestMapper(DisplayManager displayManager, - DisplayManagerInternal displayManagerInternal, Handler handler) { - mDisplayManagerInternal = displayManagerInternal; - displayManager.registerDisplayListener(mDisplayListener, handler); - mDisplayPowerRequests.append(Display.DEFAULT_DISPLAY_GROUP, new DisplayPowerRequest()); - mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, Display.DEFAULT_DISPLAY_GROUP); - } - - DisplayPowerRequest get(int displayId) { - synchronized (mLock) { - return mDisplayPowerRequests.get(mDisplayGroupIds.get(displayId)); - } - } -} diff --git a/services/core/java/com/android/server/power/FaceDownDetector.java b/services/core/java/com/android/server/power/FaceDownDetector.java new file mode 100644 index 000000000000..2442079bec8b --- /dev/null +++ b/services/core/java/com/android/server/power/FaceDownDetector.java @@ -0,0 +1,456 @@ +/* + * 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.power; + +import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE; + +import android.annotation.NonNull; +import android.app.ActivityThread; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Handler; +import android.os.Looper; +import android.os.SystemClock; +import android.provider.DeviceConfig; +import android.util.Slog; + +import com.android.internal.util.FrameworkStatsLog; + +import java.io.PrintWriter; +import java.time.Duration; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; + +/** + * Class used to detect when the phone is placed face down. This is used for Flip to Screen Off. A + * client can use this detector to trigger state changes like screen off when the phone is face + * down. + */ +public class FaceDownDetector implements SensorEventListener { + + private static final String TAG = "FaceDownDetector"; + private static final boolean DEBUG = false; + + private static final int SCREEN_OFF_RESULT = + FrameworkStatsLog.FACE_DOWN_REPORTED__FACE_DOWN_RESPONSE__SCREEN_OFF; + private static final int USER_INTERACTION = + FrameworkStatsLog.FACE_DOWN_REPORTED__FACE_DOWN_RESPONSE__USER_INTERACTION; + private static final int UNFLIP = + FrameworkStatsLog.FACE_DOWN_REPORTED__FACE_DOWN_RESPONSE__UNFLIP; + + /** + * Used by the ExponentialMovingAverage accelerations, this determines how quickly the + * average can change. A number closer to 1 will mean it will take longer to change. + */ + private static final float MOVING_AVERAGE_WEIGHT = 0.5f; + + /** DeviceConfig flag name, if {@code true}, enables Face Down features. */ + private static final String KEY_FEATURE_ENABLED = "enable_flip_to_screen_off"; + + /** Default value in absence of {@link DeviceConfig} override. */ + private static final boolean DEFAULT_FEATURE_ENABLED = true; + + private boolean mIsEnabled; + + /** + * DeviceConfig flag name, determines how long to disable sensor when user interacts while + * device is flipped. + */ + private static final String KEY_INTERACTION_BACKOFF = "face_down_interaction_backoff_millis"; + + /** Default value in absence of {@link DeviceConfig} override. */ + private static final long DEFAULT_INTERACTION_BACKOFF = 60_000; + + private long mUserInteractionBackoffMillis; + + /** + * DeviceConfig flag name, defines the max change in acceleration which will prevent face down + * due to movement. + */ + static final String KEY_ACCELERATION_THRESHOLD = "acceleration_threshold"; + + /** Default value in absence of {@link DeviceConfig} override. */ + static final float DEFAULT_ACCELERATION_THRESHOLD = 0.2f; + + private float mAccelerationThreshold; + + /** + * DeviceConfig flag name, defines the maximum z-axis acceleration that will indicate the phone + * is face down. + */ + static final String KEY_Z_ACCELERATION_THRESHOLD = "z_acceleration_threshold"; + + /** Default value in absence of {@link DeviceConfig} override. */ + static final float DEFAULT_Z_ACCELERATION_THRESHOLD = -9.5f; + + private float mZAccelerationThreshold; + + /** + * After going face down, we relax the threshold to make it more difficult to exit face down + * than to enter it. + */ + private float mZAccelerationThresholdLenient; + + /** + * DeviceConfig flag name, defines the minimum amount of time that has to pass while the phone + * is face down and not moving in order to trigger face down behavior, in milliseconds. + */ + static final String KEY_TIME_THRESHOLD_MILLIS = "time_threshold_millis"; + + /** Default value in absence of {@link DeviceConfig} override. */ + static final long DEFAULT_TIME_THRESHOLD_MILLIS = 1_000L; + + private Duration mTimeThreshold; + + private Sensor mAccelerometer; + private SensorManager mSensorManager; + private final Consumer<Boolean> mOnFlip; + + /** Values we store for logging purposes. */ + private long mLastFlipTime = 0L; + public int mPreviousResultType = 0; + public long mPreviousResultTime = 0L; + private long mMillisSaved = 0L; + + private final ExponentialMovingAverage mCurrentXYAcceleration = + new ExponentialMovingAverage(MOVING_AVERAGE_WEIGHT); + private final ExponentialMovingAverage mCurrentZAcceleration = + new ExponentialMovingAverage(MOVING_AVERAGE_WEIGHT); + + private boolean mFaceDown = false; + private boolean mActive = false; + + private float mPrevAcceleration = 0; + private long mPrevAccelerationTime = 0; + + private boolean mZAccelerationIsFaceDown = false; + private long mZAccelerationFaceDownTime = 0L; + + private final Handler mHandler; + private final Runnable mUserActivityRunnable; + + public FaceDownDetector(@NonNull Consumer<Boolean> onFlip) { + mOnFlip = Objects.requireNonNull(onFlip); + mHandler = new Handler(Looper.getMainLooper()); + mUserActivityRunnable = () -> { + if (mFaceDown) { + exitFaceDown(USER_INTERACTION, SystemClock.uptimeMillis() - mLastFlipTime); + checkAndUpdateActiveState(false); + } + }; + } + + /** Initializes the FaceDownDetector and all necessary listeners. */ + public void systemReady(Context context) { + mSensorManager = context.getSystemService(SensorManager.class); + mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + readValuesFromDeviceConfig(); + checkAndUpdateActiveState(true); + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE, + ActivityThread.currentApplication().getMainExecutor(), + (properties) -> onDeviceConfigChange(properties.getKeyset())); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_SCREEN_OFF); + intentFilter.addAction(Intent.ACTION_SCREEN_ON); + intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + context.registerReceiver(new ScreenStateReceiver(), intentFilter); + } + + /** + * Sets the active state of the detector. If false, we will not process accelerometer changes. + */ + private void checkAndUpdateActiveState(boolean active) { + if (mIsEnabled && mActive != active) { + final long currentTime = SystemClock.uptimeMillis(); + // Don't make active if there was recently a user interaction while face down. + if (active && mPreviousResultType == USER_INTERACTION + && currentTime - mPreviousResultTime < mUserInteractionBackoffMillis) { + return; + } + if (DEBUG) Slog.d(TAG, "Update active - " + active); + mActive = active; + if (!active) { + if (mFaceDown && mPreviousResultTime != USER_INTERACTION) { + mPreviousResultType = SCREEN_OFF_RESULT; + mPreviousResultTime = currentTime; + } + mSensorManager.unregisterListener(this); + mFaceDown = false; + mOnFlip.accept(false); + } else { + if (mPreviousResultType == SCREEN_OFF_RESULT) { + logScreenOff(); + } + mSensorManager.registerListener( + this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); + } + } + } + + /** Prints state information about FaceDownDetector */ + public void dump(PrintWriter pw) { + pw.println("FaceDownDetector:"); + pw.println(" mFaceDown=" + mFaceDown); + pw.println(" mActive=" + mActive); + pw.println(" mLastFlipTime=" + mLastFlipTime); + pw.println(" mUserInteractionBackoffMillis=" + mUserInteractionBackoffMillis); + pw.println(" mPreviousResultTime=" + mPreviousResultTime); + pw.println(" mPreviousResultType=" + mPreviousResultType); + pw.println(" mMillisSaved=" + mMillisSaved); + pw.println(" mZAccelerationThreshold=" + mZAccelerationThreshold); + pw.println(" mAccelerationThreshold=" + mAccelerationThreshold); + pw.println(" mTimeThreshold=" + mTimeThreshold); + } + + @Override + public void onSensorChanged(SensorEvent event) { + if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) return; + if (!mActive || !mIsEnabled) return; + + final float x = event.values[0]; + final float y = event.values[1]; + mCurrentXYAcceleration.updateMovingAverage(x * x + y * y); + mCurrentZAcceleration.updateMovingAverage(event.values[2]); + + // Detect movement + // If the x, y acceleration is within the acc threshold for at least a length of time longer + // than the time threshold, we set moving to true. + final long curTime = event.timestamp; + if (Math.abs(mCurrentXYAcceleration.mMovingAverage - mPrevAcceleration) + > mAccelerationThreshold) { + mPrevAcceleration = mCurrentXYAcceleration.mMovingAverage; + mPrevAccelerationTime = curTime; + } + final boolean moving = curTime - mPrevAccelerationTime <= mTimeThreshold.toNanos(); + + // If the z acceleration is beyond the gravity/z-acceleration threshold for at least a + // length of time longer than the time threshold, we set isFaceDownForPeriod to true. + final float zAccelerationThreshold = + mFaceDown ? mZAccelerationThresholdLenient : mZAccelerationThreshold; + final boolean isCurrentlyFaceDown = + mCurrentZAcceleration.mMovingAverage < zAccelerationThreshold; + final boolean isFaceDownForPeriod = isCurrentlyFaceDown + && mZAccelerationIsFaceDown + && curTime - mZAccelerationFaceDownTime > mTimeThreshold.toNanos(); + if (isCurrentlyFaceDown && !mZAccelerationIsFaceDown) { + mZAccelerationFaceDownTime = curTime; + mZAccelerationIsFaceDown = true; + } else if (!isCurrentlyFaceDown) { + mZAccelerationIsFaceDown = false; + } + + + if (!moving && isFaceDownForPeriod && !mFaceDown) { + faceDownDetected(); + } else if (!isFaceDownForPeriod && mFaceDown) { + unFlipDetected(); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) {} + + private void faceDownDetected() { + if (DEBUG) Slog.d(TAG, "Triggered faceDownDetected."); + mLastFlipTime = SystemClock.uptimeMillis(); + mFaceDown = true; + mOnFlip.accept(true); + } + + private void unFlipDetected() { + if (DEBUG) Slog.d(TAG, "Triggered exitFaceDown"); + exitFaceDown(UNFLIP, SystemClock.uptimeMillis() - mLastFlipTime); + } + + /** + * The user interacted with the screen while face down, indicated the phone is in use. + * We log this event and temporarily make this detector inactive. + */ + public void userActivity() { + mHandler.post(mUserActivityRunnable); + } + + private void exitFaceDown(int resultType, long millisSinceFlip) { + FrameworkStatsLog.write(FrameworkStatsLog.FACE_DOWN_REPORTED, + resultType, + millisSinceFlip, + /* millis_until_normal_timeout= */ 0L, + /* millis_until_next_screen_on= */ 0L); + mFaceDown = false; + mLastFlipTime = 0L; + mPreviousResultType = resultType; + mPreviousResultTime = SystemClock.uptimeMillis(); + mOnFlip.accept(false); + } + + private void logScreenOff() { + if (mPreviousResultType == SCREEN_OFF_RESULT) { + final long currentTime = SystemClock.uptimeMillis(); + FrameworkStatsLog.write(FrameworkStatsLog.FACE_DOWN_REPORTED, + mPreviousResultType, + /* millis_since_flip= */ mPreviousResultTime - mLastFlipTime, + mMillisSaved, + /* millis_until_next_screen_on= */ currentTime - mPreviousResultTime); + mPreviousResultType = -1; + } + } + + private boolean isEnabled() { + return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_FEATURE_ENABLED, + DEFAULT_FEATURE_ENABLED); + } + + private float getAccelerationThreshold() { + return getFloatFlagValue(KEY_ACCELERATION_THRESHOLD, + DEFAULT_ACCELERATION_THRESHOLD, + -2.0f, + 2.0f); + } + + private float getZAccelerationThreshold() { + return getFloatFlagValue(KEY_Z_ACCELERATION_THRESHOLD, + DEFAULT_Z_ACCELERATION_THRESHOLD, + -15.0f, + 0.0f); + } + + private long getUserInteractionBackoffMillis() { + return getLongFlagValue(KEY_INTERACTION_BACKOFF, + DEFAULT_INTERACTION_BACKOFF, + 0, + 3600_000); + } + + private float getFloatFlagValue(String key, float defaultValue, float min, float max) { + final float value = DeviceConfig.getFloat(NAMESPACE_ATTENTION_MANAGER_SERVICE, + key, + defaultValue); + + if (value < min || value > max) { + Slog.w(TAG, "Bad flag value supplied for: " + key); + return defaultValue; + } + + return value; + } + + private long getLongFlagValue(String key, long defaultValue, long min, long max) { + final long value = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE, + key, + defaultValue); + + if (value < min || value > max) { + Slog.w(TAG, "Bad flag value supplied for: " + key); + return defaultValue; + } + + return value; + } + + private Duration getTimeThreshold() { + final long millis = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_TIME_THRESHOLD_MILLIS, + DEFAULT_TIME_THRESHOLD_MILLIS); + + if (millis < 0 || millis > 15_000) { + Slog.w(TAG, "Bad flag value supplied for: " + KEY_TIME_THRESHOLD_MILLIS); + return Duration.ofMillis(DEFAULT_TIME_THRESHOLD_MILLIS); + } + + return Duration.ofMillis(millis); + } + + private void onDeviceConfigChange(@NonNull Set<String> keys) { + for (String key : keys) { + switch (key) { + case KEY_ACCELERATION_THRESHOLD: + case KEY_Z_ACCELERATION_THRESHOLD: + case KEY_TIME_THRESHOLD_MILLIS: + case KEY_FEATURE_ENABLED: + readValuesFromDeviceConfig(); + return; + default: + Slog.i(TAG, "Ignoring change on " + key); + } + } + } + + private void readValuesFromDeviceConfig() { + mAccelerationThreshold = getAccelerationThreshold(); + mZAccelerationThreshold = getZAccelerationThreshold(); + mZAccelerationThresholdLenient = mZAccelerationThreshold + 1.0f; + mTimeThreshold = getTimeThreshold(); + mIsEnabled = isEnabled(); + mUserInteractionBackoffMillis = getUserInteractionBackoffMillis(); + + Slog.i(TAG, "readValuesFromDeviceConfig():" + + "\nmAccelerationThreshold=" + mAccelerationThreshold + + "\nmZAccelerationThreshold=" + mZAccelerationThreshold + + "\nmTimeThreshold=" + mTimeThreshold + + "\nmIsEnabled=" + mIsEnabled); + } + + /** + * Sets how much screen on time might be saved as a result of this detector. Currently used for + * logging purposes. + */ + public void setMillisSaved(long millisSaved) { + mMillisSaved = millisSaved; + } + + private final class ScreenStateReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { + checkAndUpdateActiveState(false); + } else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) { + checkAndUpdateActiveState(true); + } + } + } + + private final class ExponentialMovingAverage { + private final float mAlpha; + private final float mInitialAverage; + private float mMovingAverage; + + ExponentialMovingAverage(float alpha) { + this(alpha, 0.0f); + } + + ExponentialMovingAverage(float alpha, float initialAverage) { + this.mAlpha = alpha; + this.mInitialAverage = initialAverage; + this.mMovingAverage = initialAverage; + } + + void updateMovingAverage(float newValue) { + mMovingAverage = newValue + mAlpha * (mMovingAverage - newValue); + } + + void reset() { + mMovingAverage = this.mInitialAverage; + } + } +} diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index b8e0156fb4cc..f49e2f1631b9 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -119,6 +119,7 @@ public class Notifier { private final AppOpsManager mAppOps; private final SuspendBlocker mSuspendBlocker; private final WindowManagerPolicy mPolicy; + private final FaceDownDetector mFaceDownDetector; private final ActivityManagerInternal mActivityManagerInternal; private final InputManagerInternal mInputManagerInternal; private final InputMethodManagerInternal mInputMethodManagerInternal; @@ -165,12 +166,14 @@ public class Notifier { private boolean mUserActivityPending; public Notifier(Looper looper, Context context, IBatteryStats batteryStats, - SuspendBlocker suspendBlocker, WindowManagerPolicy policy) { + SuspendBlocker suspendBlocker, WindowManagerPolicy policy, + FaceDownDetector faceDownDetector) { mContext = context; mBatteryStats = batteryStats; mAppOps = mContext.getSystemService(AppOpsManager.class); mSuspendBlocker = suspendBlocker; mPolicy = policy; + mFaceDownDetector = faceDownDetector; mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class); @@ -654,6 +657,7 @@ public class Notifier { TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); tm.notifyUserActivity(); mPolicy.userActivity(); + mFaceDownDetector.userActivity(); } void postEnhancedDischargePredictionBroadcast(long delayMs) { diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index c0b820293649..bc117094dd68 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -16,8 +16,13 @@ package com.android.server.power; +import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.policyToString; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT; +import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF; +import static android.os.PowerManager.GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED; +import static android.os.PowerManager.WAKE_REASON_DISPLAY_GROUP_ADDED; +import static android.os.PowerManager.WAKE_REASON_DISPLAY_GROUP_TURNED_ON; import static android.os.PowerManagerInternal.MODE_DEVICE_IDLE; import static android.os.PowerManagerInternal.MODE_DISPLAY_INACTIVE; import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; @@ -42,7 +47,6 @@ import android.database.ContentObserver; import android.hardware.SensorManager; import android.hardware.SystemSensorManager; import android.hardware.display.AmbientDisplayConfiguration; -import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.hardware.power.Boost; @@ -176,6 +180,10 @@ public final class PowerManagerService extends SystemService private static final int DIRTY_VR_MODE_CHANGED = 1 << 13; // Dirty bit: attentive timer may have timed out private static final int DIRTY_ATTENTIVE = 1 << 14; + // Dirty bit: phone flipped to face down + private static final int DIRTY_FACE_DOWN = 1 << 15; + // Dirty bit: display group power state has changed + private static final int DIRTY_DISPLAY_GROUP_POWER_UPDATED = 1 << 16; // Summarizes the state of all active wakelocks. private static final int WAKE_LOCK_CPU = 1 << 0; @@ -263,6 +271,7 @@ public final class PowerManagerService extends SystemService private final BatterySaverStateMachine mBatterySaverStateMachine; private final BatterySavingStats mBatterySavingStats; private final AttentionDetector mAttentionDetector; + private final FaceDownDetector mFaceDownDetector; private final BinderService mBinderService; private final LocalService mLocalService; private final NativeWrapper mNativeWrapper; @@ -297,10 +306,6 @@ public final class PowerManagerService extends SystemService private int mWakefulnessRaw; private boolean mWakefulnessChanging; - // True if the sandman has just been summoned for the first time since entering the - // dreaming or dozing state. Indicates whether a new dream should begin. - private boolean mSandmanSummoned; - // True if MSG_SANDMAN has been scheduled. private boolean mSandmanScheduled; @@ -351,11 +356,7 @@ public final class PowerManagerService extends SystemService // Manages the desired power state of displays. The actual state may lag behind the // requested because it is updated asynchronously by the display power controller. - private DisplayPowerRequestMapper mDisplayPowerRequestMapper; - - // True if the display power state has been fully applied, which means the display - // is actually on or actually off or whatever was requested. - private boolean mDisplayReady; + private DisplayGroupPowerStateMapper mDisplayGroupPowerStateMapper; // The suspend blocker used to keep the CPU alive when an application has acquired // a wake lock. @@ -547,6 +548,10 @@ public final class PowerManagerService extends SystemService public final float mScreenBrightnessMaximumVr; public final float mScreenBrightnessDefaultVr; + // Value we store for tracking face down behavior. + private boolean mIsFaceDown = false; + private long mLastFlipTime = 0L; + // The screen brightness mode. // One of the Settings.System.SCREEN_BRIGHTNESS_MODE_* constants. private int mScreenBrightnessModeSetting; @@ -625,6 +630,39 @@ public final class PowerManagerService extends SystemService // but the DreamService has not yet been told to start (it's an async process). private boolean mDozeStartInProgress; + private final class DisplayGroupPowerChangeListener implements + DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener { + @Override + public void onDisplayGroupEventLocked(int event, int groupId) { + final int oldWakefulness = getWakefulnessLocked(); + final int newWakefulness = mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(); + if (oldWakefulness != newWakefulness) { + final int reason; + switch (newWakefulness) { + case WAKEFULNESS_AWAKE: + reason = event == DISPLAY_GROUP_ADDED ? WAKE_REASON_DISPLAY_GROUP_ADDED + : WAKE_REASON_DISPLAY_GROUP_TURNED_ON; + break; + case WAKEFULNESS_DOZING: + reason = event == DISPLAY_GROUP_REMOVED + ? GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED + : GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF; + break; + default: + reason = 0; + } + + setGlobalWakefulnessLocked( + mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(), + mClock.uptimeMillis(), reason, Process.SYSTEM_UID, Process.SYSTEM_UID, + mContext.getOpPackageName(), "groupId: " + groupId); + } + + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; + updatePowerStateLocked(); + } + } + private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver { @Override public void onUserSwitching(@UserIdInt int newUserId) throws RemoteException { @@ -791,8 +829,10 @@ public final class PowerManagerService extends SystemService @VisibleForTesting static class Injector { Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, - SuspendBlocker suspendBlocker, WindowManagerPolicy policy) { - return new Notifier(looper, context, batteryStats, suspendBlocker, policy); + SuspendBlocker suspendBlocker, WindowManagerPolicy policy, + FaceDownDetector faceDownDetector) { + return new Notifier( + looper, context, batteryStats, suspendBlocker, policy, faceDownDetector); } SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) { @@ -868,6 +908,12 @@ public final class PowerManagerService extends SystemService void invalidateIsInteractiveCaches() { PowerManager.invalidateIsInteractiveCaches(); } + + DisplayGroupPowerStateMapper createDisplayPowerRequestMapper(Object lock, + DisplayManagerInternal displayManagerInternal, + DisplayGroupPowerStateMapper.DisplayGroupPowerChangeListener listener) { + return new DisplayGroupPowerStateMapper(lock, displayManagerInternal, listener); + } } final Constants mConstants; @@ -906,6 +952,7 @@ public final class PowerManagerService extends SystemService mAmbientDisplaySuppressionController = mInjector.createAmbientDisplaySuppressionController(context); mAttentionDetector = new AttentionDetector(this::onUserAttention, mLock); + mFaceDownDetector = new FaceDownDetector(this::onFlip); mBatterySavingStats = new BatterySavingStats(mLock); mBatterySaverPolicy = @@ -1011,6 +1058,26 @@ public final class PowerManagerService extends SystemService } } + private void onFlip(boolean isFaceDown) { + long millisUntilNormalTimeout = 0; + synchronized (mLock) { + mIsFaceDown = isFaceDown; + if (isFaceDown) { + final long currentTime = mClock.uptimeMillis(); + mLastFlipTime = currentTime; + final long sleepTimeout = getSleepTimeoutLocked(-1L); + final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, -1L); + millisUntilNormalTimeout = + mLastUserActivityTime + screenOffTimeout - mClock.uptimeMillis(); + mDirty |= DIRTY_FACE_DOWN; + updatePowerStateLocked(); + } + } + if (isFaceDown) { + mFaceDownDetector.setMillisSaved(millisUntilNormalTimeout); + } + } + @Override public void onStart() { publishBinderService(Context.POWER_SERVICE, mBinderService, /* allowIsolated= */ false, @@ -1038,7 +1105,8 @@ public final class PowerManagerService extends SystemService updatePowerStateLocked(); if (sQuiescent) { - goToSleepNoUpdateLocked(mClock.uptimeMillis(), + sleepDisplayGroupNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, + mClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_QUIESCENT, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); } @@ -1055,8 +1123,8 @@ public final class PowerManagerService extends SystemService mPolicy = getLocalService(WindowManagerPolicy.class); mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class); mAttentionDetector.systemReady(mContext); - mDisplayPowerRequestMapper = new DisplayPowerRequestMapper(mContext.getSystemService( - DisplayManager.class), mDisplayManagerInternal, mHandler); + mDisplayGroupPowerStateMapper = mInjector.createDisplayPowerRequestMapper(mLock, + mDisplayManagerInternal, new DisplayGroupPowerChangeListener()); SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper()); @@ -1065,7 +1133,7 @@ public final class PowerManagerService extends SystemService mBatteryStats = BatteryStatsService.getService(); mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats, mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"), - mPolicy); + mPolicy, mFaceDownDetector); mWirelessChargerDetector = mInjector.createWirelessChargerDetector(sensorManager, mInjector.createSuspendBlocker( @@ -1099,6 +1167,7 @@ public final class PowerManagerService extends SystemService mBatterySaverController.systemReady(); mBatterySaverPolicy.systemReady(); + mFaceDownDetector.systemReady(mContext); // Register for settings changes. resolver.registerContentObserver(Settings.Secure.getUriFor( @@ -1373,9 +1442,11 @@ public final class PowerManagerService extends SystemService opPackageName = wakeLock.mPackageName; opUid = wakeLock.mOwnerUid; } - wakeUpNoUpdateLocked(mClock.uptimeMillis(), - PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag, - opUid, opPackageName, opUid); + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + wakeDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(), + PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag, + opUid, opPackageName, opUid); + } } } @@ -1574,7 +1645,7 @@ public final class PowerManagerService extends SystemService } // Called from native code. - private void userActivityFromNative(long eventTime, int event, int flags) { + private void userActivityFromNative(long eventTime, int event, int displayId, int flags) { userActivityInternal(eventTime, event, flags, Process.SYSTEM_UID); } @@ -1664,26 +1735,29 @@ public final class PowerManagerService extends SystemService } } - private void wakeUpInternal(long eventTime, @WakeReason int reason, String details, int uid, - String opPackageName, int opUid) { + private void wakeDisplayGroup(int groupId, long eventTime, @WakeReason int reason, + String details, int uid, String opPackageName, int opUid) { synchronized (mLock) { - if (wakeUpNoUpdateLocked(eventTime, reason, details, uid, opPackageName, opUid)) { + if (wakeDisplayGroupNoUpdateLocked(groupId, eventTime, reason, details, uid, + opPackageName, opUid)) { updatePowerStateLocked(); } } } - private boolean wakeUpNoUpdateLocked(long eventTime, @WakeReason int reason, String details, - int reasonUid, String opPackageName, int opUid) { + private boolean wakeDisplayGroupNoUpdateLocked(int groupId, long eventTime, + @WakeReason int reason, String details, int uid, String opPackageName, int opUid) { if (DEBUG_SPEW) { - Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid); + Slog.d(TAG, "wakeDisplayGroupNoUpdateLocked: eventTime=" + eventTime + + ", groupId=" + groupId + ", uid=" + uid); } if (eventTime < mLastSleepTime || mForceSuspendActive || !mSystemReady) { return false; } - if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) { + final int currentState = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); + if (currentState == WAKEFULNESS_AWAKE) { if (!mBootCompleted && sQuiescent) { mDirty |= DIRTY_QUIESCENT; return true; @@ -1691,113 +1765,90 @@ public final class PowerManagerService extends SystemService return false; } - Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0); - - Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp"); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOnDisplay"); try { - Slog.i(TAG, "Waking up from " - + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked()) - + " (uid=" + reasonUid + Slog.i(TAG, "Powering on display group from" + + PowerManagerInternal.wakefulnessToString(currentState) + + " (groupId=" + groupId + + ", uid=" + uid + ", reason=" + PowerManager.wakeReasonToString(reason) + ", details=" + details + ")..."); + Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId); - mLastWakeTime = eventTime; - mLastWakeReason = reason; - setWakefulnessLocked(WAKEFULNESS_AWAKE, reason, eventTime); - - mNotifier.onWakeUp(reason, details, reasonUid, opPackageName, opUid); - userActivityNoUpdateLocked( - eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid); - - if (sQuiescent) { - mDirty |= DIRTY_QUIESCENT; - } + setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid, + opPackageName, details); + mDisplayGroupPowerStateMapper.setLastPowerOnTimeLocked(groupId, eventTime); + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } + return true; } - private void goToSleepInternal(long eventTime, int reason, int flags, int uid) { + private void sleepDisplayGroup(int groupId, long eventTime, int reason, int flags, + int uid) { synchronized (mLock) { - if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) { + if (sleepDisplayGroupNoUpdateLocked(groupId, eventTime, reason, flags, uid)) { updatePowerStateLocked(); } } } - /** - * Puts the system in doze. - * - * This method is called goToSleep for historical reasons but actually attempts to DOZE, - * and only tucks itself in to SLEEP if requested with the flag - * {@link PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE}. - */ - @SuppressWarnings("deprecation") - private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) { + private boolean sleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int reason, + int flags, int uid) { if (DEBUG_SPEW) { - Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime - + ", reason=" + reason + ", flags=" + flags + ", uid=" + uid); + Slog.d(TAG, "sleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime + + ", groupId=" + groupId + ", reason=" + reason + ", flags=" + flags + + ", uid=" + uid); } if (eventTime < mLastWakeTime - || getWakefulnessLocked() == WAKEFULNESS_ASLEEP - || getWakefulnessLocked() == WAKEFULNESS_DOZING + || !PowerManagerInternal.isInteractive(getWakefulnessLocked()) || !mSystemReady || !mBootCompleted) { return false; } - Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep"); + final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); + if (!PowerManagerInternal.isInteractive(wakefulness)) { + return false; + } + + Trace.traceBegin(Trace.TRACE_TAG_POWER, "powerOffDisplay"); try { reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX, Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN)); - Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason) - + " (uid " + uid + ")..."); + Slog.i(TAG, "Powering off display group due to " + + PowerManager.sleepReasonToString(reason) + " (groupId= " + groupId + + ", uid= " + uid + ")..."); - mLastSleepTime = eventTime; - mLastSleepReason = reason; - mSandmanSummoned = true; - mDozeStartInProgress = true; - setWakefulnessLocked(WAKEFULNESS_DOZING, reason, eventTime); - - // Report the number of wake locks that will be cleared by going to sleep. - int numWakeLocksCleared = 0; - final int numWakeLocks = mWakeLocks.size(); - for (int i = 0; i < numWakeLocks; i++) { - final WakeLock wakeLock = mWakeLocks.get(i); - switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { - case PowerManager.FULL_WAKE_LOCK: - case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: - case PowerManager.SCREEN_DIM_WAKE_LOCK: - numWakeLocksCleared += 1; - break; - } - } - EventLogTags.writePowerSleepRequested(numWakeLocksCleared); - - // Skip dozing if requested. + mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true); + setWakefulnessLocked(groupId, WAKEFULNESS_DOZING, eventTime, uid, reason, + /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null); if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) { - reallyGoToSleepNoUpdateLocked(eventTime, uid); + reallySleepDisplayGroupNoUpdateLocked(groupId, eventTime, uid); } + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } return true; } - private void napInternal(long eventTime, int uid) { + private void dreamDisplayGroup(int groupId, long eventTime, int uid) { synchronized (mLock) { - if (napNoUpdateLocked(eventTime, uid)) { + if (dreamDisplayGroupNoUpdateLocked(groupId, eventTime, uid)) { updatePowerStateLocked(); } } } - private boolean napNoUpdateLocked(long eventTime, int uid) { + private boolean dreamDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) { if (DEBUG_SPEW) { - Slog.d(TAG, "napNoUpdateLocked: eventTime=" + eventTime + ", uid=" + uid); + Slog.d(TAG, "dreamDisplayGroupNoUpdateLocked: eventTime=" + eventTime + + ", uid=" + uid); } if (eventTime < mLastWakeTime || getWakefulnessLocked() != WAKEFULNESS_AWAKE @@ -1805,36 +1856,42 @@ public final class PowerManagerService extends SystemService return false; } - Trace.traceBegin(Trace.TRACE_TAG_POWER, "nap"); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "napDisplayGroup"); try { - Slog.i(TAG, "Nap time (uid " + uid +")..."); + Slog.i(TAG, "Napping display group (groupId=" + groupId + ", uid=" + uid + ")..."); + + mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, true); + setWakefulnessLocked(groupId, WAKEFULNESS_DREAMING, eventTime, uid, /* reason= */ + 0, /* opUid= */ 0, /* opPackageName= */ null, /* details= */ null); + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; - mSandmanSummoned = true; - setWakefulnessLocked(WAKEFULNESS_DREAMING, 0, eventTime); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } return true; } - // Done dozing, drop everything and go to sleep. - private boolean reallyGoToSleepNoUpdateLocked(long eventTime, int uid) { + private boolean reallySleepDisplayGroupNoUpdateLocked(int groupId, long eventTime, int uid) { if (DEBUG_SPEW) { - Slog.d(TAG, "reallyGoToSleepNoUpdateLocked: eventTime=" + eventTime + Slog.d(TAG, "reallySleepDisplayGroupNoUpdateLocked: eventTime=" + eventTime + ", uid=" + uid); } if (eventTime < mLastWakeTime || getWakefulnessLocked() == WAKEFULNESS_ASLEEP - || !mBootCompleted || !mSystemReady) { + || !mBootCompleted || !mSystemReady + || mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) + == WAKEFULNESS_ASLEEP) { return false; } - Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallyGoToSleep"); + Trace.traceBegin(Trace.TRACE_TAG_POWER, "reallySleepDisplayGroup"); try { - Slog.i(TAG, "Sleeping (uid " + uid +")..."); + Slog.i(TAG, "Sleeping display group (groupId=" + groupId + ", uid=" + uid + ")..."); - setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, - eventTime); + setWakefulnessLocked(groupId, WAKEFULNESS_ASLEEP, eventTime, uid, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, /* opUid= */ 0, + /* opPackageName= */ null, /* details= */ null); + mDirty |= DIRTY_DISPLAY_GROUP_POWER_UPDATED; } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } @@ -1842,8 +1899,62 @@ public final class PowerManagerService extends SystemService } @VisibleForTesting - void setWakefulnessLocked(int wakefulness, int reason, long eventTime) { - if (getWakefulnessLocked() != wakefulness) { + void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason, + int opUid, String opPackageName, String details) { + if (mDisplayGroupPowerStateMapper.setWakefulnessLocked(groupId, wakefulness)) { + setGlobalWakefulnessLocked(mDisplayGroupPowerStateMapper.getGlobalWakefulnessLocked(), + eventTime, reason, uid, opUid, opPackageName, details); + } + } + + private void setGlobalWakefulnessLocked(int wakefulness, long eventTime, int reason, int uid, + int opUid, String opPackageName, String details) { + if (getWakefulnessLocked() == wakefulness) { + return; + } + + // Phase 1: Handle pre-wakefulness change bookkeeping. + final String traceMethodName; + switch (wakefulness) { + case WAKEFULNESS_ASLEEP: + traceMethodName = "reallyGoToSleep"; + Slog.i(TAG, "Sleeping (uid " + uid + ")..."); + break; + + case WAKEFULNESS_AWAKE: + traceMethodName = "wakeUp"; + Slog.i(TAG, "Waking up from " + + PowerManagerInternal.wakefulnessToString(getWakefulnessLocked()) + + " (uid=" + uid + + ", reason=" + PowerManager.wakeReasonToString(reason) + + ", details=" + details + + ")..."); + mLastWakeTime = eventTime; + mLastWakeReason = reason; + break; + + case WAKEFULNESS_DREAMING: + traceMethodName = "nap"; + Slog.i(TAG, "Nap time (uid " + uid + ")..."); + break; + + case WAKEFULNESS_DOZING: + traceMethodName = "goToSleep"; + Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason) + + " (uid " + uid + ")..."); + + mLastSleepTime = eventTime; + mLastSleepReason = reason; + mDozeStartInProgress = true; + break; + + default: + throw new IllegalArgumentException("Unexpected wakefulness: " + wakefulness); + } + + Trace.traceBegin(Trace.TRACE_TAG_POWER, traceMethodName); + try { + // Phase 2: Handle wakefulness change and bookkeeping. // Under lock, invalidate before set ensures caches won't return stale values. mInjector.invalidateIsInteractiveCaches(); mWakefulnessRaw = wakefulness; @@ -1857,6 +1968,37 @@ public final class PowerManagerService extends SystemService mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime); } mAttentionDetector.onWakefulnessChangeStarted(wakefulness); + + // Phase 3: Handle post-wakefulness change bookkeeping. + switch (wakefulness) { + case WAKEFULNESS_AWAKE: + mNotifier.onWakeUp(reason, details, uid, opPackageName, opUid); + userActivityNoUpdateLocked( + eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid); + if (sQuiescent) { + mDirty |= DIRTY_QUIESCENT; + } + break; + + case WAKEFULNESS_DOZING: + // Report the number of wake locks that will be cleared by going to sleep. + int numWakeLocksCleared = 0; + final int numWakeLocks = mWakeLocks.size(); + for (int i = 0; i < numWakeLocks; i++) { + final WakeLock wakeLock = mWakeLocks.get(i); + switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { + case PowerManager.FULL_WAKE_LOCK: + case PowerManager.SCREEN_BRIGHT_WAKE_LOCK: + case PowerManager.SCREEN_DIM_WAKE_LOCK: + numWakeLocksCleared += 1; + break; + } + } + EventLogTags.writePowerSleepRequested(numWakeLocksCleared); + break; + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); } } @@ -1879,7 +2021,7 @@ public final class PowerManagerService extends SystemService } private void finishWakefulnessChangeIfNeededLocked() { - if (mWakefulnessChanging && mDisplayReady) { + if (mWakefulnessChanging && mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { if (getWakefulnessLocked() == WAKEFULNESS_DOZING && (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) { return; // wait until dream has enabled dozing @@ -1891,13 +2033,6 @@ public final class PowerManagerService extends SystemService || getWakefulnessLocked() == WAKEFULNESS_ASLEEP) { logSleepTimeoutRecapturedLocked(); } - if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) { - Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0); - final int latencyMs = (int) (mClock.uptimeMillis() - mLastWakeTime); - if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) { - Slog.w(TAG, "Screen on took " + latencyMs + " ms"); - } - } mWakefulnessChanging = false; mNotifier.onWakefulnessChangeFinished(); } @@ -1996,7 +2131,6 @@ public final class PowerManagerService extends SystemService if ((dirty & DIRTY_BATTERY_STATE) != 0) { final boolean wasPowered = mIsPowered; final int oldPlugType = mPlugType; - final boolean oldLevelLow = mBatteryLevelLow; mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); mPlugType = mBatteryManagerInternal.getPlugType(); mBatteryLevel = mBatteryManagerInternal.getBatteryLevel(); @@ -2025,7 +2159,8 @@ public final class PowerManagerService extends SystemService final long now = mClock.uptimeMillis(); if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType, dockedOnWirelessCharger)) { - wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN, + wakeDisplayGroupNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, now, + PowerManager.WAKE_REASON_PLUGGED_IN, "android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); } @@ -2283,9 +2418,11 @@ public final class PowerManagerService extends SystemService || getWakefulnessLocked() == WAKEFULNESS_DOZING) { final long attentiveTimeout = getAttentiveTimeoutLocked(); final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout); - final long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, + long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, attentiveTimeout); final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout); + screenOffTimeout = + getScreenOffTimeoutWithFaceDownLocked(screenOffTimeout, screenDimDuration); final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager; final long nextProfileTimeout = getNextProfileTimeoutLocked(now); @@ -2307,7 +2444,8 @@ public final class PowerManagerService extends SystemService nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout; if (now < nextTimeout) { final DisplayPowerRequest displayPowerRequest = - mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); + mDisplayGroupPowerStateMapper.getPowerRequestLocked( + Display.DEFAULT_DISPLAY_GROUP); if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_BRIGHT || displayPowerRequest.policy == DisplayPowerRequest.POLICY_VR) { mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT; @@ -2541,6 +2679,16 @@ public final class PowerManagerService extends SystemService (long)(screenOffTimeout * mMaximumScreenDimRatioConfig)); } + private long getScreenOffTimeoutWithFaceDownLocked( + long screenOffTimeout, long screenDimDuration) { + // If face down, we decrease the timeout to equal the dim duration so that the + // device will go into a dim state. + if (mIsFaceDown) { + return Math.min(screenDimDuration, screenOffTimeout); + } + return screenOffTimeout; + } + /** * Updates the wakefulness of the device. * @@ -2562,13 +2710,23 @@ public final class PowerManagerService extends SystemService } final long time = mClock.uptimeMillis(); if (isAttentiveTimeoutExpired(time)) { - changed = goToSleepNoUpdateLocked(time, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, - PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); + // TODO (b/175764389): Support per-display timeouts. + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + changed = sleepDisplayGroupNoUpdateLocked(id, time, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); + } } else if (shouldNapAtBedTimeLocked()) { - changed = napNoUpdateLocked(time, Process.SYSTEM_UID); + // TODO (b/175764389): Support per-display timeouts. + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + changed = dreamDisplayGroupNoUpdateLocked(id, time, Process.SYSTEM_UID); + } } else { - changed = goToSleepNoUpdateLocked(time, - PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID); + // TODO (b/175764389): Support per-display timeouts. + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + changed = sleepDisplayGroupNoUpdateLocked(id, time, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID); + } } } } @@ -2643,6 +2801,7 @@ public final class PowerManagerService extends SystemService private void updateDreamLocked(int dirty, boolean displayBecameReady) { if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_USER_ACTIVITY + | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_ATTENTIVE | DIRTY_WAKE_LOCKS | DIRTY_BOOT_COMPLETED @@ -2651,7 +2810,7 @@ public final class PowerManagerService extends SystemService | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE | DIRTY_BATTERY_STATE)) != 0 || displayBecameReady) { - if (mDisplayReady) { + if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { scheduleSandmanLocked(); } } @@ -2666,6 +2825,14 @@ public final class PowerManagerService extends SystemService } } + private void handleSandman() { + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + if (mDisplayGroupPowerStateMapper.isSandmanSupported(id)) { + handleSandman(id); + } + } + } + /** * Called when the device enters or exits a dreaming or dozing state. * @@ -2673,16 +2840,19 @@ public final class PowerManagerService extends SystemService * the dream and we don't want to hold our lock while doing so. There is a risk that * the device will wake or go to sleep in the meantime so we have to handle that case. */ - private void handleSandman() { // runs on handler thread + private void handleSandman(int groupId) { // runs on handler thread // Handle preconditions. final boolean startDreaming; final int wakefulness; synchronized (mLock) { mSandmanScheduled = false; + // TODO (b/175764708): Support per-display doze. wakefulness = getWakefulnessLocked(); - if (mSandmanSummoned && mDisplayReady) { - startDreaming = canDreamLocked() || canDozeLocked(); - mSandmanSummoned = false; + if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) && + mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId) + && mDisplayGroupPowerStateMapper.isReady(groupId)) { + startDreaming = canDreamLocked(groupId) || canDozeLocked(); + mDisplayGroupPowerStateMapper.setSandmanSummoned(groupId, false); } else { startDreaming = false; } @@ -2721,14 +2891,15 @@ public final class PowerManagerService extends SystemService // If preconditions changed, wait for the next iteration to determine // whether the dream should continue (or be restarted). - if (mSandmanSummoned || getWakefulnessLocked() != wakefulness) { + if (mDisplayGroupPowerStateMapper.isSandmanSummoned(groupId) + || mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId) != wakefulness) { return; // wait for next cycle } // Determine whether the dream should continue. long now = mClock.uptimeMillis(); if (wakefulness == WAKEFULNESS_DREAMING) { - if (isDreaming && canDreamLocked()) { + if (isDreaming && canDreamLocked(groupId)) { if (mDreamsBatteryLevelDrainCutoffConfig >= 0 && mBatteryLevel < mBatteryLevelWhenDreamStarted - mDreamsBatteryLevelDrainCutoffConfig @@ -2748,16 +2919,13 @@ public final class PowerManagerService extends SystemService // Dream has ended or will be stopped. Update the power state. if (isItBedTimeYetLocked()) { - int flags = 0; - if (isAttentiveTimeoutExpired(now)) { - flags |= PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE; - } - goToSleepNoUpdateLocked(now, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, - Process.SYSTEM_UID); + final int flags = isAttentiveTimeoutExpired(now) + ? PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE : 0; + sleepDisplayGroupNoUpdateLocked(groupId, now, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, flags, Process.SYSTEM_UID); updatePowerStateLocked(); } else { - wakeUpNoUpdateLocked(now, - PowerManager.WAKE_REASON_UNKNOWN, + wakeDisplayGroupNoUpdateLocked(groupId, now, PowerManager.WAKE_REASON_UNKNOWN, "android.server.power:DREAM_FINISHED", Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); updatePowerStateLocked(); @@ -2768,7 +2936,7 @@ public final class PowerManagerService extends SystemService } // Doze has ended or will be stopped. Update the power state. - reallyGoToSleepNoUpdateLocked(now, Process.SYSTEM_UID); + reallySleepDisplayGroupNoUpdateLocked(groupId, now, Process.SYSTEM_UID); updatePowerStateLocked(); } } @@ -2780,11 +2948,11 @@ public final class PowerManagerService extends SystemService } /** - * Returns true if the device is allowed to dream in its current state. + * Returns true if the {@code groupId} is allowed to dream in its current state. */ - private boolean canDreamLocked() { + private boolean canDreamLocked(int groupId) { final DisplayPowerRequest displayPowerRequest = - mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); + mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId); if (getWakefulnessLocked() != WAKEFULNESS_DREAMING || !mDreamsSupportedConfig || !mDreamsEnabledSetting @@ -2822,95 +2990,117 @@ public final class PowerManagerService extends SystemService /** * Updates the display power state asynchronously. - * When the update is finished, mDisplayReady will be set to true. The display - * controller posts a message to tell us when the actual display power state + * When the update is finished, the ready state of the displays will be updated. The display + * controllers post a message to tell us when the actual display power state * has been updated so we come back here to double-check and finish up. * * This function recalculates the display power state each time. * - * @return true if the display became ready. + * @return {@code true} if all displays became ready; {@code false} otherwise */ private boolean updateDisplayPowerStateLocked(int dirty) { - final boolean oldDisplayReady = mDisplayReady; + final boolean oldDisplayReady = mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked(); if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED | - DIRTY_QUIESCENT)) != 0) { + DIRTY_QUIESCENT | DIRTY_DISPLAY_GROUP_POWER_UPDATED)) != 0) { if ((dirty & DIRTY_QUIESCENT) != 0) { - if (mDisplayReady) { + if (mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { sQuiescent = false; } else { mDirty |= DIRTY_QUIESCENT; } } - final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( - Display.DEFAULT_DISPLAY); - displayPowerRequest.policy = getDesiredScreenPolicyLocked(); - - // Determine appropriate screen brightness and auto-brightness adjustments. - final boolean autoBrightness; - final float screenBrightnessOverride; - if (!mBootCompleted) { - // Keep the brightness steady during boot. This requires the - // bootloader brightness and the default brightness to be identical. - autoBrightness = false; - screenBrightnessOverride = mScreenBrightnessDefault; - } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { - autoBrightness = false; - screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager; - } else { - autoBrightness = (mScreenBrightnessModeSetting == - Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT; - } + for (final int groupId : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + final DisplayPowerRequest displayPowerRequest = + mDisplayGroupPowerStateMapper.getPowerRequestLocked(groupId); + displayPowerRequest.policy = getDesiredScreenPolicyLocked(groupId); + + // Determine appropriate screen brightness and auto-brightness adjustments. + final boolean autoBrightness; + final float screenBrightnessOverride; + if (!mBootCompleted) { + // Keep the brightness steady during boot. This requires the + // bootloader brightness and the default brightness to be identical. + autoBrightness = false; + screenBrightnessOverride = mScreenBrightnessDefault; + } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { + autoBrightness = false; + screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager; + } else { + autoBrightness = (mScreenBrightnessModeSetting + == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + screenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT; + } - // Update display power request. - displayPowerRequest.screenBrightnessOverride = screenBrightnessOverride; - displayPowerRequest.useAutoBrightness = autoBrightness; - displayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); - displayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness(); + // Update display power request. + displayPowerRequest.screenBrightnessOverride = screenBrightnessOverride; + displayPowerRequest.useAutoBrightness = autoBrightness; + displayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); + displayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness(); - updatePowerRequestFromBatterySaverPolicy(displayPowerRequest); + updatePowerRequestFromBatterySaverPolicy(displayPowerRequest); - if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) { - displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager; - if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0 - && !mDrawWakeLockOverrideFromSidekick) { - if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) { - displayPowerRequest.dozeScreenState = Display.STATE_DOZE; - } - if (displayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) { - displayPowerRequest.dozeScreenState = Display.STATE_ON; + if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) { + displayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager; + if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0 + && !mDrawWakeLockOverrideFromSidekick) { + if (displayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) { + displayPowerRequest.dozeScreenState = Display.STATE_DOZE; + } + if (displayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) { + displayPowerRequest.dozeScreenState = Display.STATE_ON; + } } + displayPowerRequest.dozeScreenBrightness = + mDozeScreenBrightnessOverrideFromDreamManagerFloat; + } else { + displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN; + displayPowerRequest.dozeScreenBrightness = + PowerManager.BRIGHTNESS_INVALID_FLOAT; } - displayPowerRequest.dozeScreenBrightness = - mDozeScreenBrightnessOverrideFromDreamManagerFloat; - } else { - displayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN; - displayPowerRequest.dozeScreenBrightness = - PowerManager.BRIGHTNESS_INVALID_FLOAT; - } - mDisplayReady = mDisplayManagerInternal.requestPowerState(Display.DEFAULT_DISPLAY_GROUP, - displayPowerRequest, mRequestWaitForNegativeProximity); - mRequestWaitForNegativeProximity = false; + final boolean ready = mDisplayManagerInternal.requestPowerState(groupId, + displayPowerRequest, mRequestWaitForNegativeProximity); - if (DEBUG_SPEW) { - Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady - + ", policy=" + displayPowerRequest.policy - + ", mWakefulness=" + getWakefulnessLocked() - + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary) - + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary) - + ", mBootCompleted=" + mBootCompleted - + ", screenBrightnessOverride=" + screenBrightnessOverride - + ", useAutoBrightness=" + autoBrightness - + ", mScreenBrightnessBoostInProgress=" + mScreenBrightnessBoostInProgress - + ", mIsVrModeEnabled= " + mIsVrModeEnabled - + ", sQuiescent=" + sQuiescent); + if (DEBUG_SPEW) { + Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready + + ", groupId=" + groupId + + ", policy=" + policyToString(displayPowerRequest.policy) + + ", mWakefulness=" + + PowerManagerInternal.wakefulnessToString( + mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId)) + + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary) + + ", mUserActivitySummary=0x" + Integer.toHexString( + mUserActivitySummary) + + ", mBootCompleted=" + mBootCompleted + + ", screenBrightnessOverride=" + + displayPowerRequest.screenBrightnessOverride + + ", useAutoBrightness=" + displayPowerRequest.useAutoBrightness + + ", mScreenBrightnessBoostInProgress=" + + mScreenBrightnessBoostInProgress + + ", mIsVrModeEnabled= " + mIsVrModeEnabled + + ", sQuiescent=" + sQuiescent); + } + + final boolean displayReadyStateChanged = + mDisplayGroupPowerStateMapper.setDisplayGroupReadyLocked(groupId, ready); + if (ready && displayReadyStateChanged + && mDisplayGroupPowerStateMapper.getWakefulnessLocked( + groupId) == WAKEFULNESS_AWAKE) { + Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId); + final int latencyMs = (int) (mClock.uptimeMillis() + - mDisplayGroupPowerStateMapper.getLastPowerOnTimeLocked(groupId)); + if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) { + Slog.w(TAG, "Screen on took " + latencyMs + " ms"); + } + } } + mRequestWaitForNegativeProximity = false; } - return mDisplayReady && !oldDisplayReady; + + return mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked() && !oldDisplayReady; } private void updateScreenBrightnessBoostLocked(int dirty) { @@ -2944,12 +3134,11 @@ public final class PowerManagerService extends SystemService } @VisibleForTesting - int getDesiredScreenPolicyLocked() { - if (getWakefulnessLocked() == WAKEFULNESS_ASLEEP || sQuiescent) { + int getDesiredScreenPolicyLocked(int groupId) { + final int wakefulness = mDisplayGroupPowerStateMapper.getWakefulnessLocked(groupId); + if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) { return DisplayPowerRequest.POLICY_OFF; - } - - if (getWakefulnessLocked() == WAKEFULNESS_DOZING) { + } else if (wakefulness == WAKEFULNESS_DOZING) { if ((mWakeLockSummary & WAKE_LOCK_DOZE) != 0) { return DisplayPowerRequest.POLICY_DOZE; } @@ -2979,7 +3168,6 @@ public final class PowerManagerService extends SystemService private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks = new DisplayManagerInternal.DisplayPowerCallbacks() { - private int mDisplayState = Display.STATE_UNKNOWN; @Override public void onStateChanged() { @@ -3010,29 +3198,25 @@ public final class PowerManagerService extends SystemService } @Override - public void onDisplayStateChange(int state) { + public void onDisplayStateChange(boolean allInactive, boolean allOff) { // This method is only needed to support legacy display blanking behavior // where the display's power state is coupled to suspend or to the power HAL. // The order of operations matters here. synchronized (mLock) { - if (mDisplayState != state) { - mDisplayState = state; - setPowerModeInternal(MODE_DISPLAY_INACTIVE, - !Display.isActiveState(state)); - if (state == Display.STATE_OFF) { - if (!mDecoupleHalInteractiveModeFromDisplayConfig) { - setHalInteractiveModeLocked(false); - } - if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) { - setHalAutoSuspendModeLocked(true); - } - } else { - if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) { - setHalAutoSuspendModeLocked(false); - } - if (!mDecoupleHalInteractiveModeFromDisplayConfig) { - setHalInteractiveModeLocked(true); - } + setPowerModeInternal(MODE_DISPLAY_INACTIVE, allInactive); + if (allOff) { + if (!mDecoupleHalInteractiveModeFromDisplayConfig) { + setHalInteractiveModeLocked(false); + } + if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) { + setHalAutoSuspendModeLocked(true); + } + } else { + if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) { + setHalAutoSuspendModeLocked(false); + } + if (!mDecoupleHalInteractiveModeFromDisplayConfig) { + setHalInteractiveModeLocked(true); } } } @@ -3047,13 +3231,6 @@ public final class PowerManagerService extends SystemService public void releaseSuspendBlocker() { mDisplaySuspendBlocker.release(); } - - @Override - public String toString() { - synchronized (this) { - return "state=" + Display.stateToString(mDisplayState); - } - } }; private boolean shouldUseProximitySensorLocked() { @@ -3069,9 +3246,11 @@ public final class PowerManagerService extends SystemService final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0); final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked(); final boolean autoSuspend = !needDisplaySuspendBlocker; - final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( - Display.DEFAULT_DISPLAY); - final boolean interactive = displayPowerRequest.isBrightOrDim(); + final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked(); + boolean interactive = false; + for (int id : groupIds) { + interactive |= mDisplayGroupPowerStateMapper.getPowerRequestLocked(id).isBrightOrDim(); + } // Disable auto-suspend if needed. // FIXME We should consider just leaving auto-suspend enabled forever since @@ -3101,7 +3280,7 @@ public final class PowerManagerService extends SystemService // until the display is actually ready so that all transitions have // completed. This is probably a good sign that things have gotten // too tangled over here... - if (interactive || mDisplayReady) { + if (interactive || mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { setHalInteractiveModeLocked(interactive); } } @@ -3127,29 +3306,10 @@ public final class PowerManagerService extends SystemService * We do so if the screen is on or is in transition between states. */ private boolean needDisplaySuspendBlockerLocked() { - if (!mDisplayReady) { + if (!mDisplayGroupPowerStateMapper.areAllDisplaysReadyLocked()) { return true; } - final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( - Display.DEFAULT_DISPLAY); - if (displayPowerRequest.isBrightOrDim()) { - // If we asked for the screen to be on but it is off due to the proximity - // sensor then we may suspend but only if the configuration allows it. - // On some hardware it may not be safe to suspend because the proximity - // sensor may not be correctly configured as a wake-up source. - if (!displayPowerRequest.useProximitySensor || !mProximityPositive - || !mSuspendWhenScreenOffDueToProximityConfig) { - return true; - } - } - if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE - && displayPowerRequest.dozeScreenState == Display.STATE_ON) { - // Although we are in DOZE and would normally allow the device to suspend, - // the doze service has explicitly requested the display to remain in the ON - // state which means we should hold the display suspend blocker. - return true; - } if (mScreenBrightnessBoostInProgress) { return true; } @@ -3163,6 +3323,30 @@ public final class PowerManagerService extends SystemService return true; } + final int[] groupIds = mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked(); + for (int id : groupIds) { + final DisplayPowerRequest displayPowerRequest = + mDisplayGroupPowerStateMapper.getPowerRequestLocked(id); + if (displayPowerRequest.isBrightOrDim()) { + // If we asked for the screen to be on but it is off due to the proximity + // sensor then we may suspend but only if the configuration allows it. + // On some hardware it may not be safe to suspend because the proximity + // sensor may not be correctly configured as a wake-up source. + if (!displayPowerRequest.useProximitySensor || !mProximityPositive + || !mSuspendWhenScreenOffDueToProximityConfig) { + return true; + } + } + + if (displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE + && displayPowerRequest.dozeScreenState == Display.STATE_ON) { + // Although we are in DOZE and would normally allow the device to suspend, + // the doze service has explicitly requested the display to remain in the ON + // state which means we should hold the display suspend blocker. + return true; + } + } + // Let the system suspend if the screen is off or dozing. return false; } @@ -3696,9 +3880,15 @@ public final class PowerManagerService extends SystemService synchronized (mLock) { mForceSuspendActive = true; // Place the system in an non-interactive state - goToSleepInternal(mClock.uptimeMillis(), - PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND, - PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid); + boolean updatePowerState = false; + for (int id : mDisplayGroupPowerStateMapper.getDisplayGroupIdsLocked()) { + updatePowerState |= sleepDisplayGroupNoUpdateLocked(id, mClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid); + } + if (updatePowerState) { + updatePowerStateLocked(); + } // Disable all the partial wake locks as well updateWakeLockDisabledStatesLocked(); @@ -3838,7 +4028,6 @@ public final class PowerManagerService extends SystemService pw.println(" mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)); pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity); pw.println(" mSandmanScheduled=" + mSandmanScheduled); - pw.println(" mSandmanSummoned=" + mSandmanSummoned); pw.println(" mBatteryLevelLow=" + mBatteryLevelLow); pw.println(" mLightDeviceIdleMode=" + mLightDeviceIdleMode); pw.println(" mDeviceIdleMode=" + mDeviceIdleMode); @@ -3856,9 +4045,10 @@ public final class PowerManagerService extends SystemService + TimeUtils.formatUptime(mLastScreenBrightnessBoostTime)); pw.println(" mScreenBrightnessBoostInProgress=" + mScreenBrightnessBoostInProgress); - pw.println(" mDisplayReady=" + mDisplayReady); pw.println(" mHoldingWakeLockSuspendBlocker=" + mHoldingWakeLockSuspendBlocker); pw.println(" mHoldingDisplaySuspendBlocker=" + mHoldingDisplaySuspendBlocker); + pw.println(" mLastFlipTime=" + mLastFlipTime); + pw.println(" mIsFaceDown=" + mIsFaceDown); pw.println(); pw.println("Settings and Configuration:"); @@ -4003,6 +4193,8 @@ public final class PowerManagerService extends SystemService mNotifier.dump(pw); } + mFaceDownDetector.dump(pw); + mAmbientDisplaySuppressionController.dump(pw); } @@ -4091,7 +4283,6 @@ public final class PowerManagerService extends SystemService PowerManagerServiceDumpProto.IS_REQUEST_WAIT_FOR_NEGATIVE_PROXIMITY, mRequestWaitForNegativeProximity); proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SCHEDULED, mSandmanScheduled); - proto.write(PowerManagerServiceDumpProto.IS_SANDMAN_SUMMONED, mSandmanSummoned); proto.write(PowerManagerServiceDumpProto.IS_BATTERY_LEVEL_LOW, mBatteryLevelLow); proto.write(PowerManagerServiceDumpProto.IS_LIGHT_DEVICE_IDLE_MODE, mLightDeviceIdleMode); proto.write(PowerManagerServiceDumpProto.IS_DEVICE_IDLE_MODE, mDeviceIdleMode); @@ -4118,7 +4309,6 @@ public final class PowerManagerService extends SystemService proto.write( PowerManagerServiceDumpProto.IS_SCREEN_BRIGHTNESS_BOOST_IN_PROGRESS, mScreenBrightnessBoostInProgress); - proto.write(PowerManagerServiceDumpProto.IS_DISPLAY_READY, mDisplayReady); proto.write( PowerManagerServiceDumpProto.IS_HOLDING_WAKE_LOCK_SUSPEND_BLOCKER, mHoldingWakeLockSuspendBlocker); @@ -4932,7 +5122,8 @@ public final class PowerManagerService extends SystemService final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - wakeUpInternal(eventTime, reason, details, uid, opPackageName, uid); + wakeDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, details, uid, + opPackageName, uid); } finally { Binder.restoreCallingIdentity(ident); } @@ -4950,7 +5141,7 @@ public final class PowerManagerService extends SystemService final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - goToSleepInternal(eventTime, reason, flags, uid); + sleepDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, flags, uid); } finally { Binder.restoreCallingIdentity(ident); } @@ -4968,7 +5159,7 @@ public final class PowerManagerService extends SystemService final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); try { - napInternal(eventTime, uid); + dreamDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, uid); } finally { Binder.restoreCallingIdentity(ident); } @@ -5121,7 +5312,6 @@ public final class PowerManagerService extends SystemService @Override // Binder call public int getPowerSaveModeTrigger() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER, null); final long ident = Binder.clearCallingIdentity(); try { return Settings.Global.getInt(mContext.getContentResolver(), @@ -5631,7 +5821,8 @@ public final class PowerManagerService extends SystemService // also tells us that we're not already ignoring the proximity sensor. final DisplayPowerRequest displayPowerRequest = - mDisplayPowerRequestMapper.get(Display.DEFAULT_DISPLAY); + mDisplayGroupPowerStateMapper.getPowerRequestLocked( + Display.DEFAULT_DISPLAY_GROUP); if (displayPowerRequest.useProximitySensor && mProximityPositive) { mDisplayManagerInternal.ignoreProximitySensorUntilChanged(); return true; diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java index 9a91848475fe..f382d10e0846 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java @@ -16,6 +16,7 @@ package com.android.server.powerstats; +import android.annotation.Nullable; import android.hardware.power.stats.Channel; import android.hardware.power.stats.EnergyMeasurement; import android.hardware.power.stats.IPowerStats; @@ -51,6 +52,7 @@ public final class PowerStatsHALWrapper { * * @return List of information on each PowerEntity. */ + @Nullable android.hardware.power.stats.PowerEntity[] getPowerEntityInfo(); /** @@ -70,6 +72,7 @@ public final class PowerStatsHALWrapper { * * @return StateResidency since boot for each requested PowerEntity */ + @Nullable android.hardware.power.stats.StateResidencyResult[] getStateResidency(int[] powerEntityIds); /** @@ -81,6 +84,7 @@ public final class PowerStatsHALWrapper { * * @return List of EnergyConsumers all available energy consumers. */ + @Nullable android.hardware.power.stats.EnergyConsumer[] getEnergyConsumerInfo(); /** @@ -96,6 +100,7 @@ public final class PowerStatsHALWrapper { * @return List of EnergyConsumerResult objects containing energy consumer results for all * available energy consumers (power models). */ + @Nullable android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed( int[] energyConsumerIds); @@ -105,6 +110,7 @@ public final class PowerStatsHALWrapper { * @return List of Channel objects containing channel info for all available energy * meters. */ + @Nullable android.hardware.power.stats.Channel[] getEnergyMeterInfo(); /** @@ -120,6 +126,7 @@ public final class PowerStatsHALWrapper { * @return List of EnergyMeasurement objects containing energy measurements for all * available energy meters. */ + @Nullable android.hardware.power.stats.EnergyMeasurement[] readEnergyMeter(int[] channelIds); /** diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java index c4f29ea69218..ef0079e0c01f 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java @@ -16,6 +16,8 @@ package com.android.server.powerstats; +import static java.lang.System.currentTimeMillis; + import android.content.Context; import android.hardware.power.stats.Channel; import android.hardware.power.stats.EnergyConsumer; @@ -26,11 +28,14 @@ import android.hardware.power.stats.StateResidencyResult; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.SystemClock; import android.util.AtomicFile; import android.util.Slog; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; + import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils; import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils; @@ -61,6 +66,8 @@ public final class PowerStatsLogger extends Handler { protected static final int MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY = 1; protected static final int MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY = 2; + // TODO(b/181240441): Add a listener to update the Wall clock baseline when changed + private final long mStartWallTime; private final PowerStatsDataStorage mPowerStatsMeterStorage; private final PowerStatsDataStorage mPowerStatsModelStorage; private final PowerStatsDataStorage mPowerStatsResidencyStorage; @@ -79,6 +86,8 @@ public final class PowerStatsLogger extends Handler { // Log power meter data. EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeter(new int[0]); + EnergyMeasurementUtils.adjustTimeSinceBootToEpoch(energyMeasurements, + mStartWallTime); mPowerStatsMeterStorage.write( EnergyMeasurementUtils.getProtoBytes(energyMeasurements)); if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements); @@ -86,6 +95,8 @@ public final class PowerStatsLogger extends Handler { // Log power model data without attribution data. EnergyConsumerResult[] ecrNoAttribution = mPowerStatsHALWrapper.getEnergyConsumed(new int[0]); + EnergyConsumerResultUtils.adjustTimeSinceBootToEpoch(ecrNoAttribution, + mStartWallTime); mPowerStatsModelStorage.write( EnergyConsumerResultUtils.getProtoBytes(ecrNoAttribution, false)); if (DEBUG) EnergyConsumerResultUtils.print(ecrNoAttribution); @@ -97,6 +108,8 @@ public final class PowerStatsLogger extends Handler { // Log power model data with attribution data. EnergyConsumerResult[] ecrAttribution = mPowerStatsHALWrapper.getEnergyConsumed(new int[0]); + EnergyConsumerResultUtils.adjustTimeSinceBootToEpoch(ecrAttribution, + mStartWallTime); mPowerStatsModelStorage.write( EnergyConsumerResultUtils.getProtoBytes(ecrAttribution, true)); if (DEBUG) EnergyConsumerResultUtils.print(ecrAttribution); @@ -108,6 +121,8 @@ public final class PowerStatsLogger extends Handler { // Log state residency data. StateResidencyResult[] stateResidencyResults = mPowerStatsHALWrapper.getStateResidency(new int[0]); + StateResidencyResultUtils.adjustTimeSinceBootToEpoch(stateResidencyResults, + mStartWallTime); mPowerStatsResidencyStorage.write( StateResidencyResultUtils.getProtoBytes(stateResidencyResults)); if (DEBUG) StateResidencyResultUtils.print(stateResidencyResults); @@ -293,12 +308,19 @@ public final class PowerStatsLogger extends Handler { return mDeleteResidencyDataOnBoot; } + @VisibleForTesting + public long getStartWallTime() { + return mStartWallTime; + } + public PowerStatsLogger(Context context, File dataStoragePath, String meterFilename, String meterCacheFilename, String modelFilename, String modelCacheFilename, String residencyFilename, String residencyCacheFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { super(Looper.getMainLooper()); + mStartWallTime = currentTimeMillis() - SystemClock.elapsedRealtime(); + if (DEBUG) Slog.d(TAG, "mStartWallTime: " + mStartWallTime); mPowerStatsHALWrapper = powerStatsHALWrapper; mDataStoragePath = dataStoragePath; diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java index 11b22a574476..746a09882f93 100644 --- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java +++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java @@ -109,6 +109,18 @@ public class ProtoStreamUtils { } static class StateResidencyResultUtils { + public static void adjustTimeSinceBootToEpoch(StateResidencyResult[] stateResidencyResult, + long startWallTime) { + for (int i = 0; i < stateResidencyResult.length; i++) { + final int stateLength = stateResidencyResult[i].stateResidencyData.length; + for (int j = 0; j < stateLength; j++) { + final StateResidency stateResidencyData = + stateResidencyResult[i].stateResidencyData[j]; + stateResidencyData.lastEntryTimestampMs += startWallTime; + } + } + } + public static byte[] getProtoBytes(StateResidencyResult[] stateResidencyResult) { ProtoOutputStream pos = new ProtoOutputStream(); packProtoMessage(stateResidencyResult, pos); @@ -306,6 +318,13 @@ public class ProtoStreamUtils { } static class EnergyMeasurementUtils { + public static void adjustTimeSinceBootToEpoch(EnergyMeasurement[] energyMeasurement, + long startWallTime) { + for (int i = 0; i < energyMeasurement.length; i++) { + energyMeasurement[i].timestampMs += startWallTime; + } + } + public static byte[] getProtoBytes(EnergyMeasurement[] energyMeasurement) { ProtoOutputStream pos = new ProtoOutputStream(); packProtoMessage(energyMeasurement, pos); @@ -518,6 +537,13 @@ public class ProtoStreamUtils { } static class EnergyConsumerResultUtils { + public static void adjustTimeSinceBootToEpoch(EnergyConsumerResult[] energyConsumerResult, + long startWallTime) { + for (int i = 0; i < energyConsumerResult.length; i++) { + energyConsumerResult[i].timestampMs += startWallTime; + } + } + public static byte[] getProtoBytes(EnergyConsumerResult[] energyConsumerResult, boolean includeAttribution) { ProtoOutputStream pos = new ProtoOutputStream(); diff --git a/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java b/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java index bdabefbbeee6..ba778b3a481c 100644 --- a/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java +++ b/services/core/java/com/android/server/powerstats/StatsPullAtomCallbackImpl.java @@ -87,6 +87,8 @@ public class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCall return StatsManager.PULL_SKIP; } + if (energyMeasurements == null) return StatsManager.PULL_SKIP; + for (int i = 0; i < energyMeasurements.length; i++) { // Only report energy measurements that have been accumulated since boot final EnergyMeasurement energyMeasurement = energyMeasurements[i]; @@ -135,6 +137,8 @@ public class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCall return StatsManager.PULL_SKIP; } + if (results == null) return StatsManager.PULL_SKIP; + for (int i = 0; i < results.length; i++) { final StateResidencyResult result = results[i]; for (int j = 0; j < result.stateResidencyData.length; j++) { diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java index 6f7c016cb3f6..0cd0458c6b2b 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java @@ -65,6 +65,7 @@ final class RotationResolverManagerPerUserService extends @GuardedBy("mLock") RemoteRotationResolverService mRemoteService; + private static String sTestingPackage; private ComponentName mComponentName; RotationResolverManagerPerUserService(@NonNull RotationResolverManagerService main, @@ -136,19 +137,43 @@ final class RotationResolverManagerPerUserService extends } /** + * Set the testing package name. + * + * @param packageName the name of the package that implements {@link RotationResolverService} + * and is used for testing only. + */ + @VisibleForTesting + void setTestingPackage(String packageName) { + sTestingPackage = packageName; + mComponentName = resolveRotationResolverService(getContext()); + } + + /** + * get the currently bound component name. + */ + @VisibleForTesting + ComponentName getComponentName() { + return mComponentName; + } + + /** * Provides rotation resolver service component name at runtime, making sure it's provided * by the system. */ - private static ComponentName resolveRotationResolverService(Context context) { - final String serviceConfigPackage = getServiceConfigPackage(context); - + static ComponentName resolveRotationResolverService(Context context) { String resolvedPackage; int flags = PackageManager.MATCH_SYSTEM_ONLY; - - if (!TextUtils.isEmpty(serviceConfigPackage)) { - resolvedPackage = serviceConfigPackage; + if (!TextUtils.isEmpty(sTestingPackage)) { + // Testing Package is set. + resolvedPackage = sTestingPackage; + flags = PackageManager.GET_META_DATA; } else { - return null; + final String serviceConfigPackage = getServiceConfigPackage(context); + if (!TextUtils.isEmpty(serviceConfigPackage)) { + resolvedPackage = serviceConfigPackage; + } else { + return null; + } } final Intent intent = new Intent( @@ -158,14 +183,15 @@ final class RotationResolverManagerPerUserService extends flags, context.getUserId()); if (resolveInfo == null || resolveInfo.serviceInfo == null) { Slog.wtf(TAG, String.format("Service %s not found in package %s", - RotationResolverService.SERVICE_INTERFACE, serviceConfigPackage - )); + RotationResolverService.SERVICE_INTERFACE, resolvedPackage)); return null; } final ServiceInfo serviceInfo = resolveInfo.serviceInfo; final String permission = serviceInfo.permission; if (Manifest.permission.BIND_ROTATION_RESOLVER_SERVICE.equals(permission)) { + Slog.i(TAG, String.format("Successfully bound the service from package: %s", + resolvedPackage)); return serviceInfo.getComponentName(); } Slog.e(TAG, String.format( diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java index 54a9edba4e03..e5088c023533 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java @@ -16,12 +16,13 @@ package com.android.server.rotationresolver; - import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.ComponentName; import android.os.CancellationSignal; import android.os.ShellCommand; import android.rotationresolver.RotationResolverInternal.RotationResolverCallbackInternal; +import android.text.TextUtils; import android.view.Surface; import java.io.PrintWriter; @@ -59,7 +60,7 @@ final class RotationResolverShellCommand extends ShellCommand { } } - final TestableRotationCallbackInternal mTestableRotationCallbackInternal = + static final TestableRotationCallbackInternal sTestableRotationCallbackInternal = new TestableRotationCallbackInternal(); @Override @@ -73,20 +74,47 @@ final class RotationResolverShellCommand extends ShellCommand { return runResolveRotation(); case "get-last-resolution": return getLastResolution(); + case "set-testing-package": + return setTestRotationResolverPackage(getNextArgRequired()); + case "get-bound-package": + return getBoundPackageName(); + case "clear-testing-package": + return resetTestRotationResolverPackage(); default: return handleDefaultCommands(cmd); } } + private int getBoundPackageName() { + final PrintWriter out = getOutPrintWriter(); + final ComponentName componentName = mService.getComponentName(); + out.println(componentName == null ? "" : componentName.getPackageName()); + return 0; + } + + private int setTestRotationResolverPackage(String testingPackage) { + if (!TextUtils.isEmpty((testingPackage))) { + mService.setTestingPackage(testingPackage); + sTestableRotationCallbackInternal.reset(); + } + return 0; + } + + private int resetTestRotationResolverPackage() { + mService.setTestingPackage(""); + sTestableRotationCallbackInternal.reset(); + return 0; + } + private int runResolveRotation() { - mService.resolveRotationLocked(mTestableRotationCallbackInternal, Surface.ROTATION_0, + mService.resolveRotationLocked(sTestableRotationCallbackInternal, Surface.ROTATION_0, Surface.ROTATION_0, "", 2000L, new CancellationSignal()); return 0; } private int getLastResolution() { final PrintWriter out = getOutPrintWriter(); - out.println(mTestableRotationCallbackInternal.getLastCallbackCode()); + out.println(sTestableRotationCallbackInternal.getLastCallbackCode()); return 0; } @@ -99,5 +127,8 @@ final class RotationResolverShellCommand extends ShellCommand { pw.println(); pw.println(" resolve-rotation: request a rotation resolution."); pw.println(" get-last-resolution: show the last rotation resolution result."); + pw.println(" set-testing-package: Set the testing package that implements the service."); + pw.println(" get-bound-package: print the bound package that implements the service."); + pw.println(" clear-testing-package: reset the testing package."); } } diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java index 6ec71b717ec6..74bb99351a6d 100644 --- a/services/core/java/com/android/server/security/FileIntegrityService.java +++ b/services/core/java/com/android/server/security/FileIntegrityService.java @@ -23,10 +23,8 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.os.Binder; -import android.os.Build; import android.os.Environment; import android.os.IBinder; -import android.os.SystemProperties; import android.os.UserHandle; import android.security.IFileIntegrityService; import android.util.Slog; @@ -60,7 +58,7 @@ public class FileIntegrityService extends SystemService { private final IBinder mService = new IFileIntegrityService.Stub() { @Override public boolean isApkVeritySupported() { - return FileIntegrityService.isApkVeritySupported(); + return VerityUtils.isFsVeritySupported(); } @Override @@ -69,7 +67,7 @@ public class FileIntegrityService extends SystemService { checkCallerPermission(packageName); try { - if (!isApkVeritySupported()) { + if (!VerityUtils.isFsVeritySupported()) { return false; } if (certificateBytes == null) { @@ -110,11 +108,6 @@ public class FileIntegrityService extends SystemService { } }; - public static boolean isApkVeritySupported() { - return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R - || SystemProperties.getInt("ro.apk_verity.mode", 0) == 2; - } - public FileIntegrityService(final Context context) { super(context); try { diff --git a/services/core/java/com/android/server/security/KeyChainSystemService.java b/services/core/java/com/android/server/security/KeyChainSystemService.java index 3c06d0ec7950..edd4a3d7be93 100644 --- a/services/core/java/com/android/server/security/KeyChainSystemService.java +++ b/services/core/java/com/android/server/security/KeyChainSystemService.java @@ -16,6 +16,8 @@ package com.android.server.security; +import static android.os.PowerWhitelistManager.REASON_KEY_CHAIN; + import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -102,7 +104,8 @@ public class KeyChainSystemService extends SystemService { final DeviceIdleInternal idleController = LocalServices.getService(DeviceIdleInternal.class); idleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName, - KEYCHAIN_IDLE_WHITELIST_DURATION_MS, user.getIdentifier(), false, "keychain"); + KEYCHAIN_IDLE_WHITELIST_DURATION_MS, user.getIdentifier(), false, + REASON_KEY_CHAIN, "keychain"); getContext().startServiceAsUser(intent, user); } diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java index 09ee001a5544..48a60387fee7 100644 --- a/services/core/java/com/android/server/security/VerityUtils.java +++ b/services/core/java/com/android/server/security/VerityUtils.java @@ -17,7 +17,9 @@ package com.android.server.security; import android.annotation.NonNull; +import android.os.Build; import android.os.SharedMemory; +import android.os.SystemProperties; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; @@ -57,6 +59,11 @@ abstract public class VerityUtils { private static final boolean DEBUG = false; + public static boolean isFsVeritySupported() { + return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R + || SystemProperties.getInt("ro.apk_verity.mode", 0) == 2; + } + /** Returns true if the given file looks like containing an fs-verity signature. */ public static boolean isFsveritySignatureFile(File file) { return file.getName().endsWith(FSVERITY_SIGNATURE_FILE_EXTENSION); diff --git a/services/core/java/com/android/server/speech/Android.bp b/services/core/java/com/android/server/speech/Android.bp index 379b0754b82a..5605349812da 100644 --- a/services/core/java/com/android/server/speech/Android.bp +++ b/services/core/java/com/android/server/speech/Android.bp @@ -1,3 +1,12 @@ +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: "services.speech-sources", srcs: ["java/**/*.java"], diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java index 96248c3711e3..3f203026a219 100644 --- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java +++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java @@ -18,22 +18,65 @@ package com.android.server.speech; import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS; +import android.annotation.Nullable; +import android.app.AppOpsManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; import android.os.RemoteException; import android.speech.IRecognitionListener; import android.speech.IRecognitionService; import android.speech.RecognitionService; +import android.speech.SpeechRecognizer; +import android.util.Log; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.ServiceConnector; final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecognitionService> { private static final String TAG = RemoteSpeechRecognitionService.class.getSimpleName(); - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; - RemoteSpeechRecognitionService(Context context, ComponentName serviceName, int userId) { + private static final String APP_OP_MESSAGE = "Recording audio for speech recognition"; + private static final String RECORD_AUDIO_APP_OP = + AppOpsManager.permissionToOp(android.Manifest.permission.RECORD_AUDIO); + + private final Object mLock = new Object(); + + private boolean mConnected = false; + + @Nullable + private IRecognitionListener mListener; + + @Nullable + @GuardedBy("mLock") + private String mPackageName; + + @Nullable + @GuardedBy("mLock") + private String mFeatureId; + + @Nullable + @GuardedBy("mLock") + private DelegatingListener mDelegatingListener; + + // Makes sure we can block startListening() if session is still in progress. + @GuardedBy("mLock") + private boolean mSessionInProgress = false; + + // Makes sure we call startProxyOp / finishProxyOp at right times and only once per session. + @GuardedBy("mLock") + private boolean mRecordingInProgress = false; + + private final int mCallingUid; + private final AppOpsManager mAppOpsManager; + private final ComponentName mComponentName; + + RemoteSpeechRecognitionService( + Context context, ComponentName serviceName, int userId, int callingUid) { super(context, new Intent(RecognitionService.SERVICE_INTERFACE).setComponent(serviceName), Context.BIND_AUTO_CREATE @@ -43,46 +86,197 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn userId, IRecognitionService.Stub::asInterface); + mCallingUid = callingUid; + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + mComponentName = serviceName; + if (DEBUG) { Slog.i(TAG, "Bound to recognition service at: " + serviceName.flattenToString()); } } + ComponentName getServiceComponentName() { + return mComponentName; + } + void startListening(Intent recognizerIntent, IRecognitionListener listener, String packageName, - String featureId) throws RemoteException { + String featureId) { if (DEBUG) { - Slog.i(TAG, "#startListening for package: " + packageName + ", feature=" + featureId); + Slog.i(TAG, String.format("#startListening for package: %s, feature=%s, callingUid=%d", + packageName, featureId, mCallingUid)); + } + + if (listener == null) { + Log.w(TAG, "#startListening called with no preceding #setListening - ignoring"); + return; + } + + if (!mConnected) { + tryRespondWithError(listener, SpeechRecognizer.ERROR_SERVER_DISCONNECTED); + return; + } + + synchronized (mLock) { + if (mSessionInProgress) { + Slog.i(TAG, "#startListening called while listening is in progress."); + tryRespondWithError(listener, SpeechRecognizer.ERROR_RECOGNIZER_BUSY); + return; + } + + if (startProxyOp(packageName, featureId) != AppOpsManager.MODE_ALLOWED) { + tryRespondWithError(listener, SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS); + return; + } + mSessionInProgress = true; + mRecordingInProgress = true; + + mListener = listener; + mDelegatingListener = new DelegatingListener(listener, () -> { + // To be invoked in terminal calls of the callback: results() or error() + if (DEBUG) { + Slog.i(TAG, "Recognition session complete"); + } + + synchronized (mLock) { + resetStateLocked(); + } + }); + mPackageName = packageName; + mFeatureId = featureId; + + run(service -> + service.startListening( + recognizerIntent, + mDelegatingListener, + packageName, + featureId, + mCallingUid)); } - run(service -> service.startListening(recognizerIntent, listener, packageName, featureId)); } - void stopListening(IRecognitionListener listener, String packageName, String featureId) - throws RemoteException { + void stopListening( + IRecognitionListener listener, String packageName, String featureId) { if (DEBUG) { Slog.i(TAG, "#stopListening for package: " + packageName + ", feature=" + featureId); } - run(service -> service.stopListening(listener, packageName, featureId)); + + if (!mConnected) { + tryRespondWithError(listener, SpeechRecognizer.ERROR_SERVER_DISCONNECTED); + return; + } + + synchronized (mLock) { + if (mListener == null) { + Log.w(TAG, "#stopListening called with no preceding #startListening - ignoring"); + tryRespondWithError(listener, SpeechRecognizer.ERROR_CLIENT); + return; + } + + if (mListener.asBinder() != listener.asBinder()) { + Log.w(TAG, "#stopListening called with an unexpected listener"); + tryRespondWithError(listener, SpeechRecognizer.ERROR_CLIENT); + return; + } + + if (!mRecordingInProgress) { + Slog.i(TAG, "#stopListening called while listening isn't in progress, ignoring."); + return; + } + mRecordingInProgress = false; + + finishProxyOp(packageName, featureId); + + run(service -> service.stopListening(mDelegatingListener, packageName, featureId)); + } } - void cancel(IRecognitionListener listener, String packageName, String featureId) - throws RemoteException { + void cancel( + IRecognitionListener listener, + String packageName, + String featureId, + boolean isShutdown) { if (DEBUG) { Slog.i(TAG, "#cancel for package: " + packageName + ", feature=" + featureId); } - run(service -> service.cancel(listener, packageName, featureId)); + + if (!mConnected) { + tryRespondWithError(listener, SpeechRecognizer.ERROR_SERVER_DISCONNECTED); + } + + synchronized (mLock) { + if (mListener == null) { + if (DEBUG) { + Log.w(TAG, "#cancel called with no preceding #startListening - ignoring"); + } + return; + } + + if (mListener.asBinder() != listener.asBinder()) { + Log.w(TAG, "#cancel called with an unexpected listener"); + tryRespondWithError(listener, SpeechRecognizer.ERROR_CLIENT); + return; + } + + // Temporary reference to allow for resetting the hard link mDelegatingListener to null. + IRecognitionListener delegatingListener = mDelegatingListener; + + run(service -> service.cancel(delegatingListener, packageName, featureId, isShutdown)); + + if (mRecordingInProgress) { + finishProxyOp(packageName, featureId); + } + mRecordingInProgress = false; + mSessionInProgress = false; + + mDelegatingListener = null; + mListener = null; + + // Schedule to unbind after cancel is delivered. + if (isShutdown) { + run(service -> unbind()); + } + } + } + + void shutdown() { + synchronized (mLock) { + if (this.mListener == null) { + if (DEBUG) { + Slog.i(TAG, "Package died, but session wasn't initialized. " + + "Not invoking #cancel"); + } + return; + } + } + + cancel(mListener, mPackageName, mFeatureId, true /* isShutdown */); } @Override // from ServiceConnector.Impl protected void onServiceConnectionStatusChanged( IRecognitionService service, boolean connected) { - if (!DEBUG) { - return; + mConnected = connected; + + if (DEBUG) { + if (connected) { + Slog.i(TAG, "Connected to speech recognition service"); + } else { + Slog.w(TAG, "Disconnected from speech recognition service"); + } } - if (connected) { - Slog.i(TAG, "Connected to ASR service"); - } else { - Slog.w(TAG, "Disconnected from ASR service"); + synchronized (mLock) { + if (!connected) { + if (mListener == null) { + Slog.i(TAG, "Connection to speech recognition service lost, but no " + + "#startListening has been invoked yet."); + return; + } + + tryRespondWithError(mListener, SpeechRecognizer.ERROR_SERVER_DISCONNECTED); + + resetStateLocked(); + } } } @@ -90,4 +284,119 @@ final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecogn protected long getAutoDisconnectTimeoutMs() { return PERMANENT_BOUND_TIMEOUT_MS; } + + private void resetStateLocked() { + if (mRecordingInProgress && mPackageName != null) { + finishProxyOp(mPackageName, mFeatureId); + } + + mListener = null; + mDelegatingListener = null; + mSessionInProgress = false; + mRecordingInProgress = false; + } + + private int startProxyOp(String packageName, String featureId) { + final long identity = Binder.clearCallingIdentity(); + try { + return mAppOpsManager.startProxyOp( + RECORD_AUDIO_APP_OP, + mCallingUid, + packageName, + featureId, + APP_OP_MESSAGE); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private void finishProxyOp(String packageName, String featureId) { + final long identity = Binder.clearCallingIdentity(); + try { + mAppOpsManager.finishProxyOp( + RECORD_AUDIO_APP_OP, mCallingUid, packageName, featureId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private static void tryRespondWithError(IRecognitionListener listener, int errorCode) { + if (DEBUG) { + Slog.i(TAG, "Responding with error " + errorCode); + } + + try { + if (listener != null) { + listener.onError(errorCode); + } + } catch (RemoteException e) { + Slog.w(TAG, + String.format("Failed to respond with an error %d to the client", errorCode), + e); + } + } + + private static class DelegatingListener extends IRecognitionListener.Stub { + + private final IRecognitionListener mRemoteListener; + private final Runnable mOnSessionComplete; + + DelegatingListener(IRecognitionListener listener, Runnable onSessionComplete) { + mRemoteListener = listener; + mOnSessionComplete = onSessionComplete; + } + + @Override + public void onReadyForSpeech(Bundle params) throws RemoteException { + mRemoteListener.onReadyForSpeech(params); + } + + @Override + public void onBeginningOfSpeech() throws RemoteException { + mRemoteListener.onBeginningOfSpeech(); + } + + @Override + public void onRmsChanged(float rmsdB) throws RemoteException { + mRemoteListener.onRmsChanged(rmsdB); + } + + @Override + public void onBufferReceived(byte[] buffer) throws RemoteException { + mRemoteListener.onBufferReceived(buffer); + } + + @Override + public void onEndOfSpeech() throws RemoteException { + mRemoteListener.onEndOfSpeech(); + } + + @Override + public void onError(int error) throws RemoteException { + if (DEBUG) { + Slog.i(TAG, String.format("Error %d during recognition session", error)); + } + mOnSessionComplete.run(); + mRemoteListener.onError(error); + } + + @Override + public void onResults(Bundle results) throws RemoteException { + if (DEBUG) { + Slog.i(TAG, "#onResults invoked for a recognition session"); + } + mOnSessionComplete.run(); + mRemoteListener.onResults(results); + } + + @Override + public void onPartialResults(Bundle results) throws RemoteException { + mRemoteListener.onPartialResults(results); + } + + @Override + public void onEvent(int eventType, Bundle params) throws RemoteException { + mRemoteListener.onEvent(eventType, params); + } + } } diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java index 592ba9e616b8..dbe73546d748 100644 --- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java +++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java @@ -18,7 +18,9 @@ package com.android.server.speech; import android.annotation.NonNull; import android.annotation.UserIdInt; +import android.content.ComponentName; import android.content.Context; +import android.os.IBinder; import android.os.UserHandle; import android.speech.IRecognitionServiceManager; import android.speech.IRecognitionServiceManagerCallback; @@ -42,6 +44,7 @@ public final class SpeechRecognitionManagerService extends public SpeechRecognitionManagerService(@NonNull Context context) { super(context, + // TODO(b/176578753): think if we want to favor the particular service here. new FrameworkResourcesServiceNameResolver( context, R.string.config_defaultOnDeviceSpeechRecognitionService), @@ -63,11 +66,15 @@ public final class SpeechRecognitionManagerService extends final class SpeechRecognitionManagerServiceStub extends IRecognitionServiceManager.Stub { @Override - public void createSession(IRecognitionServiceManagerCallback callback) { + public void createSession( + ComponentName componentName, + IBinder clientToken, + boolean onDevice, + IRecognitionServiceManagerCallback callback) { int userId = UserHandle.getCallingUserId(); synchronized (mLock) { SpeechRecognitionManagerServiceImpl service = getServiceForUserLocked(userId); - service.createSessionLocked(callback); + service.createSessionLocked(componentName, clientToken, onDevice, callback); } } } diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java index bcaf174b1d92..2656a3d32555 100644 --- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java +++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java @@ -24,30 +24,44 @@ import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; +import android.os.Binder; +import android.os.IBinder; import android.os.RemoteException; import android.speech.IRecognitionListener; import android.speech.IRecognitionService; import android.speech.IRecognitionServiceManagerCallback; +import android.speech.SpeechRecognizer; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.infra.AbstractPerUserSystemService; +import com.google.android.collect.Sets; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + final class SpeechRecognitionManagerServiceImpl extends AbstractPerUserSystemService<SpeechRecognitionManagerServiceImpl, SpeechRecognitionManagerService> { - private static final String TAG = SpeechRecognitionManagerServiceImpl.class.getSimpleName(); + private static final int MAX_CONCURRENT_CONNECTIONS_BY_CLIENT = 10; + + private final Object mLock = new Object(); + + @NonNull @GuardedBy("mLock") - @Nullable - private RemoteSpeechRecognitionService mRemoteService; + private final Map<Integer, Set<RemoteSpeechRecognitionService>> mRemoteServicesByUid = + new HashMap<>(); SpeechRecognitionManagerServiceImpl( @NonNull SpeechRecognitionManagerService master, @NonNull Object lock, @UserIdInt int userId, boolean disabled) { super(master, lock, userId); - updateRemoteServiceLocked(); } @GuardedBy("mLock") @@ -67,92 +81,196 @@ final class SpeechRecognitionManagerServiceImpl extends @Override // from PerUserSystemService protected boolean updateLocked(boolean disabled) { final boolean enabledChanged = super.updateLocked(disabled); - updateRemoteServiceLocked(); return enabledChanged; } - /** - * Updates the reference to the remote service. - */ - @GuardedBy("mLock") - private void updateRemoteServiceLocked() { - if (mRemoteService != null) { - if (mMaster.debug) { - Slog.d(TAG, "updateRemoteService(): destroying old remote service"); - } - mRemoteService.unbind(); - mRemoteService = null; + void createSessionLocked( + ComponentName componentName, + IBinder clientToken, + boolean onDevice, + IRecognitionServiceManagerCallback callback) { + if (mMaster.debug) { + Slog.i(TAG, String.format("#createSessionLocked, component=%s, onDevice=%s", + componentName, onDevice)); + } + + ComponentName serviceComponent = componentName; + if (onDevice) { + serviceComponent = getOnDeviceComponentNameLocked(); } - } - void createSessionLocked(IRecognitionServiceManagerCallback callback) { - // TODO(b/176578753): check clients have record audio permission. - // TODO(b/176578753): verify caller package is the one supplied + if (serviceComponent == null) { + tryRespondWithError(callback, SpeechRecognizer.ERROR_CLIENT); + return; + } - RemoteSpeechRecognitionService service = ensureRemoteServiceLocked(); + final int creatorCallingUid = Binder.getCallingUid(); + Set<String> creatorPackageNames = + Sets.newArraySet( + getContext().getPackageManager().getPackagesForUid(creatorCallingUid)); + + RemoteSpeechRecognitionService service = createService(creatorCallingUid, serviceComponent); if (service == null) { - tryRespondWithError(callback); + tryRespondWithError(callback, SpeechRecognizer.ERROR_TOO_MANY_REQUESTS); return; } + IBinder.DeathRecipient deathRecipient = + () -> handleClientDeath(creatorCallingUid, service, true /* invoke #cancel */); + + try { + clientToken.linkToDeath(deathRecipient, 0); + } catch (RemoteException e) { + // RemoteException == binder already died, schedule disconnect anyway. + handleClientDeath(creatorCallingUid, service, true /* invoke #cancel */); + } + service.connect().thenAccept(binderService -> { if (binderService != null) { try { callback.onSuccess(new IRecognitionService.Stub() { @Override - public void startListening(Intent recognizerIntent, + public void startListening( + Intent recognizerIntent, IRecognitionListener listener, - String packageName, String featureId) throws RemoteException { + String packageName, + String featureId, + int callingUid) throws RemoteException { + verifyCallerIdentity( + creatorCallingUid, packageName, creatorPackageNames, listener); + if (callingUid != creatorCallingUid) { + listener.onError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS); + return; + } + service.startListening( recognizerIntent, listener, packageName, featureId); } @Override - public void stopListening(IRecognitionListener listener, + public void stopListening( + IRecognitionListener listener, String packageName, String featureId) throws RemoteException { + verifyCallerIdentity( + creatorCallingUid, packageName, creatorPackageNames, listener); + service.stopListening(listener, packageName, featureId); } @Override - public void cancel(IRecognitionListener listener, + public void cancel( + IRecognitionListener listener, String packageName, - String featureId) throws RemoteException { - service.cancel(listener, packageName, featureId); + String featureId, + boolean isShutdown) throws RemoteException { + verifyCallerIdentity( + creatorCallingUid, packageName, creatorPackageNames, listener); + + service.cancel(listener, packageName, featureId, isShutdown); + + if (isShutdown) { + handleClientDeath( + creatorCallingUid, + service, + false /* invoke #cancel */); + clientToken.unlinkToDeath(deathRecipient, 0); + } } }); } catch (RemoteException e) { Slog.e(TAG, "Error creating a speech recognition session", e); - tryRespondWithError(callback); + tryRespondWithError(callback, SpeechRecognizer.ERROR_CLIENT); } } else { - tryRespondWithError(callback); + tryRespondWithError(callback, SpeechRecognizer.ERROR_CLIENT); } }); } + private void verifyCallerIdentity( + int creatorCallingUid, + String packageName, + Set<String> creatorPackageNames, + IRecognitionListener listener) throws RemoteException { + if (creatorCallingUid != Binder.getCallingUid() + || !creatorPackageNames.contains(packageName)) { + listener.onError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS); + } + } + + private void handleClientDeath( + int callingUid, + RemoteSpeechRecognitionService service, boolean invokeCancel) { + if (invokeCancel) { + service.shutdown(); + } + removeService(callingUid, service); + } + @GuardedBy("mLock") @Nullable - private RemoteSpeechRecognitionService ensureRemoteServiceLocked() { - if (mRemoteService == null) { - final String serviceName = getComponentNameLocked(); - if (serviceName == null) { - if (mMaster.verbose) { - Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name."); - } + private ComponentName getOnDeviceComponentNameLocked() { + final String serviceName = getComponentNameLocked(); + if (serviceName == null) { + if (mMaster.verbose) { + Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name."); + } + return null; + } + return ComponentName.unflattenFromString(serviceName); + } + + private RemoteSpeechRecognitionService createService( + int callingUid, ComponentName serviceComponent) { + synchronized (mLock) { + Set<RemoteSpeechRecognitionService> servicesForClient = + mRemoteServicesByUid.get(callingUid); + + if (servicesForClient != null + && servicesForClient.size() >= MAX_CONCURRENT_CONNECTIONS_BY_CLIENT) { return null; } - final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName); - mRemoteService = - new RemoteSpeechRecognitionService(getContext(), serviceComponent, mUserId); + + if (servicesForClient != null) { + Optional<RemoteSpeechRecognitionService> existingService = + servicesForClient + .stream() + .filter(service -> + service.getServiceComponentName().equals(serviceComponent)) + .findFirst(); + if (existingService.isPresent()) { + return existingService.get(); + } + } + + RemoteSpeechRecognitionService service = + new RemoteSpeechRecognitionService( + getContext(), serviceComponent, getUserId(), callingUid); + + Set<RemoteSpeechRecognitionService> valuesByCaller = + mRemoteServicesByUid.computeIfAbsent(callingUid, key -> new HashSet<>()); + valuesByCaller.add(service); + + return service; + } + } + + private void removeService(int callingUid, RemoteSpeechRecognitionService service) { + synchronized (mLock) { + Set<RemoteSpeechRecognitionService> valuesByCaller = + mRemoteServicesByUid.get(callingUid); + if (valuesByCaller != null) { + valuesByCaller.remove(service); + } } - return mRemoteService; } - private static void tryRespondWithError(IRecognitionServiceManagerCallback callback) { + private static void tryRespondWithError(IRecognitionServiceManagerCallback callback, + int errorCode) { try { - callback.onError(); + callback.onError(errorCode); } catch (RemoteException e) { Slog.w(TAG, "Failed to respond with error"); } 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 15c72b34dbc0..6aa7c8a290c1 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -1471,12 +1471,18 @@ public class StatsPullAtomService extends SystemService { } int pullCpuTimePerClusterFreqLocked(int atomTag, List<StatsEvent> pulledData) { - boolean success = KernelCpuTotalBpfMapReader.read((cluster, freq, timeMs) -> { - pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, cluster, freq, timeMs)); - }); - if (!success) { + int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters(); + long[] freqs = KernelCpuBpfTracking.getFreqs(); + long[] timesMs = KernelCpuTotalBpfMapReader.read(); + if (timesMs == null) { return StatsManager.PULL_SKIP; } + for (int freqIndex = 0; freqIndex < timesMs.length; ++freqIndex) { + int cluster = freqsClusters[freqIndex]; + long freq = freqs[freqIndex]; + long timeMs = timesMs[freqIndex]; + pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, cluster, freq, timeMs)); + } return StatsManager.PULL_SUCCESS; } @@ -1503,48 +1509,42 @@ public class StatsPullAtomService extends SystemService { } private void registerCpuCyclesPerUidCluster() { - int tagId = FrameworkStatsLog.CPU_CYCLES_PER_UID_CLUSTER; - PullAtomMetadata metadata = new PullAtomMetadata.Builder() - .setAdditiveFields(new int[] {3, 4, 5}) - .build(); - mStatsManager.setPullAtomCallback( - tagId, - metadata, - DIRECT_EXECUTOR, - mStatsCallbackImpl - ); + // If eBPF tracking is not support, the procfs fallback is used if the kernel knows about + // CPU frequencies. + if (KernelCpuBpfTracking.isSupported() || KernelCpuBpfTracking.getClusters() > 0) { + int tagId = FrameworkStatsLog.CPU_CYCLES_PER_UID_CLUSTER; + PullAtomMetadata metadata = new PullAtomMetadata.Builder() + .setAdditiveFields(new int[] {3, 4, 5}) + .build(); + mStatsManager.setPullAtomCallback( + tagId, + metadata, + DIRECT_EXECUTOR, + mStatsCallbackImpl + ); + } } int pullCpuCyclesPerUidClusterLocked(int atomTag, List<StatsEvent> pulledData) { - // TODO(b/179485697): Remove power profile dependency. PowerProfile powerProfile = new PowerProfile(mContext); - // Frequency index to frequency mapping. - long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile); - // Frequency index to cluster mapping. - int[] freqClusters = new int[freqs.length]; - // Frequency index to power mapping. - double[] freqPowers = new double[freqs.length]; - // Number of clusters. - int clusters; - - // Initialize frequency mappings. + int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters(); + int clusters = KernelCpuBpfTracking.getClusters(); + long[] freqs = KernelCpuBpfTracking.getFreqs(); + double[] freqsPowers = new double[freqs.length]; + // Initialize frequency power mapping. { - int cluster = 0; int freqClusterIndex = 0; - long lastFreq = -1; + int lastCluster = -1; for (int freqIndex = 0; freqIndex < freqs.length; ++freqIndex, ++freqClusterIndex) { - long currFreq = freqs[freqIndex]; - if (currFreq <= lastFreq) { - cluster++; + int cluster = freqsClusters[freqIndex]; + if (cluster != lastCluster) { freqClusterIndex = 0; } - freqClusters[freqIndex] = cluster; - freqPowers[freqIndex] = + lastCluster = cluster; + + freqsPowers[freqIndex] = powerProfile.getAveragePowerForCpuCore(cluster, freqClusterIndex); - lastFreq = currFreq; } - - clusters = cluster + 1; } // Aggregate 0: mcycles, 1: runtime ms, 2: power profile estimate for the same uids for @@ -1570,12 +1570,12 @@ public class StatsPullAtomService extends SystemService { } for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) { - int cluster = freqClusters[freqIndex]; + int cluster = freqsClusters[freqIndex]; long timeMs = cpuFreqTimeMs[freqIndex]; values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES] += freqs[freqIndex] * timeMs; values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 1] += timeMs; values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 2] += - freqPowers[freqIndex] * timeMs; + freqsPowers[freqIndex] * timeMs; } }); @@ -1665,34 +1665,6 @@ public class StatsPullAtomService extends SystemService { } int pullCpuCyclesPerThreadGroupCluster(int atomTag, List<StatsEvent> pulledData) { - // TODO(b/179485697): Remove power profile dependency. - PowerProfile powerProfile = new PowerProfile(mContext); - // Frequency index to frequency mapping. - long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile); - if (freqs == null) { - return StatsManager.PULL_SKIP; - } - // Frequency index to cluster mapping. - int[] freqClusters = new int[freqs.length]; - // Number of clusters. - int clusters; - - // Initialize frequency mappings. - { - int cluster = 0; - long lastFreq = -1; - for (int freqIndex = 0; freqIndex < freqs.length; ++freqIndex) { - long currFreq = freqs[freqIndex]; - if (currFreq <= lastFreq) { - cluster++; - } - freqClusters[freqIndex] = cluster; - lastFreq = currFreq; - } - - clusters = cluster + 1; - } - SystemServiceCpuThreadTimes times = LocalServices.getService(BatteryStatsInternal.class) .getSystemServiceCpuThreadTimes(); if (times == null) { @@ -1701,22 +1673,24 @@ public class StatsPullAtomService extends SystemService { addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData, FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER, - times.threadCpuTimesUs, clusters, freqs, freqClusters); + times.threadCpuTimesUs); addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData, FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER_BINDER, - times.binderThreadCpuTimesUs, clusters, freqs, freqClusters); + times.binderThreadCpuTimesUs); return StatsManager.PULL_SUCCESS; } private static void addCpuCyclesPerThreadGroupClusterAtoms( - int atomTag, List<StatsEvent> pulledData, int threadGroup, long[] cpuTimesUs, - int clusters, long[] freqs, int[] freqClusters) { + int atomTag, List<StatsEvent> pulledData, int threadGroup, long[] cpuTimesUs) { + int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters(); + int clusters = KernelCpuBpfTracking.getClusters(); + long[] freqs = KernelCpuBpfTracking.getFreqs(); long[] aggregatedCycles = new long[clusters]; long[] aggregatedTimesUs = new long[clusters]; for (int i = 0; i < cpuTimesUs.length; ++i) { - aggregatedCycles[freqClusters[i]] += freqs[i] * cpuTimesUs[i] / 1_000; - aggregatedTimesUs[freqClusters[i]] += cpuTimesUs[i]; + aggregatedCycles[freqsClusters[i]] += freqs[i] * cpuTimesUs[i] / 1_000; + aggregatedTimesUs[freqsClusters[i]] += cpuTimesUs[i]; } for (int cluster = 0; cluster < clusters; ++cluster) { pulledData.add(FrameworkStatsLog.buildStatsEvent( @@ -2134,7 +2108,9 @@ public class StatsPullAtomService extends SystemService { metrics.kernelStackKb, metrics.totalIonKb, metrics.unaccountedKb, - metrics.gpuTotalUsageKb)); + metrics.gpuTotalUsageKb, + metrics.gpuPrivateAllocationsKb, + metrics.dmaBufTotalExportedKb)); return StatsManager.PULL_SUCCESS; } diff --git a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java index 1e80c4fe89fb..628c1d61ce9a 100644 --- a/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java +++ b/services/core/java/com/android/server/stats/pull/SystemMemoryUtil.java @@ -28,6 +28,8 @@ final class SystemMemoryUtil { static Metrics getMetrics() { int totalIonKb = (int) Debug.getIonHeapsSizeKb(); int gpuTotalUsageKb = (int) Debug.getGpuTotalUsageKb(); + int gpuDmaBufUsageKb = (int) Debug.getGpuDmaBufUsageKb(); + int dmaBufTotalExportedKb = (int) Debug.getDmabufTotalExportedKb(); long[] mInfos = new long[Debug.MEMINFO_COUNT]; Debug.getMemInfo(mInfos); @@ -49,14 +51,36 @@ final class SystemMemoryUtil { + mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE] + kReclaimableKb + mInfos[Debug.MEMINFO_VM_ALLOC_USED] - + mInfos[Debug.MEMINFO_PAGE_TABLES] - + Math.max(totalIonKb, 0); + + mInfos[Debug.MEMINFO_PAGE_TABLES]; if (!Debug.isVmapStack()) { // See b/146088882 accountedKb += mInfos[Debug.MEMINFO_KERNEL_STACK]; } + int gpuPrivateAllocationsKb = -1; + if (gpuTotalUsageKb >= 0 && gpuDmaBufUsageKb >= 0) { + gpuPrivateAllocationsKb = gpuTotalUsageKb - gpuDmaBufUsageKb; + } + // If we can distinguish gpu private allocs it means the dmabuf metrics + // are supported already. + if (dmaBufTotalExportedKb >= 0 && gpuPrivateAllocationsKb >= 0) { + // If we can calculate the overlap between dma memory and gpu + // drivers we can do more accurate tracking. But this is only + // available on 5.4+ kernels. + accountedKb += dmaBufTotalExportedKb + gpuPrivateAllocationsKb; + } else { + // If we cannot distinguish, accept that we will double count the + // dma buffers also used by the gpu driver. + accountedKb += Math.max(0, gpuTotalUsageKb); + if (dmaBufTotalExportedKb >= 0) { + accountedKb += dmaBufTotalExportedKb; + } else if (totalIonKb >= 0) { + // ION is a subset of total exported dmabuf memory. + accountedKb += totalIonKb; + } + } + Metrics result = new Metrics(); result.unreclaimableSlabKb = (int) mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE]; result.vmallocUsedKb = (int) mInfos[Debug.MEMINFO_VM_ALLOC_USED]; @@ -64,6 +88,8 @@ final class SystemMemoryUtil { result.kernelStackKb = (int) mInfos[Debug.MEMINFO_KERNEL_STACK]; result.totalIonKb = totalIonKb; result.gpuTotalUsageKb = gpuTotalUsageKb; + result.gpuPrivateAllocationsKb = gpuPrivateAllocationsKb; + result.dmaBufTotalExportedKb = dmaBufTotalExportedKb; result.unaccountedKb = (int) (mInfos[Debug.MEMINFO_TOTAL] - accountedKb); return result; } @@ -75,6 +101,8 @@ final class SystemMemoryUtil { public int kernelStackKb; public int totalIonKb; public int gpuTotalUsageKb; + public int gpuPrivateAllocationsKb; + public int dmaBufTotalExportedKb; public int unaccountedKb; } } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index d664651a68dc..8ffbb0a87dc0 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -1336,7 +1336,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void onNotificationClear(String pkg, String tag, int id, int userId, String key, + public void onNotificationClear(String pkg, int userId, String key, @NotificationStats.DismissalSurface int dismissalSurface, @NotificationStats.DismissalSentiment int dismissalSentiment, NotificationVisibility nv) { @@ -1345,7 +1345,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D final int callingPid = Binder.getCallingPid(); final long identity = Binder.clearCallingIdentity(); try { - mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId, + mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, userId, key, dismissalSurface, dismissalSentiment, nv); } finally { Binder.restoreCallingIdentity(identity); @@ -1501,6 +1501,18 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override + public void onNotificationFeedbackReceived(String key, Bundle feedback) { + enforceStatusBarService(); + final long identity = Binder.clearCallingIdentity(); + try { + mNotificationDelegate.onNotificationFeedbackReceived(key, feedback); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + + @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { (new StatusBarShellCommand(this, mContext)).exec( diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java index 0087c0c29853..dedb3ac6397d 100644 --- a/services/core/java/com/android/server/storage/StorageSessionController.java +++ b/services/core/java/com/android/server/storage/StorageSessionController.java @@ -32,6 +32,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.UserHandle; +import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import android.os.storage.VolumeInfo; import android.provider.MediaStore; @@ -162,22 +163,17 @@ public final class StorageSessionController { * * @return ANR dialog delay in milliseconds */ - public long getAnrDelayMillis(String packageName, int uid) + public void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason) throws ExternalStorageServiceException { + final int userId = UserHandle.getUserId(uid); + final StorageUserConnection connection; synchronized (mLock) { - int size = mConnections.size(); - for (int i = 0; i < size; i++) { - int key = mConnections.keyAt(i); - StorageUserConnection connection = mConnections.get(key); - if (connection != null) { - long delay = connection.getAnrDelayMillis(packageName, uid); - if (delay > 0) { - return delay; - } - } - } + connection = mConnections.get(userId); + } + + if (connection != null) { + connection.notifyAnrDelayStarted(packageName, uid, tid, reason); } - return 0; } /** @@ -371,6 +367,60 @@ public final class StorageSessionController { return mExternalStorageServiceComponent; } + /** + * Notify the controller that an app with {@code uid} and {@code tid} is blocked on an IO + * request on {@code volumeUuid} for {@code reason}. + * + * This blocked state can be queried with {@link #isAppIoBlocked} + * + * @hide + */ + public void notifyAppIoBlocked(String volumeUuid, int uid, int tid, + @StorageManager.AppIoBlockedReason int reason) { + final int userId = UserHandle.getUserId(uid); + final StorageUserConnection connection; + synchronized (mLock) { + connection = mConnections.get(userId); + } + + if (connection != null) { + connection.notifyAppIoBlocked(volumeUuid, uid, tid, reason); + } + } + + /** + * Notify the controller that an app with {@code uid} and {@code tid} has resmed a previously + * blocked IO request on {@code volumeUuid} for {@code reason}. + * + * All app IO will be automatically marked as unblocked if {@code volumeUuid} is unmounted. + */ + public void notifyAppIoResumed(String volumeUuid, int uid, int tid, + @StorageManager.AppIoBlockedReason int reason) { + final int userId = UserHandle.getUserId(uid); + final StorageUserConnection connection; + synchronized (mLock) { + connection = mConnections.get(userId); + } + + if (connection != null) { + connection.notifyAppIoResumed(volumeUuid, uid, tid, reason); + } + } + + /** Returns {@code true} if {@code uid} is blocked on IO, {@code false} otherwise */ + public boolean isAppIoBlocked(int uid) { + final int userId = UserHandle.getUserId(uid); + final StorageUserConnection connection; + synchronized (mLock) { + connection = mConnections.get(userId); + } + + if (connection != null) { + return connection.isAppIoBlocked(uid); + } + return false; + } + private void killExternalStorageService(int userId) { IActivityManager am = ActivityManager.getService(); try { diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java index 709d558ea0bc..d2b05c0914d7 100644 --- a/services/core/java/com/android/server/storage/StorageUserConnection.java +++ b/services/core/java/com/android/server/storage/StorageUserConnection.java @@ -16,7 +16,6 @@ package com.android.server.storage; -import static android.service.storage.ExternalStorageService.EXTRA_ANR_TIMEOUT_MS; import static android.service.storage.ExternalStorageService.EXTRA_ERROR; import static android.service.storage.ExternalStorageService.FLAG_SESSION_ATTRIBUTE_INDEXABLE; import static android.service.storage.ExternalStorageService.FLAG_SESSION_TYPE_FUSE; @@ -38,10 +37,12 @@ import android.os.ParcelableException; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; +import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.os.storage.StorageVolume; import android.service.storage.ExternalStorageService; import android.service.storage.IExternalStorageService; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -73,6 +74,7 @@ public final class StorageUserConnection { private final StorageSessionController mSessionController; private final ActiveConnection mActiveConnection = new ActiveConnection(); @GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>(); + @GuardedBy("mLock") private final Set<Integer> mUidsBlockedOnIo = new ArraySet<>(); private final HandlerThread mHandlerThread; public StorageUserConnection(Context context, int userId, StorageSessionController controller) { @@ -148,17 +150,13 @@ public final class StorageUserConnection { * * @return ANR dialog delay in milliseconds */ - public long getAnrDelayMillis(String packageName, int uid) + public void notifyAnrDelayStarted(String packageName, int uid, int tid, int reason) throws ExternalStorageServiceException { synchronized (mSessionsLock) { for (String sessionId : mSessions.keySet()) { - long delay = mActiveConnection.getAnrDelayMillis(packageName, uid); - if (delay > 0) { - return delay; - } + mActiveConnection.notifyAnrDelayStarted(packageName, uid, tid, reason); } } - return 0; } /** @@ -170,6 +168,7 @@ public final class StorageUserConnection { **/ public Session removeSession(String sessionId) { synchronized (mSessionsLock) { + mUidsBlockedOnIo.clear(); return mSessions.remove(sessionId); } } @@ -232,6 +231,41 @@ public final class StorageUserConnection { } } + /** + * Notify the controller that an app with {@code uid} and {@code tid} is blocked on an IO + * request on {@code volumeUuid} for {@code reason}. + * + * This blocked state can be queried with {@link #isAppIoBlocked} + * + * @hide + */ + public void notifyAppIoBlocked(String volumeUuid, int uid, int tid, + @StorageManager.AppIoBlockedReason int reason) { + synchronized (mSessionsLock) { + mUidsBlockedOnIo.add(uid); + } + } + + /** + * Notify the connection that an app with {@code uid} and {@code tid} has resmed a previously + * blocked IO request on {@code volumeUuid} for {@code reason}. + * + * All app IO will be automatically marked as unblocked if {@code volumeUuid} is unmounted. + */ + public void notifyAppIoResumed(String volumeUuid, int uid, int tid, + @StorageManager.AppIoBlockedReason int reason) { + synchronized (mSessionsLock) { + mUidsBlockedOnIo.remove(uid); + } + } + + /** Returns {@code true} if {@code uid} is blocked on IO, {@code false} otherwise */ + public boolean isAppIoBlocked(int uid) { + synchronized (mSessionsLock) { + return mUidsBlockedOnIo.contains(uid); + } + } + @FunctionalInterface interface AsyncStorageServiceCall { void run(@NonNull IExternalStorageService service, RemoteCallback callback) throws @@ -253,9 +287,6 @@ public final class StorageUserConnection { @GuardedBy("mLock") private final ArrayList<CompletableFuture<Void>> mOutstandingOps = new ArrayList<>(); - @GuardedBy("mLock") - private final ArrayList<CompletableFuture<Long>> mOutstandingTimeoutOps = new ArrayList<>(); - @Override public void close() { ServiceConnection oldConnection = null; @@ -272,9 +303,6 @@ public final class StorageUserConnection { for (CompletableFuture<Void> op : mOutstandingOps) { op.cancel(true); } - for (CompletableFuture<Long> op : mOutstandingTimeoutOps) { - op.cancel(true); - } mOutstandingOps.clear(); } @@ -297,15 +325,6 @@ public final class StorageUserConnection { DEFAULT_REMOTE_TIMEOUT_SECONDS); } - private long waitForAsyncLong(AsyncStorageServiceCall asyncCall) throws Exception { - CompletableFuture<Long> opFuture = new CompletableFuture<>(); - RemoteCallback callback = - new RemoteCallback(result -> setTimeoutResult(result, opFuture)); - - return waitForAsync(asyncCall, callback, opFuture, mOutstandingTimeoutOps, - 1 /* timeoutSeconds */); - } - private <T> T waitForAsync(AsyncStorageServiceCall asyncCall, RemoteCallback callback, CompletableFuture<T> opFuture, ArrayList<CompletableFuture<T>> outstandingOps, long timeoutSeconds) throws Exception { @@ -380,27 +399,17 @@ public final class StorageUserConnection { } } - public long getAnrDelayMillis(String packgeName, int uid) + public void notifyAnrDelayStarted(String packgeName, int uid, int tid, int reason) throws ExternalStorageServiceException { try { - return waitForAsyncLong((service, callback) -> - service.getAnrDelayMillis(packgeName, uid, callback)); + waitForAsyncVoid((service, callback) -> + service.notifyAnrDelayStarted(packgeName, uid, tid, reason, callback)); } catch (Exception e) { - throw new ExternalStorageServiceException("Failed to notify app not responding: " + throw new ExternalStorageServiceException("Failed to notify ANR delay started: " + packgeName, e); } } - private void setTimeoutResult(Bundle result, CompletableFuture<Long> future) { - ParcelableException ex = result.getParcelable(EXTRA_ERROR); - if (ex != null) { - future.completeExceptionally(ex); - } else { - long timeoutMs = result.getLong(EXTRA_ANR_TIMEOUT_MS); - future.complete(timeoutMs); - } - } - private void setResult(Bundle result, CompletableFuture<Void> future) { ParcelableException ex = result.getParcelable(EXTRA_ERROR); if (ex != null) { diff --git a/services/core/java/com/android/server/telecom/InternalServiceRepository.java b/services/core/java/com/android/server/telecom/InternalServiceRepository.java index 76ea5c788bd7..bd1746d3fc61 100644 --- a/services/core/java/com/android/server/telecom/InternalServiceRepository.java +++ b/services/core/java/com/android/server/telecom/InternalServiceRepository.java @@ -16,6 +16,8 @@ package com.android.server.telecom; +import static android.os.PowerWhitelistManager.REASON_UNKNOWN; + import android.content.Context; import android.os.Binder; import android.os.Process; @@ -38,7 +40,7 @@ public class InternalServiceRepository extends IInternalServiceRetriever.Stub { public void exemptAppTemporarilyForEvent(String packageName, long duration, int userHandle, String reason) { mDeviceIdleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName, - duration, userHandle, true /*sync*/, reason); + duration, userHandle, true /*sync*/, REASON_UNKNOWN, reason); } }; diff --git a/services/core/java/com/android/server/timedetector/DeviceConfig.java b/services/core/java/com/android/server/timedetector/DeviceConfig.java deleted file mode 100644 index 7b9ad0f531aa..000000000000 --- a/services/core/java/com/android/server/timedetector/DeviceConfig.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 com.android.server.timedetector; - -import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.StringDef; - -import java.time.Duration; -import java.util.concurrent.Executor; - -/** - * A helper class for reading / monitoring the {@link - * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace for server-configured flags. - */ -public final class DeviceConfig { - - /** - * An annotation used to indicate when a {@link - * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} key is required. - * - * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider - * also shares the {@link android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the - * prefix "geotz_" on all of its key strings. - */ - @StringDef(prefix = "KEY_", value = { - KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED, - KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT, - KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS, - KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS, - KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS, - }) - @interface DeviceConfigKey {} - - /** - * The key to force location time zone detection on for a device. Only intended for use during - * release testing with droidfooders. The user can still disable the feature by turning off the - * master location switch, or disabling automatic time zone detection. - */ - @DeviceConfigKey - public static final String KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED = - "force_location_time_zone_detection_enabled"; - - /** - * The key for the default value used to determine whether location time zone detection is - * enabled when the user hasn't explicitly set it yet. - */ - @DeviceConfigKey - public static final String KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT = - "location_time_zone_detection_enabled_default"; - - /** - * The key for the minimum delay after location time zone detection has been enabled before the - * location time zone manager can report it is uncertain about the time zone. - */ - @DeviceConfigKey - public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS = - "location_time_zone_detection_uncertainty_delay_millis"; - - /** - * The key for the timeout passed to a location time zone provider that tells it how long it has - * to provide an explicit first suggestion without being declared uncertain. - */ - @DeviceConfigKey - public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS = - "ltpz_init_timeout_millis"; - - /** - * The key for the extra time added to {@link - * #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone - * manager before the location time zone provider will actually be declared uncertain. - */ - @DeviceConfigKey - public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS = - "ltpz_init_timeout_fuzz_millis"; - - /** Creates an instance. */ - public DeviceConfig() {} - - /** Adds a listener for the system_time namespace. */ - public void addListener( - @NonNull Executor handlerExecutor, @NonNull Runnable listener) { - android.provider.DeviceConfig.addOnPropertiesChangedListener( - NAMESPACE_SYSTEM_TIME, - handlerExecutor, - properties -> listener.run()); - } - - /** - * Returns a boolean value from {@link android.provider.DeviceConfig} from the system_time - * namespace, or {@code defaultValue} if there is no explicit value set. - */ - public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) { - return android.provider.DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue); - } - - /** - * Returns a positive duration from {@link android.provider.DeviceConfig} from the system_time - * namespace, or {@code defaultValue} if there is no explicit value set. - */ - @Nullable - public Duration getDurationFromMillis( - @DeviceConfigKey String key, @Nullable Duration defaultValue) { - long deviceConfigValue = - android.provider.DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1); - if (deviceConfigValue < 0) { - return defaultValue; - } - return Duration.ofMillis(deviceConfigValue); - } -} diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java new file mode 100644 index 000000000000..8819b371b225 --- /dev/null +++ b/services/core/java/com/android/server/timedetector/ServerFlags.java @@ -0,0 +1,238 @@ +/* + * 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.timedetector; + +import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StringDef; +import android.content.Context; +import android.provider.DeviceConfig; +import android.util.ArrayMap; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.timezonedetector.ConfigurationChangeListener; +import com.android.server.timezonedetector.ServiceConfigAccessor; + +import java.time.Duration; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +/** + * A helper class for reading / monitoring the {@link DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace + * for server-configured flags. + */ +public final class ServerFlags { + + private static final Optional<Boolean> OPTIONAL_TRUE = Optional.of(true); + private static final Optional<Boolean> OPTIONAL_FALSE = Optional.of(false); + + /** + * An annotation used to indicate when a {@link DeviceConfig#NAMESPACE_SYSTEM_TIME} key is + * required. + * + * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider + * also shares the {@link DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the + * prefix "geotz_" on all of its key strings. + */ + @StringDef(prefix = "KEY_", value = { + KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED, + KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE, + KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE, + KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS, + KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS, + KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS, + KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE, + KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT, + }) + @interface DeviceConfigKey {} + + /** + * Controls whether the location time zone manager service will started. Only observed if + * the device build is configured to support location-based time zone detection. See + * {@link ServiceConfigAccessor#isGeoTimeZoneDetectionFeatureSupportedInConfig()} and {@link + * ServiceConfigAccessor#isGeoTimeZoneDetectionFeatureSupported()}. + */ + @DeviceConfigKey + public static final String KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED = + "location_time_zone_detection_feature_supported"; + + /** + * The key for the server flag that can override the device config for whether the primary + * location time zone provider is enabled or disabled. + */ + @DeviceConfigKey + public static final String KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE = + "primary_location_time_zone_provider_enabled_override"; + + /** + * The key for the server flag that can override the device config for whether the secondary + * location time zone provider is enabled or disabled. + */ + @DeviceConfigKey + public static final String KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE = + "secondary_location_time_zone_provider_enabled_override"; + + /** + * The key for the minimum delay after location time zone detection has been enabled before the + * location time zone manager can report it is uncertain about the time zone. + */ + @DeviceConfigKey + public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS = + "location_time_zone_detection_uncertainty_delay_millis"; + + /** + * The key for the timeout passed to a location time zone provider that tells it how long it has + * to provide an explicit first suggestion without being declared uncertain. + */ + @DeviceConfigKey + public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS = + "ltpz_init_timeout_millis"; + + /** + * The key for the extra time added to {@link + * #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone + * manager before the location time zone provider will actually be declared uncertain. + */ + @DeviceConfigKey + public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS = + "ltpz_init_timeout_fuzz_millis"; + + /** + * The key for the server flag that can override location time zone detection being enabled for + * a user. Only intended for use during release testing with droidfooders. The user can still + * disable the feature by turning off the master location switch, or by disabling automatic time + * zone detection. + */ + @DeviceConfigKey + public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE = + "location_time_zone_detection_setting_enabled_override"; + + /** + * The key for the default value used to determine whether location time zone detection is + * enabled when the user hasn't explicitly set it yet. + */ + @DeviceConfigKey + public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT = + "location_time_zone_detection_setting_enabled_default"; + + @GuardedBy("mListeners") + private final ArrayMap<ConfigurationChangeListener, Set<String>> mListeners = new ArrayMap<>(); + + private static final Object SLOCK = new Object(); + + @GuardedBy("SLOCK") + @Nullable + private static ServerFlags sInstance; + + private ServerFlags(Context context) { + DeviceConfig.addOnPropertiesChangedListener( + NAMESPACE_SYSTEM_TIME, + context.getMainExecutor(), + this::handlePropertiesChanged); + } + + /** Returns the singleton instance. */ + public static ServerFlags getInstance(Context context) { + synchronized (SLOCK) { + if (sInstance == null) { + sInstance = new ServerFlags(context); + } + return sInstance; + } + } + + private void handlePropertiesChanged(@NonNull DeviceConfig.Properties properties) { + synchronized (mListeners) { + for (Map.Entry<ConfigurationChangeListener, Set<String>> listenerEntry + : mListeners.entrySet()) { + if (intersects(listenerEntry.getValue(), properties.getKeyset())) { + listenerEntry.getKey().onChange(); + } + } + } + } + + private static boolean intersects(@NonNull Set<String> one, @NonNull Set<String> two) { + for (String toFind : one) { + if (two.contains(toFind)) { + return true; + } + } + return false; + } + + /** + * Adds a listener for the system_time namespace that will trigger if any of the specified keys + * change. Listener callbacks are delivered on the main looper thread. + * + * <p>Note: Only for use by long-lived objects like other singletons. There is deliberately no + * associated remove method. + */ + public void addListener(@NonNull ConfigurationChangeListener listener, + @NonNull Set<String> keys) { + Objects.requireNonNull(listener); + Objects.requireNonNull(keys); + + synchronized (mListeners) { + mListeners.put(listener, keys); + } + } + + /** + * Returns an optional boolean value from {@link DeviceConfig} from the system_time + * namespace, returns {@link Optional#empty()} if there is no explicit value set. + */ + @NonNull + public Optional<Boolean> getOptionalBoolean(@DeviceConfigKey String key) { + String value = DeviceConfig.getProperty(NAMESPACE_SYSTEM_TIME, key); + return parseOptionalBoolean(value); + } + + @NonNull + private static Optional<Boolean> parseOptionalBoolean(@Nullable String value) { + if (value == null) { + return Optional.empty(); + } else { + return Boolean.parseBoolean(value) ? OPTIONAL_TRUE : OPTIONAL_FALSE; + } + } + + /** + * Returns a boolean value from {@link DeviceConfig} from the system_time + * namespace, or {@code defaultValue} if there is no explicit value set. + */ + public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) { + return DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue); + } + + /** + * Returns a positive duration from {@link DeviceConfig} from the system_time + * namespace, or {@code defaultValue} if there is no explicit value set. + */ + @Nullable + public Duration getDurationFromMillis( + @DeviceConfigKey String key, @Nullable Duration defaultValue) { + long deviceConfigValue = DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1); + if (deviceConfigValue < 0) { + return defaultValue; + } + return Duration.ofMillis(deviceConfigValue); + } +} diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java index 4c7b1f38dd5a..aa8ad37815bf 100644 --- a/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java +++ b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java @@ -17,10 +17,11 @@ package com.android.server.timezonedetector; /** - * A listener used to receive notification that time zone configuration has changed. + * A listener used to receive notification that configuration has / may have changed (depending on + * the usecase). */ @FunctionalInterface public interface ConfigurationChangeListener { - /** Called when the current user or a configuration value has changed. */ + /** Called when the configuration may have changed. */ void onChange(); } diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java index 1f73977444f8..3ae9d641e81c 100644 --- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java +++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java @@ -33,26 +33,27 @@ import com.android.internal.util.Preconditions; import java.util.Objects; /** - * Holds all configuration values that affect time zone behavior and some associated logic, e.g. - * {@link #getAutoDetectionEnabledBehavior()}, {@link #getGeoDetectionEnabledBehavior()} and {@link - * #createCapabilitiesAndConfig()}. + * Holds configuration values that affect user-facing time zone behavior and some associated logic. + * Some configuration is global, some is user scoped, but this class deliberately doesn't make a + * distinction for simplicity. */ public final class ConfigurationInternal { - private final @UserIdInt int mUserId; - private final boolean mUserConfigAllowed; private final boolean mAutoDetectionSupported; private final boolean mGeoDetectionSupported; private final boolean mAutoDetectionEnabled; + private final @UserIdInt int mUserId; + private final boolean mUserConfigAllowed; private final boolean mLocationEnabled; private final boolean mGeoDetectionEnabled; private ConfigurationInternal(Builder builder) { - mUserId = builder.mUserId; - mUserConfigAllowed = builder.mUserConfigAllowed; mAutoDetectionSupported = builder.mAutoDetectionSupported; mGeoDetectionSupported = builder.mGeoDetectionSupported; mAutoDetectionEnabled = builder.mAutoDetectionEnabled; + + mUserId = builder.mUserId; + mUserConfigAllowed = builder.mUserConfigAllowed; mLocationEnabled = builder.mLocationEnabled; mGeoDetectionEnabled = builder.mGeoDetectionEnabled; // if mGeoDetectionSupported then mAutoDetectionSupported, i.e. mGeoDetectionSupported @@ -60,22 +61,6 @@ public final class ConfigurationInternal { Preconditions.checkState(mAutoDetectionSupported || !mGeoDetectionSupported); } - /** Returns the ID of the user this configuration is associated with. */ - public @UserIdInt int getUserId() { - return mUserId; - } - - /** Returns the handle of the user this configuration is associated with. */ - @NonNull - public UserHandle getUserHandle() { - return UserHandle.of(mUserId); - } - - /** Returns true if the user allowed to modify time zone configuration. */ - public boolean isUserConfigAllowed() { - return mUserConfigAllowed; - } - /** Returns true if the device supports any form of auto time zone detection. */ public boolean isAutoDetectionSupported() { return mAutoDetectionSupported; @@ -98,6 +83,22 @@ public final class ConfigurationInternal { return mAutoDetectionSupported && mAutoDetectionEnabled; } + /** Returns the ID of the user this configuration is associated with. */ + public @UserIdInt int getUserId() { + return mUserId; + } + + /** Returns the handle of the user this configuration is associated with. */ + @NonNull + public UserHandle getUserHandle() { + return UserHandle.of(mUserId); + } + + /** Returns true if the user allowed to modify time zone configuration. */ + public boolean isUserConfigAllowed() { + return mUserConfigAllowed; + } + /** Returns true if user's location can be used generally. */ public boolean isLocationEnabled() { return mLocationEnabled; @@ -283,7 +284,7 @@ public final class ConfigurationInternal { /** * Sets whether any form of automatic time zone detection is supported on this device. */ - public Builder setAutoDetectionSupported(boolean supported) { + public Builder setAutoDetectionFeatureSupported(boolean supported) { mAutoDetectionSupported = supported; return this; } @@ -291,7 +292,7 @@ public final class ConfigurationInternal { /** * Sets whether geolocation time zone detection is supported on this device. */ - public Builder setGeoDetectionSupported(boolean supported) { + public Builder setGeoDetectionFeatureSupported(boolean supported) { mGeoDetectionSupported = supported; return this; } diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java index f52b9b1b1c58..e3caae9482d9 100644 --- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java +++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java @@ -31,9 +31,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.location.LocationManager; -import android.net.ConnectivityManager; import android.os.Handler; -import android.os.HandlerExecutor; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -42,10 +40,9 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; -import com.android.server.timedetector.DeviceConfig; import java.util.Objects; -import java.util.concurrent.Executor; +import java.util.Optional; /** * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}. @@ -59,8 +56,7 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir @NonNull private final Handler mHandler; @NonNull private final ContentResolver mCr; @NonNull private final UserManager mUserManager; - @NonNull private final DeviceConfig mDeviceConfig; - @NonNull private final boolean mGeoDetectionSupported; + @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final LocationManager mLocationManager; // @NonNull after setConfigChangeListener() is called. @@ -68,17 +64,16 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir private ConfigurationChangeListener mConfigChangeListener; EnvironmentImpl(@NonNull Context context, @NonNull Handler handler, - @NonNull DeviceConfig deviceConfig, boolean geoDetectionSupported) { + @NonNull ServiceConfigAccessor serviceConfigAccessor) { mContext = Objects.requireNonNull(context); mHandler = Objects.requireNonNull(handler); - Executor handlerExecutor = new HandlerExecutor(mHandler); mCr = context.getContentResolver(); mUserManager = context.getSystemService(UserManager.class); mLocationManager = context.getSystemService(LocationManager.class); - mDeviceConfig = deviceConfig; - mGeoDetectionSupported = geoDetectionSupported; + mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor); - // Wire up the change listeners. All invocations are performed on the mHandler thread. + // Wire up the config change listeners. All invocations are performed on the mHandler + // thread. // Listen for the user changing / the user's location mode changing. IntentFilter filter = new IntentFilter(); @@ -112,13 +107,6 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir handleConfigChangeOnHandlerThread(); } }, UserHandle.USER_ALL); - - // Add async callbacks for changes to server-side flags: some of the flags affect device / - // user config. All changes can be treated like a config change. If flags that affect config - // haven't changed then call will be a no-op. - mDeviceConfig.addListener( - handlerExecutor, - this::handleConfigChangeOnHandlerThread); } private void handleConfigChangeOnHandlerThread() { @@ -140,10 +128,12 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir @Override public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) { return new ConfigurationInternal.Builder(userId) - .setUserConfigAllowed(isUserConfigAllowed(userId)) - .setAutoDetectionSupported(isAutoDetectionSupported()) - .setGeoDetectionSupported(isGeoDetectionSupported()) + .setAutoDetectionFeatureSupported( + mServiceConfigAccessor.isAutoDetectionFeatureSupported()) + .setGeoDetectionFeatureSupported( + mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) .setAutoDetectionEnabled(isAutoDetectionEnabled()) + .setUserConfigAllowed(isUserConfigAllowed(userId)) .setLocationEnabled(isLocationEnabled(userId)) .setGeoDetectionEnabled(isGeoDetectionEnabled(userId)) .build(); @@ -186,18 +176,19 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir // time zone detection: if we wrote it down then we'd set the value explicitly, which would // prevent detecting "default" later. That might influence what happens on later releases // that support new types of auto detection on the same hardware. - if (isAutoDetectionSupported()) { + if (mServiceConfigAccessor.isAutoDetectionFeatureSupported()) { final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled(); setAutoDetectionEnabledIfRequired(autoDetectionEnabled); - // Avoid writing the geo detection enabled setting for devices that do not support geo - // time zone detection: if we wrote it down then we'd set the value explicitly, which - // would prevent detecting "default" later. That might influence what happens on later - // releases that support geo detection on the same hardware. - // Also avoid writing the geo detection enabled setting for devices that are currently - // force-enabled: otherwise we might overwrite a droidfood user's real setting - // permanently. - if (isGeoDetectionSupported() && !isGeoDetectionForceEnabled()) { + // Avoid writing the geo detection enabled setting for devices with settings that + // are currently overridden by server flags: otherwise we might overwrite a droidfood + // user's real setting permanently. + // Also avoid writing the geo detection enabled setting for devices that do not support + // geo time zone detection: if we wrote it down then we'd set the value explicitly, + // which would prevent detecting "default" later. That might influence what happens on + // later releases that start to support geo detection on the same hardware. + if (!mServiceConfigAccessor.getGeoDetectionSettingEnabledOverride().isPresent() + && mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) { final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled(); setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled); } @@ -209,14 +200,6 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle); } - private boolean isAutoDetectionSupported() { - return deviceHasTelephonyNetwork() || isGeoDetectionSupported(); - } - - private boolean isGeoDetectionSupported() { - return mGeoDetectionSupported; - } - private boolean isAutoDetectionEnabled() { return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0; } @@ -237,24 +220,20 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir private boolean isGeoDetectionEnabled(@UserIdInt int userId) { // We may never use this, but it gives us a way to force location-based time zone detection - // on for testers (where their other settings allow). - boolean forceEnabled = isGeoDetectionForceEnabled(); - if (forceEnabled) { - return true; + // on/off for testers (but only where their other settings would allow them to turn it on + // for themselves). + Optional<Boolean> override = mServiceConfigAccessor.getGeoDetectionSettingEnabledOverride(); + if (override.isPresent()) { + return override.get(); } - final boolean geoDetectionEnabledByDefault = mDeviceConfig.getBoolean( - DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT, false); + final boolean geoDetectionEnabledByDefault = + mServiceConfigAccessor.isGeoDetectionEnabledForUsersByDefault(); return Settings.Secure.getIntForUser(mCr, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, (geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0; } - private boolean isGeoDetectionForceEnabled() { - return mDeviceConfig.getBoolean( - DeviceConfig.KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED, false); - } - private void setGeoDetectionEnabledIfRequired(@UserIdInt int userId, boolean enabled) { // See comment in setAutoDetectionEnabledIfRequired. http://b/171953500 if (isGeoDetectionEnabled(userId) != enabled) { @@ -262,10 +241,4 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir enabled ? 1 : 0, userId); } } - - private boolean deviceHasTelephonyNetwork() { - // TODO b/150583524 Avoid the use of a deprecated API. - return mContext.getSystemService(ConnectivityManager.class) - .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); - } } diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java new file mode 100644 index 000000000000..86c32f8d7b45 --- /dev/null +++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java @@ -0,0 +1,249 @@ +/* + * 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.timezonedetector; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.os.SystemProperties; +import android.util.ArraySet; + +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.server.timedetector.ServerFlags; + +import java.time.Duration; +import java.util.Collections; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +/** + * A singleton that provides access to service configuration for time zone detection. This hides how + * configuration is split between static, compile-time config and dynamic, server-pushed flags. It + * provides a rudimentary mechanism to signal when values have changed. + */ +public final class ServiceConfigAccessor { + + private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet( + new ArraySet<>(new String[] { + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED, + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT, + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE, + ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE, + ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE, + ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS, + ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS, + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS + })); + + // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented + private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1); + // TODO(b/179488561): Put this back to 1 minute when primary provider is fully implemented + private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = + Duration.ofSeconds(20); + private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5); + + private static final Object SLOCK = new Object(); + + /** The singleton instance. Initialized once in {@link #getInstance(Context)}. */ + @GuardedBy("SLOCK") + @Nullable + private static ServiceConfigAccessor sInstance; + + @NonNull private final Context mContext; + + /** + * An ultimate "feature switch" for location-based time zone detection. If this is + * {@code false}, the device cannot support the feature without a config change or a reboot: + * This affects what services are started on boot to minimize expense when the feature is not + * wanted. + */ + private final boolean mGeoDetectionFeatureSupportedInConfig; + + @NonNull private final ServerFlags mServerFlags; + + private ServiceConfigAccessor(@NonNull Context context) { + mContext = Objects.requireNonNull(context); + + // The config value is expected to be the main feature flag. Platform developers can also + // force enable the feature using a persistent system property. Because system properties + // can change, this value is cached and only changes on reboot. + mGeoDetectionFeatureSupportedInConfig = context.getResources().getBoolean( + com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection) + || SystemProperties.getBoolean( + "persist.sys.location_time_zone_detection_feature_supported", false); + + mServerFlags = ServerFlags.getInstance(mContext); + } + + /** Returns the singleton instance. */ + public static ServiceConfigAccessor getInstance(Context context) { + synchronized (SLOCK) { + if (sInstance == null) { + sInstance = new ServiceConfigAccessor(context); + } + return sInstance; + } + } + + /** + * Adds a listener that will be called server flags related to this class change. The callbacks + * are delivered on the main looper thread. + * + * <p>Note: Only for use by long-lived objects. There is deliberately no associated remove + * method. + */ + public void addListener(@NonNull ConfigurationChangeListener listener) { + mServerFlags.addListener(listener, SERVER_FLAGS_KEYS_TO_WATCH); + } + + /** Returns {@code true} if any form of automatic time zone detection is supported. */ + public boolean isAutoDetectionFeatureSupported() { + return deviceHasTelephonyNetwork() || isGeoTimeZoneDetectionFeatureSupported(); + } + + private boolean deviceHasTelephonyNetwork() { + // TODO b/150583524 Avoid the use of a deprecated API. + return mContext.getSystemService(ConnectivityManager.class) + .isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + } + + /** + * Returns {@code true} if the location-based time zone detection feature can be supported on + * this device at all according to config. When {@code false}, implies that various other + * location-based settings will be turned off or rendered meaningless. Typically {@link + * #isGeoTimeZoneDetectionFeatureSupported()} should be used instead. + */ + public boolean isGeoTimeZoneDetectionFeatureSupportedInConfig() { + return mGeoDetectionFeatureSupportedInConfig; + } + + /** + * Returns {@code true} if the location-based time zone detection feature is supported on the + * device. This can be used during feature testing on builds that are capable of location time + * zone detection to enable / disable the feature for some users. + */ + public boolean isGeoTimeZoneDetectionFeatureSupported() { + return mGeoDetectionFeatureSupportedInConfig + && isGeoTimeZoneDetectionFeatureSupportedInternal(); + } + + private boolean isGeoTimeZoneDetectionFeatureSupportedInternal() { + final boolean defaultEnabled = true; + return mServerFlags.getBoolean( + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED, + defaultEnabled); + } + + /** + * Returns {@code true} if the primary location time zone provider can be used. + */ + public boolean isPrimaryLocationTimeZoneProviderEnabled() { + return getPrimaryLocationTimeZoneProviderEnabledOverride() + .orElse(isPrimaryLocationTimeZoneProviderEnabledInConfig()); + } + + private boolean isPrimaryLocationTimeZoneProviderEnabledInConfig() { + int providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider; + return getConfigBoolean(providerEnabledConfigId); + } + + @NonNull + private Optional<Boolean> getPrimaryLocationTimeZoneProviderEnabledOverride() { + return mServerFlags.getOptionalBoolean( + ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE); + } + + /** + * Returns {@code true} if the secondary location time zone provider can be used. + */ + public boolean isSecondaryLocationTimeZoneProviderEnabled() { + return getSecondaryLocationTimeZoneProviderEnabledOverride() + .orElse(isSecondaryLocationTimeZoneProviderEnabledInConfig()); + } + + private boolean isSecondaryLocationTimeZoneProviderEnabledInConfig() { + int providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider; + return getConfigBoolean(providerEnabledConfigId); + } + + @NonNull + private Optional<Boolean> getSecondaryLocationTimeZoneProviderEnabledOverride() { + return mServerFlags.getOptionalBoolean( + ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE); + } + + /** + * Returns whether location time zone detection is enabled for users when there's no setting + * value. Intended for use during feature release testing to "opt-in" users that haven't shown + * an explicit preference. + */ + public boolean isGeoDetectionEnabledForUsersByDefault() { + return mServerFlags.getBoolean( + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT, false); + } + + /** + * Returns whether location time zone detection is force enabled/disabled for users. Intended + * for use during feature release testing to force a given state. + */ + @NonNull + public Optional<Boolean> getGeoDetectionSettingEnabledOverride() { + return mServerFlags.getOptionalBoolean( + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE); + } + + /** + * Returns the time to send to a location time zone provider that informs it how long it has + * to return its first time zone suggestion. + */ + @NonNull + public Duration getLocationTimeZoneProviderInitializationTimeout() { + return mServerFlags.getDurationFromMillis( + ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS, + DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT); + } + + /** + * Returns the time added to {@link #getLocationTimeZoneProviderInitializationTimeout()} by the + * server before unilaterally declaring the provider is uncertain. + */ + @NonNull + public Duration getLocationTimeZoneProviderInitializationTimeoutFuzz() { + return mServerFlags.getDurationFromMillis( + ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS, + DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ); + } + + /** + * Returns the time after uncertainty is detected by providers before the location time zone + * manager makes a suggestion to the time zone detector. + */ + @NonNull + public Duration getLocationTimeZoneUncertaintyDelay() { + return mServerFlags.getDurationFromMillis( + ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS, + DEFAULT_PROVIDER_UNCERTAINTY_DELAY); + } + + private boolean getConfigBoolean(int providerEnabledConfigId) { + Resources resources = mContext.getResources(); + return resources.getBoolean(providerEnabledConfigId); + } +} diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java index 203a8a4e02cc..cd220b164851 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java @@ -27,16 +27,21 @@ import android.annotation.NonNull; */ public interface TimeZoneDetectorInternal extends Dumpable.Container { - /** Adds a listener that will be invoked when time zone detection configuration is changed. */ - void addConfigurationListener(ConfigurationChangeListener listener); + /** Adds a listener that will be invoked when {@link ConfigurationInternal} may have changed. */ + void addConfigurationListener(@NonNull ConfigurationChangeListener listener); /** * Removes a listener previously added via {@link * #addConfigurationListener(ConfigurationChangeListener)}. */ - void removeConfigurationListener(ConfigurationChangeListener listener); + void removeConfigurationListener(@NonNull ConfigurationChangeListener listener); - /** Returns the {@link ConfigurationInternal} for the current user. */ + /** + * Returns a snapshot of the {@link ConfigurationInternal} for the current user. This is only a + * snapshot so callers must use {@link #addConfigurationListener(ConfigurationChangeListener)} + * to be notified when it changes. + */ + @NonNull ConfigurationInternal getCurrentUserConfigurationInternal(); /** diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index bd71ddf67094..c20400ae7a4b 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -33,7 +33,6 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; -import android.os.SystemProperties; import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.Slog; @@ -62,27 +61,6 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub static final String TAG = "time_zone_detector"; /** - * A "feature switch" for location-based time zone detection. If this is {@code false}. It is - * initialized and never refreshed; it affects what services are started on boot so consistency - * is important. - */ - @Nullable - private static Boolean sGeoLocationTimeZoneDetectionSupported; - - /** Returns {@code true} if the location-based time zone detection feature is enabled. */ - public static boolean isGeoLocationTimeZoneDetectionSupported(Context context) { - if (sGeoLocationTimeZoneDetectionSupported == null) { - // The config value is expected to be the main switch. Platform developers can also - // enable the feature using a persistent system property. - sGeoLocationTimeZoneDetectionSupported = context.getResources().getBoolean( - com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection) - || SystemProperties.getBoolean( - "persist.sys.location_time_zone_detection_feature_enabled", false); - } - return sGeoLocationTimeZoneDetectionSupported; - } - - /** * Handles the service lifecycle for {@link TimeZoneDetectorService} and * {@link TimeZoneDetectorInternalImpl}. */ @@ -98,11 +76,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub Context context = getContext(); Handler handler = FgThread.getHandler(); - boolean geolocationTimeZoneDetectionSupported = - isGeoLocationTimeZoneDetectionSupported(context); + ServiceConfigAccessor serviceConfigAccessor = + ServiceConfigAccessor.getInstance(context); TimeZoneDetectorStrategy timeZoneDetectorStrategy = - TimeZoneDetectorStrategyImpl.create( - context, handler, geolocationTimeZoneDetectionSupported); + TimeZoneDetectorStrategyImpl.create(context, handler, serviceConfigAccessor); // Create and publish the local service for use by internal callers. TimeZoneDetectorInternal internal = @@ -330,7 +307,8 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub boolean isGeoTimeZoneDetectionSupported() { enforceManageTimeZoneDetectorPermission(); - return isGeoLocationTimeZoneDetectionSupported(mContext); + return ServiceConfigAccessor.getInstance(mContext) + .isGeoTimeZoneDetectionFeatureSupported(); } @Override diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java index 0b1d6d71ea7b..8266f121822e 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java @@ -75,17 +75,21 @@ import android.util.IndentingPrintWriter; public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container { /** - * Sets a listener that will be triggered whenever time zone detection configuration is + * Adds a listener that will be triggered whenever {@link ConfigurationInternal} may have * changed. */ void addConfigChangeListener(@NonNull ConfigurationChangeListener listener); - /** Returns the user's time zone configuration. */ + /** + * Returns a snapshot of the configuration that controls time zone detector behavior for the + * specified user. + */ @NonNull ConfigurationInternal getConfigurationInternal(@UserIdInt int userId); /** - * Returns the configuration that controls time zone detector behavior for the current user. + * Returns a snapshot of the configuration that controls time zone detector behavior for the + * current user. */ @NonNull ConfigurationInternal getCurrentUserConfigurationInternal(); diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index c464b74ca726..d163a0e22320 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -38,7 +38,6 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.timedetector.DeviceConfig; import java.util.ArrayList; import java.util.List; @@ -204,11 +203,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat */ public static TimeZoneDetectorStrategyImpl create( @NonNull Context context, @NonNull Handler handler, - boolean geoDetectionSupported) { + @NonNull ServiceConfigAccessor serviceConfigAccessor) { - DeviceConfig deviceConfig = new DeviceConfig(); - EnvironmentImpl environment = new EnvironmentImpl( - context, handler, deviceConfig, geoDetectionSupported); + Environment environment = new EnvironmentImpl(context, handler, serviceConfigAccessor); return new TimeZoneDetectorStrategyImpl(environment); } diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java index e463ee22452d..98e984d2c3ac 100644 --- a/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java +++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java @@ -19,9 +19,9 @@ package com.android.server.timezonedetector.location; import android.annotation.NonNull; import com.android.server.LocalServices; -import com.android.server.timedetector.DeviceConfig; import com.android.server.timezonedetector.ConfigurationChangeListener; import com.android.server.timezonedetector.ConfigurationInternal; +import com.android.server.timezonedetector.ServiceConfigAccessor; import com.android.server.timezonedetector.TimeZoneDetectorInternal; import java.time.Duration; @@ -33,28 +33,19 @@ import java.util.Objects; */ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment { - // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented - private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1); - // TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented - private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = - Duration.ofSeconds(20); - private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5); - @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal; - @NonNull private final LocationTimeZoneProviderController mController; - @NonNull private final DeviceConfig mDeviceConfig; + @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final ConfigurationChangeListener mConfigurationChangeListener; ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain, - @NonNull DeviceConfig deviceConfig, + @NonNull ServiceConfigAccessor serviceConfigAccessor, @NonNull LocationTimeZoneProviderController controller) { super(threadingDomain); - mController = Objects.requireNonNull(controller); - mDeviceConfig = Objects.requireNonNull(deviceConfig); + mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor); mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class); // Listen for configuration changes. - mConfigurationChangeListener = () -> mThreadingDomain.post(mController::onConfigChanged); + mConfigurationChangeListener = () -> mThreadingDomain.post(controller::onConfigChanged); mTimeZoneDetectorInternal.addConfigurationListener(mConfigurationChangeListener); } @@ -73,24 +64,18 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir @Override @NonNull Duration getProviderInitializationTimeout() { - return mDeviceConfig.getDurationFromMillis( - DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS, - DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT); + return mServiceConfigAccessor.getLocationTimeZoneProviderInitializationTimeout(); } @Override @NonNull Duration getProviderInitializationTimeoutFuzz() { - return mDeviceConfig.getDurationFromMillis( - DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS, - DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ); + return mServiceConfigAccessor.getLocationTimeZoneProviderInitializationTimeoutFuzz(); } @Override @NonNull Duration getUncertaintyDelay() { - return mDeviceConfig.getDurationFromMillis( - DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS, - DEFAULT_PROVIDER_UNCERTAINTY_DELAY); + return mServiceConfigAccessor.getLocationTimeZoneUncertaintyDelay(); } } diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java index 364eaf8dac04..0d1692a8781d 100644 --- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java @@ -21,11 +21,11 @@ import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DI import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE; import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED; import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME; +import static android.app.time.LocationTimeZoneManager.SERVICE_NAME; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.content.res.Resources; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -43,9 +43,8 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.server.FgThread; import com.android.server.SystemService; -import com.android.server.timedetector.DeviceConfig; +import com.android.server.timezonedetector.ServiceConfigAccessor; import com.android.server.timezonedetector.TimeZoneDetectorInternal; -import com.android.server.timezonedetector.TimeZoneDetectorService; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -96,28 +95,31 @@ public class LocationTimeZoneManagerService extends Binder { private LocationTimeZoneManagerService mService; + @NonNull + private final ServiceConfigAccessor mServerConfigAccessor; + public Lifecycle(@NonNull Context context) { super(Objects.requireNonNull(context)); + mServerConfigAccessor = ServiceConfigAccessor.getInstance(context); } @Override public void onStart() { Context context = getContext(); - if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) { + if (mServerConfigAccessor.isGeoTimeZoneDetectionFeatureSupportedInConfig()) { mService = new LocationTimeZoneManagerService(context); // The service currently exposes no LocalService or Binder API, but it extends // Binder and is registered as a binder service so it can receive shell commands. - publishBinderService("location_time_zone_manager", mService); + publishBinderService(SERVICE_NAME, mService); } else { - Slog.i(TAG, getClass() + " is disabled"); + Slog.d(TAG, "Geo time zone detection feature is disabled in config"); } } @Override - public void onBootPhase(int phase) { - Context context = getContext(); - if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) { + public void onBootPhase(@BootPhase int phase) { + if (mServerConfigAccessor.isGeoTimeZoneDetectionFeatureSupportedInConfig()) { if (phase == PHASE_SYSTEM_SERVICES_READY) { // The location service must be functioning after this boot phase. mService.onSystemReady(); @@ -159,6 +161,9 @@ public class LocationTimeZoneManagerService extends Binder { /** The shared lock from {@link #mThreadingDomain}. */ @NonNull private final Object mSharedLock; + @NonNull + private final ServiceConfigAccessor mServiceConfigAccessor; + // Lazily initialized. Can be null if the service has been stopped. @GuardedBy("mSharedLock") private ControllerImpl mLocationTimeZoneDetectorController; @@ -180,24 +185,38 @@ public class LocationTimeZoneManagerService extends Binder { mHandler = FgThread.getHandler(); mThreadingDomain = new HandlerThreadingDomain(mHandler); mSharedLock = mThreadingDomain.getLockObject(); + mServiceConfigAccessor = ServiceConfigAccessor.getInstance(mContext); } + // According to the SystemService docs: All lifecycle methods are called from the system + // server's main looper thread. void onSystemReady() { - // Called on an arbitrary thread during initialization. - synchronized (mSharedLock) { - // TODO(b/152744911): LocationManagerService watches for packages disappearing. Need to - // do anything here? + mServiceConfigAccessor.addListener(this::handleServiceConfigurationChangedOnMainThread); + } - // TODO(b/152744911): LocationManagerService watches for foreground app changes. Need to - // do anything here? - // TODO(b/152744911): LocationManagerService watches screen state. Need to do anything - // here? + private void handleServiceConfigurationChangedOnMainThread() { + // This method is called on the main thread, but service logic takes place on the threading + // domain thread, so we post the work there. + + // The way all service-level configuration changes are handled is to just restart this + // service - this is simple and effective, and service configuration changes should be rare. + mThreadingDomain.post(this::restartIfRequiredOnDomainThread); + } + + private void restartIfRequiredOnDomainThread() { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + // Stop and start the service, waiting until completion. + stopOnDomainThread(); + startOnDomainThread(); } } + // According to the SystemService docs: All lifecycle methods are called from the system + // server's main looper thread. void onSystemThirdPartyAppsCanStart() { - // Called on an arbitrary thread during initialization. We do not want to wait for - // completion as it would delay boot. + // Do not wait for completion as it would delay boot. final boolean waitForCompletion = false; startInternal(waitForCompletion); } @@ -205,6 +224,9 @@ public class LocationTimeZoneManagerService extends Binder { /** * Starts the service during server initialization or during tests after a call to * {@link #stop()}. + * + * <p>Because this method posts work to the {@code mThreadingDomain} thread and waits for + * completion, it cannot be called from the {@code mThreadingDomain} thread. */ void start() { enforceManageTimeZoneDetectorPermission(); @@ -214,28 +236,17 @@ public class LocationTimeZoneManagerService extends Binder { } /** - * Starts the service during server initialization or during tests after a call to - * {@link #stop()}. + * Starts the service during server initialization, if the configuration changes or during tests + * after a call to {@link #stop()}. * * <p>To avoid tests needing to sleep, when {@code waitForCompletion} is {@code true}, this * method will not return until all the system server components have started. + * + * <p>Because this method posts work to the {@code mThreadingDomain} thread, it cannot be + * called from the {@code mThreadingDomain} thread when {@code waitForCompletion} is true. */ private void startInternal(boolean waitForCompletion) { - Runnable runnable = () -> { - synchronized (mSharedLock) { - if (mLocationTimeZoneDetectorController == null) { - LocationTimeZoneProvider primary = createPrimaryProvider(); - LocationTimeZoneProvider secondary = createSecondaryProvider(); - mLocationTimeZoneDetectorController = - new ControllerImpl(mThreadingDomain, primary, secondary); - DeviceConfig deviceConfig = new DeviceConfig(); - mEnvironment = new ControllerEnvironmentImpl( - mThreadingDomain, deviceConfig, mLocationTimeZoneDetectorController); - ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain); - mLocationTimeZoneDetectorController.initialize(mEnvironment, callback); - } - } - }; + Runnable runnable = this::startOnDomainThread; if (waitForCompletion) { mThreadingDomain.postAndWait(runnable, BLOCKING_OP_WAIT_DURATION_MILLIS); } else { @@ -243,11 +254,38 @@ public class LocationTimeZoneManagerService extends Binder { } } + private void startOnDomainThread() { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + if (!mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) { + debugLog("Not starting " + SERVICE_NAME + ": it is disabled in service config"); + return; + } + + if (mLocationTimeZoneDetectorController == null) { + LocationTimeZoneProvider primary = createPrimaryProvider(); + LocationTimeZoneProvider secondary = createSecondaryProvider(); + + ControllerImpl controller = + new ControllerImpl(mThreadingDomain, primary, secondary); + ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl( + mThreadingDomain, mServiceConfigAccessor, controller); + ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain); + controller.initialize(environment, callback); + + mEnvironment = environment; + mLocationTimeZoneDetectorController = controller; + } + } + } + + @NonNull private LocationTimeZoneProvider createPrimaryProvider() { LocationTimeZoneProviderProxy proxy; if (isProviderInSimulationMode(PRIMARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); - } else if (isProviderDisabled(PRIMARY_PROVIDER_NAME)) { + } else if (!isProviderEnabled(PRIMARY_PROVIDER_NAME)) { proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { proxy = new RealLocationTimeZoneProviderProxy( @@ -262,11 +300,12 @@ public class LocationTimeZoneManagerService extends Binder { return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy); } + @NonNull private LocationTimeZoneProvider createSecondaryProvider() { LocationTimeZoneProviderProxy proxy; if (isProviderInSimulationMode(SECONDARY_PROVIDER_NAME)) { proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain); - } else if (isProviderDisabled(SECONDARY_PROVIDER_NAME)) { + } else if (!isProviderEnabled(SECONDARY_PROVIDER_NAME)) { proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain); } else { proxy = new RealLocationTimeZoneProviderProxy( @@ -282,33 +321,27 @@ public class LocationTimeZoneManagerService extends Binder { } /** Used for bug triage and in tests to simulate provider events. */ - private boolean isProviderInSimulationMode(String providerName) { + private boolean isProviderInSimulationMode(@NonNull String providerName) { return isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_SIMULATED); } - /** Used for bug triage, tests and experiments to remove a provider. */ - private boolean isProviderDisabled(String providerName) { - return !isProviderEnabledInConfig(providerName) - || isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED); - } + /** Used for bug triage, and by tests and experiments to remove a provider. */ + private boolean isProviderEnabled(@NonNull String providerName) { + if (isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED)) { + return false; + } - private boolean isProviderEnabledInConfig(String providerName) { - int providerEnabledConfigId; switch (providerName) { case PRIMARY_PROVIDER_NAME: { - providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider; - break; + return mServiceConfigAccessor.isPrimaryLocationTimeZoneProviderEnabled(); } case SECONDARY_PROVIDER_NAME: { - providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider; - break; + return mServiceConfigAccessor.isSecondaryLocationTimeZoneProviderEnabled(); } default: { throw new IllegalArgumentException(providerName); } } - Resources resources = mContext.getResources(); - return resources.getBoolean(providerEnabledConfigId); } private boolean isProviderModeOverrideSet(@NonNull String providerName, @NonNull String mode) { @@ -326,22 +359,29 @@ public class LocationTimeZoneManagerService extends Binder { } /** - * Stops the service for tests. To avoid tests needing to sleep, this method will not return - * until all the system server components have stopped. + * Stops the service for tests and other rare cases. To avoid tests needing to sleep, this + * method will not return until all the system server components have stopped. + * + * <p>Because this method posts work to the {@code mThreadingDomain} thread and waits it cannot + * be called from the {@code mThreadingDomain} thread. */ void stop() { enforceManageTimeZoneDetectorPermission(); - mThreadingDomain.postAndWait(() -> { - synchronized (mSharedLock) { - if (mLocationTimeZoneDetectorController != null) { - mLocationTimeZoneDetectorController.destroy(); - mLocationTimeZoneDetectorController = null; - mEnvironment.destroy(); - mEnvironment = null; - } + mThreadingDomain.postAndWait(this::stopOnDomainThread, BLOCKING_OP_WAIT_DURATION_MILLIS); + } + + private void stopOnDomainThread() { + mThreadingDomain.assertCurrentThread(); + + synchronized (mSharedLock) { + if (mLocationTimeZoneDetectorController != null) { + mLocationTimeZoneDetectorController.destroy(); + mLocationTimeZoneDetectorController = null; + mEnvironment.destroy(); + mEnvironment = null; } - }, BLOCKING_OP_WAIT_DURATION_MILLIS); + } } @Override diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java index b53150c729bc..bdf4a70a6a2b 100644 --- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java @@ -21,6 +21,7 @@ import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DI import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE; import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED; import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME; +import static android.app.time.LocationTimeZoneManager.SERVICE_NAME; import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_DUMP_STATE; import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_RECORD_PROVIDER_STATES; import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND; @@ -102,7 +103,7 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand { @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); - pw.println("Location Time Zone Manager (location_time_zone_manager) commands for tests:"); + pw.printf("Location Time Zone Manager (%s) commands for tests:\n", SERVICE_NAME); pw.println(" help"); pw.println(" Print this help text."); pw.printf(" %s\n", SHELL_COMMAND_START); diff --git a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java index 38211efc1c63..531c62c5b4e9 100644 --- a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java +++ b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java @@ -25,7 +25,6 @@ import static com.android.server.timezonedetector.location.LocationTimeZoneManag import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -41,6 +40,7 @@ import android.util.IndentingPrintWriter; import com.android.internal.annotations.GuardedBy; import com.android.server.ServiceWatcher; +import com.android.server.ServiceWatcher.BoundService; import java.util.Objects; import java.util.function.Predicate; @@ -123,7 +123,7 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy { return resolves; } - private void onBind(IBinder binder, ComponentName componentName) { + private void onBind(IBinder binder, BoundService boundService) { mThreadingDomain.assertCurrentThread(); synchronized (mSharedLock) { diff --git a/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java index 14820319d9df..e8386bc22403 100644 --- a/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java +++ b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java @@ -63,8 +63,6 @@ final class TimeZoneProviderRequest { return mSendUpdates; } - // TODO(b/152744911) - once there are a couple of implementations, decide whether this needs to - // be passed to the TimeZoneProviderService and remove if it is not useful. /** * Returns the maximum time that the provider is allowed to initialize before it is expected to * send an event of any sort. Only valid when {@link #sendUpdates()} is {@code true}. Failure to diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index da3e48ad2c0d..4fbc79507b95 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -703,7 +703,7 @@ public class TrustManagerService extends SystemService { if (changed) { dispatchDeviceLocked(userId, locked); - mAuthorizationService.onLockScreenEvent(locked, userId, null); + Authorization.onLockScreenEvent(locked, userId, null); KeyStore.getInstance().onUserLockedStateChanged(userId, locked); // Also update the user's profiles who have unified challenge, since they // share the same unlocked state (see {@link #isDeviceLocked(int)}) @@ -1261,7 +1261,7 @@ public class TrustManagerService extends SystemService { mDeviceLockedForUser.put(userId, locked); } - mAuthorizationService.onLockScreenEvent(locked, userId, null); + Authorization.onLockScreenEvent(locked, userId, null); KeyStore.getInstance().onUserLockedStateChanged(userId, locked); if (locked) { diff --git a/services/core/java/com/android/server/utils/WatchedLongSparseArray.java b/services/core/java/com/android/server/utils/WatchedLongSparseArray.java new file mode 100644 index 000000000000..bf23de12ffef --- /dev/null +++ b/services/core/java/com/android/server/utils/WatchedLongSparseArray.java @@ -0,0 +1,418 @@ +/* + * 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.utils; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.LongSparseArray; + +/** + * A watched variant of {@link LongSparseArray}. If a {@link Watchable} is stored in the + * array, the array registers with the {@link Watchable}. The array registers only once + * with each {@link Watchable} no matter how many times the {@link Watchable} is stored in + * the array. + * @param <E> The element type, stored in the array. + */ +public class WatchedLongSparseArray<E> extends WatchableImpl + implements Snappable { + + // The storage + private final LongSparseArray<E> mStorage; + + // If true, the array is watching its children + private volatile boolean mWatching = false; + + // The local observer + private final Watcher mObserver = new Watcher() { + @Override + public void onChange(@Nullable Watchable o) { + WatchedLongSparseArray.this.dispatchChange(o); + } + }; + + /** + * A private convenience function that notifies registered listeners that an element + * has been added to or removed from the array. The what parameter is the array itself. + */ + private void onChanged() { + dispatchChange(this); + } + + /** + * A convenience function. Register the object if it is {@link Watchable} and if the + * array is currently watching. Note that the watching flag must be true if this + * function is to succeed. + */ + private void registerChild(Object o) { + if (mWatching && o instanceof Watchable) { + ((Watchable) o).registerObserver(mObserver); + } + } + + /** + * A convenience function. Unregister the object if it is {@link Watchable} and if + * the array is currently watching. Note that the watching flag must be true if this + * function is to succeed. + */ + private void unregisterChild(Object o) { + if (mWatching && o instanceof Watchable) { + ((Watchable) o).unregisterObserver(mObserver); + } + } + + /** + * A convenience function. Unregister the object if it is {@link Watchable}, if the array is + * currently watching, and if the storage does not contain the object. Note that the watching + * flag must be true if this function is to succeed. This must be called after an object has + * been removed from the storage. + */ + private void unregisterChildIf(Object o) { + if (mWatching && o instanceof Watchable) { + if (mStorage.indexOfValue((E) o) == -1) { + ((Watchable) o).unregisterObserver(mObserver); + } + } + } + + /** + * Register a {@link Watcher} with the array. If this is the first Watcher than any + * array values that are {@link Watchable} are registered to the array itself. + */ + @Override + public void registerObserver(@NonNull Watcher observer) { + super.registerObserver(observer); + if (registeredObserverCount() == 1) { + // The watching flag must be set true before any children are registered. + mWatching = true; + final int end = mStorage.size(); + for (int i = 0; i < end; i++) { + registerChild(mStorage.valueAt(i)); + } + } + } + + /** + * Unregister a {@link Watcher} from the array. If this is the last Watcher than any + * array values that are {@link Watchable} are unregistered to the array itself. + */ + @Override + public void unregisterObserver(@NonNull Watcher observer) { + super.unregisterObserver(observer); + if (registeredObserverCount() == 0) { + final int end = mStorage.size(); + for (int i = 0; i < end; i++) { + unregisterChild(mStorage.valueAt(i)); + } + // The watching flag must be true while children are unregistered. + mWatching = false; + } + } + + /** + * Creates a new WatchedSparseArray containing no mappings. + */ + public WatchedLongSparseArray() { + mStorage = new LongSparseArray(); + } + + /** + * Creates a new WatchedSparseArray containing no mappings that + * will not require any additional memory allocation to store the + * specified number of mappings. If you supply an initial capacity of + * 0, the sparse array will be initialized with a light-weight + * representation not requiring any additional array allocations. + */ + public WatchedLongSparseArray(int initialCapacity) { + mStorage = new LongSparseArray(initialCapacity); + } + + /** + * Create a {@link WatchedLongSparseArray} from a {@link LongSparseArray}. + */ + public WatchedLongSparseArray(@NonNull LongSparseArray<E> c) { + mStorage = c.clone(); + } + + /** + * The copy constructor does not copy the watcher data. + */ + public WatchedLongSparseArray(@NonNull WatchedLongSparseArray<E> r) { + mStorage = r.mStorage.clone(); + } + + /** + * Make <this> a copy of src. Any data in <this> is discarded. + */ + public void copyFrom(@NonNull LongSparseArray<E> src) { + clear(); + final int end = src.size(); + for (int i = 0; i < end; i++) { + put(src.keyAt(i), src.valueAt(i)); + } + } + + /** + * Make dst a copy of <this>. Any previous data in dst is discarded. + */ + public void copyTo(@NonNull LongSparseArray<E> dst) { + dst.clear(); + final int end = size(); + for (int i = 0; i < end; i++) { + dst.put(keyAt(i), valueAt(i)); + } + } + + /** + * Return the underlying storage. This breaks the wrapper but is necessary when + * passing the array to distant methods. + */ + public LongSparseArray<E> untrackedStorage() { + return mStorage; + } + + /** + * Gets the Object mapped from the specified key, or <code>null</code> + * if no such mapping has been made. + */ + public E get(long key) { + return mStorage.get(key); + } + + /** + * Gets the Object mapped from the specified key, or the specified Object + * if no such mapping has been made. + */ + public E get(long key, E valueIfKeyNotFound) { + return mStorage.get(key, valueIfKeyNotFound); + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(long key) { + E old = mStorage.get(key, null); + mStorage.delete(key); + unregisterChildIf(old); + onChanged(); + + } + + /** + * Alias for {@link #delete(long)}. + */ + public void remove(long key) { + delete(key); + } + + /** + * Removes the mapping at the specified index. + * + * <p>For indices outside of the range <code>0...size()-1</code>, an + * {@link ArrayIndexOutOfBoundsException} is thrown.</p> + */ + public void removeAt(int index) { + E old = mStorage.valueAt(index); + mStorage.removeAt(index); + unregisterChildIf(old); + onChanged(); + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(long key, E value) { + E old = mStorage.get(key); + mStorage.put(key, value); + unregisterChildIf(old); + registerChild(value); + onChanged(); + } + + /** + * Returns the number of key-value mappings that this LongSparseArray + * currently stores. + */ + public int size() { + return mStorage.size(); + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the key from the <code>index</code>th key-value mapping that this + * LongSparseArray stores. + * + * <p>The keys corresponding to indices in ascending order are guaranteed to + * be in ascending order, e.g., <code>keyAt(0)</code> will return the + * smallest key and <code>keyAt(size()-1)</code> will return the largest + * key.</p> + * + * <p>For indices outside of the range <code>0...size()-1</code>, an + * {@link ArrayIndexOutOfBoundsException} is thrown.</p> + */ + public long keyAt(int index) { + return mStorage.keyAt(index); + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the value from the <code>index</code>th key-value mapping that this + * LongSparseArray stores. + * + * <p>The values corresponding to indices in ascending order are guaranteed + * to be associated with keys in ascending order, e.g., + * <code>valueAt(0)</code> will return the value associated with the + * smallest key and <code>valueAt(size()-1)</code> will return the value + * associated with the largest key.</p> + * + * <p>For indices outside of the range <code>0...size()-1</code>, an + * {@link ArrayIndexOutOfBoundsException} is thrown.</p> + */ + public E valueAt(int index) { + return mStorage.valueAt(index); + } + + /** + * Given an index in the range <code>0...size()-1</code>, sets a new + * value for the <code>index</code>th key-value mapping that this + * LongSparseArray stores. + * + * <p>For indices outside of the range <code>0...size()-1</code>, an + * {@link ArrayIndexOutOfBoundsException} is thrown.</p> + */ + public void setValueAt(int index, E value) { + E old = mStorage.valueAt(index); + mStorage.setValueAt(index, value); + unregisterChildIf(old); + registerChild(value); + onChanged(); + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(long key) { + return mStorage.indexOfKey(key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(E value) { + return mStorage.indexOfValue(value); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * <p>Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + * <p>Note also that this method uses {@code equals} unlike {@code indexOfValue}. + * @hide + */ + public int indexOfValueByValue(E value) { + return mStorage.indexOfValueByValue(value); + } + + /** + * Removes all key-value mappings from this LongSparseArray. + */ + public void clear() { + final int end = mStorage.size(); + for (int i = 0; i < end; i++) { + unregisterChild(mStorage.valueAt(i)); + } + mStorage.clear(); + onChanged(); + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(long key, E value) { + mStorage.append(key, value); + registerChild(value); + onChanged(); + } + + /** + * {@inheritDoc} + * + * <p>This implementation composes a string by iterating over its mappings. If + * this map contains itself as a value, the string "(this Map)" + * will appear in its place. + */ + @Override + public String toString() { + return mStorage.toString(); + } + + /** + * Create a copy of the array. If the element is a subclass of Snapper then the copy + * contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The returned snapshot is immutable. + * @return A new array whose elements are the elements of <this>. + */ + public WatchedLongSparseArray<E> snapshot() { + WatchedLongSparseArray<E> l = new WatchedLongSparseArray<>(size()); + snapshot(l, this); + return l; + } + + /** + * Make <this> a snapshot of the argument. Note that <this> is immutable when the + * method returns. <this> must be empty when the function is called. + * @param r The source array, which is copied into <this> + */ + public void snapshot(@NonNull WatchedLongSparseArray<E> r) { + snapshot(this, r); + } + + /** + * Make the destination a copy of the source. If the element is a subclass of Snapper then the + * copy contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The destination must be initially empty. Upon return, the destination is + * immutable. + * @param dst The destination array. It must be empty. + * @param src The source array. It is not modified. + */ + public static <E> void snapshot(@NonNull WatchedLongSparseArray<E> dst, + @NonNull WatchedLongSparseArray<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + final E val = Snapshots.maybeSnapshot(src.valueAt(i)); + final long key = src.keyAt(i); + dst.put(key, val); + } + dst.seal(); + } + +} diff --git a/services/core/java/com/android/server/vcn/Android.bp b/services/core/java/com/android/server/vcn/Android.bp index 5ed204fd7640..ab5da3ee45b4 100644 --- a/services/core/java/com/android/server/vcn/Android.bp +++ b/services/core/java/com/android/server/vcn/Android.bp @@ -1,4 +1,13 @@ +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-vcn-util-sources", srcs: ["util/**/*.java"], -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index 02a597e9aa69..6ad30b544257 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -19,10 +19,12 @@ package com.android.server.vcn; import static com.android.server.VcnManagementService.VDBG; import android.annotation.NonNull; +import android.annotation.Nullable; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.vcn.VcnConfig; import android.net.vcn.VcnGatewayConnectionConfig; +import android.net.vcn.VcnManager.VcnErrorCode; import android.os.Handler; import android.os.Message; import android.os.ParcelUuid; @@ -30,7 +32,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; -import com.android.server.VcnManagementService.VcnSafeModeCallback; +import com.android.server.VcnManagementService.VcnCallback; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import java.util.Collections; @@ -97,7 +99,7 @@ public class Vcn extends Handler { @NonNull private final ParcelUuid mSubscriptionGroup; @NonNull private final Dependencies mDeps; @NonNull private final VcnNetworkRequestListener mRequestListener; - @NonNull private final VcnSafeModeCallback mVcnSafeModeCallback; + @NonNull private final VcnCallback mVcnCallback; @NonNull private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections = @@ -125,14 +127,8 @@ public class Vcn extends Handler { @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, - @NonNull VcnSafeModeCallback vcnSafeModeCallback) { - this( - vcnContext, - subscriptionGroup, - config, - snapshot, - vcnSafeModeCallback, - new Dependencies()); + @NonNull VcnCallback vcnCallback) { + this(vcnContext, subscriptionGroup, config, snapshot, vcnCallback, new Dependencies()); } @VisibleForTesting(visibility = Visibility.PRIVATE) @@ -141,13 +137,12 @@ public class Vcn extends Handler { @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, - @NonNull VcnSafeModeCallback vcnSafeModeCallback, + @NonNull VcnCallback vcnCallback, @NonNull Dependencies deps) { super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); mVcnContext = vcnContext; mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); - mVcnSafeModeCallback = - Objects.requireNonNull(vcnSafeModeCallback, "Missing vcnSafeModeCallback"); + mVcnCallback = Objects.requireNonNull(vcnCallback, "Missing vcnCallback"); mDeps = Objects.requireNonNull(deps, "Missing deps"); mRequestListener = new VcnNetworkRequestListener(); @@ -246,7 +241,7 @@ public class Vcn extends Handler { private void handleEnterSafeMode() { handleTeardown(); - mVcnSafeModeCallback.onEnteredSafeMode(); + mVcnCallback.onEnteredSafeMode(); } private void handleNetworkRequested( @@ -337,6 +332,13 @@ public class Vcn extends Handler { public interface VcnGatewayStatusCallback { /** Called by a VcnGatewayConnection to indicate that it has entered safe mode. */ void onEnteredSafeMode(); + + /** Callback by a VcnGatewayConnection to indicate that an error occurred. */ + void onGatewayConnectionError( + @NonNull int[] networkCapabilities, + @VcnErrorCode int errorCode, + @Nullable String exceptionClass, + @Nullable String exceptionMessage); } private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback { @@ -344,6 +346,16 @@ public class Vcn extends Handler { public void onEnteredSafeMode() { sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE)); } + + @Override + public void onGatewayConnectionError( + @NonNull int[] networkCapabilities, + @VcnErrorCode int errorCode, + @Nullable String exceptionClass, + @Nullable String exceptionMessage) { + mVcnCallback.onGatewayConnectionError( + networkCapabilities, errorCode, exceptionClass, exceptionMessage); + } } /** External dependencies used by Vcn, for injection in tests */ diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 59cb6aca7668..06748a3aa2d1 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -22,6 +22,9 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR; +import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR; +import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR; import static com.android.server.VcnManagementService.VDBG; @@ -52,7 +55,9 @@ import android.net.ipsec.ike.IkeSession; import android.net.ipsec.ike.IkeSessionCallback; import android.net.ipsec.ike.IkeSessionConfiguration; import android.net.ipsec.ike.IkeSessionParams; +import android.net.ipsec.ike.exceptions.AuthenticationFailedException; import android.net.ipsec.ike.exceptions.IkeException; +import android.net.ipsec.ike.exceptions.IkeInternalException; import android.net.ipsec.ike.exceptions.IkeProtocolException; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnTransportInfo; @@ -951,15 +956,68 @@ public class VcnGatewayConnection extends StateMachine { removeEqualMessages(EVENT_SAFE_MODE_TIMEOUT_EXCEEDED); } - private void sessionLost(int token, @Nullable Exception exception) { + private void sessionLostWithoutCallback(int token, @Nullable Exception exception) { sendMessageAndAcquireWakeLock( EVENT_SESSION_LOST, token, new EventSessionLostInfo(exception)); } + private void sessionLost(int token, @Nullable Exception exception) { + // Only notify mGatewayStatusCallback if the session was lost with an error. All + // authentication and DNS failures are sent through + // IkeSessionCallback.onClosedExceptionally(), which calls sessionClosed() + if (exception != null) { + mGatewayStatusCallback.onGatewayConnectionError( + mConnectionConfig.getRequiredUnderlyingCapabilities(), + VCN_ERROR_CODE_INTERNAL_ERROR, + RuntimeException.class.getName(), + "Received " + + exception.getClass().getSimpleName() + + " with message: " + + exception.getMessage()); + } + + sessionLostWithoutCallback(token, exception); + } + + private void notifyStatusCallbackForSessionClosed(@NonNull Exception exception) { + final int errorCode; + final String exceptionClass; + final String exceptionMessage; + + if (exception instanceof AuthenticationFailedException) { + errorCode = VCN_ERROR_CODE_CONFIG_ERROR; + exceptionClass = exception.getClass().getName(); + exceptionMessage = exception.getMessage(); + } else if (exception instanceof IkeInternalException + && exception.getCause() instanceof IOException) { + errorCode = VCN_ERROR_CODE_NETWORK_ERROR; + exceptionClass = IOException.class.getName(); + exceptionMessage = exception.getCause().getMessage(); + } else { + errorCode = VCN_ERROR_CODE_INTERNAL_ERROR; + exceptionClass = RuntimeException.class.getName(); + exceptionMessage = + "Received " + + exception.getClass().getSimpleName() + + " with message: " + + exception.getMessage(); + } + + mGatewayStatusCallback.onGatewayConnectionError( + mConnectionConfig.getRequiredUnderlyingCapabilities(), + errorCode, + exceptionClass, + exceptionMessage); + } + private void sessionClosed(int token, @Nullable Exception exception) { + if (exception != null) { + notifyStatusCallbackForSessionClosed(exception); + } + // SESSION_LOST MUST be sent before SESSION_CLOSED to ensure that the SM moves to the // Disconnecting state. - sessionLost(token, exception); + sessionLostWithoutCallback(token, exception); sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, token); } @@ -1084,6 +1142,8 @@ public class VcnGatewayConnection extends StateMachine { } protected void handleDisconnectRequested(String msg) { + // TODO(b/180526152): notify VcnStatusCallback for Network loss + Slog.v(TAG, "Tearing down. Cause: " + msg); mIsRunning = false; @@ -1228,6 +1288,8 @@ public class VcnGatewayConnection extends StateMachine { String reason = ((EventDisconnectRequestedInfo) msg.obj).reason; if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) { + // TODO(b/180526152): notify VcnStatusCallback for Network loss + // Will trigger EVENT_SESSION_CLOSED immediately. mIkeSession.kill(); break; @@ -1573,8 +1635,7 @@ public class VcnGatewayConnection extends StateMachine { @Override protected void exitState() { - // Attempt to set the safe mode alarm - this requires the Vcn Network being validated - // while in ConnectedState (which cancels the previous alarm) + // Will only set a new alarm if no safe mode alarm is currently scheduled. setSafeModeAlarm(); } } @@ -1710,8 +1771,6 @@ public class VcnGatewayConnection extends StateMachine { } } - // TODO: Make a VcnNetworkSpecifier, and match all underlying subscription IDs. - return builder.build(); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 5697564ce93f..370d921de2af 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -31,6 +31,7 @@ import android.annotation.NonNull; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.ILocalWallpaperColorConsumer; import android.app.IWallpaperManager; import android.app.IWallpaperManagerCallback; import android.app.PendingIntent; @@ -59,6 +60,7 @@ import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Color; import android.graphics.Rect; +import android.graphics.RectF; import android.hardware.display.DisplayManager; import android.os.Binder; import android.os.Bundle; @@ -85,7 +87,10 @@ import android.service.wallpaper.IWallpaperService; import android.service.wallpaper.WallpaperService; import android.system.ErrnoException; import android.system.Os; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.EventLog; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -105,7 +110,6 @@ import com.android.server.EventLogTags; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.pm.UserManagerInternal; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.wm.WindowManagerInternal; @@ -136,6 +140,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private static final String TAG = "WallpaperManagerService"; private static final boolean DEBUG = false; private static final boolean DEBUG_LIVE = true; + private static final @NonNull RectF LOCAL_COLOR_BOUNDS = + new RectF(0, 0, 1, 1); public static class Lifecycle extends SystemService { private IWallpaperManagerService mService; @@ -866,6 +872,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private final SparseBooleanArray mUserRestorecon = new SparseBooleanArray(); private int mCurrentUserId = UserHandle.USER_NULL; private boolean mInAmbientMode; + private ArrayMap<IBinder, ArraySet<RectF>> mLocalColorCallbackAreas = + new ArrayMap<>(); + private ArrayMap<RectF, RemoteCallbackList<ILocalWallpaperColorConsumer>> + mLocalColorAreaCallbacks = new ArrayMap<>(); + private ArrayMap<Integer, ArraySet<RectF>> mLocalColorDisplayIdAreas = new ArrayMap<>(); + private ArrayMap<IBinder, Integer> mLocalColorCallbackDisplayId = new ArrayMap<>(); static class WallpaperData { @@ -1276,6 +1288,32 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } @Override + public void onLocalWallpaperColorsChanged(RectF area, WallpaperColors colors, + int displayId) { + forEachDisplayConnector(displayConnector -> { + if (displayConnector.mDisplayId == displayId) { + RemoteCallbackList<ILocalWallpaperColorConsumer> callbacks; + ArrayMap<IBinder, Integer> callbackDisplayIds; + synchronized (mLock) { + callbacks = mLocalColorAreaCallbacks.get(area); + callbackDisplayIds = new ArrayMap<>(mLocalColorCallbackDisplayId); + } + if (callbacks == null) return; + callbacks.broadcast(c -> { + try { + int targetDisplayId = + callbackDisplayIds.get(c.asBinder()); + if (targetDisplayId == displayId) c.onColorsChanged(area, colors); + } catch (RemoteException e) { + e.printStackTrace(); + } + }); + } + }); + } + + + @Override public void onServiceDisconnected(ComponentName name) { synchronized (mLock) { Slog.w(TAG, "Wallpaper service gone: " + name); @@ -1437,6 +1475,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } catch (RemoteException e) { Slog.w(TAG, "Failed to request wallpaper colors", e); } + + ArraySet<RectF> areas = mLocalColorDisplayIdAreas.get(displayId); + if (areas != null && areas.size() != 0) { + try { + connector.mEngine.addLocalColorsAreas(new ArrayList<>(areas)); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to register local colors areas", e); + } + } } } @@ -2340,6 +2387,115 @@ public class WallpaperManagerService extends IWallpaperManager.Stub return true; } + private IWallpaperEngine getEngine(int which, int userId, int displayId) { + WallpaperData wallpaperData = findWallpaperAtDisplay(userId, displayId); + if (wallpaperData == null) return null; + IWallpaperEngine engine = null; + synchronized (mLock) { + for (int i = 0; i < wallpaperData.connection.mDisplayConnector.size(); i++) { + int id = wallpaperData.connection.mDisplayConnector.get(i).mDisplayId; + int currentWhich = wallpaperData.connection.mDisplayConnector.get(i).mDisplayId; + if (id != displayId && currentWhich != which) continue; + engine = wallpaperData.connection.mDisplayConnector.get(i).mEngine; + break; + } + } + return engine; + } + + @Override + public void addOnLocalColorsChangedListener(@NonNull ILocalWallpaperColorConsumer callback, + @NonNull List<RectF> regions, int which, int userId, int displayId) + throws RemoteException { + if (which != FLAG_LOCK && which != FLAG_SYSTEM) { + throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM"); + } + IWallpaperEngine engine = getEngine(which, userId, displayId); + if (engine == null) return; + ArrayList<RectF> validAreas = new ArrayList<>(regions.size()); + synchronized (mLock) { + ArraySet<RectF> areas = mLocalColorCallbackAreas.get(callback); + if (areas == null) areas = new ArraySet<>(regions.size()); + areas.addAll(regions); + mLocalColorCallbackAreas.put(callback.asBinder(), areas); + } + for (int i = 0; i < regions.size(); i++) { + if (!LOCAL_COLOR_BOUNDS.contains(regions.get(i))) { + continue; + } + RemoteCallbackList callbacks; + synchronized (mLock) { + callbacks = mLocalColorAreaCallbacks.get( + regions.get(i)); + if (callbacks == null) { + callbacks = new RemoteCallbackList(); + mLocalColorAreaCallbacks.put(regions.get(i), callbacks); + } + mLocalColorCallbackDisplayId.put(callback.asBinder(), displayId); + ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId); + if (displayAreas == null) { + displayAreas = new ArraySet<>(1); + mLocalColorDisplayIdAreas.put(displayId, displayAreas); + } + displayAreas.add(regions.get(i)); + } + validAreas.add(regions.get(i)); + callbacks.register(callback); + } + engine.addLocalColorsAreas(validAreas); + } + + @Override + public void removeOnLocalColorsChangedListener( + @NonNull ILocalWallpaperColorConsumer callback, int which, int userId, + int displayId) throws RemoteException { + if (which != FLAG_LOCK && which != FLAG_SYSTEM) { + throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM"); + } + final UserHandle callingUser = Binder.getCallingUserHandle(); + if (callingUser.getIdentifier() != userId) { + throw new SecurityException("calling user id does not match"); + } + final long identity = Binder.clearCallingIdentity(); + ArrayList<RectF> removeAreas = new ArrayList<>(); + ArrayList<Pair<RemoteCallbackList, ILocalWallpaperColorConsumer>> + callbacksToRemove = new ArrayList<>(); + try { + synchronized (mLock) { + ArraySet<RectF> areas = mLocalColorCallbackAreas.remove(callback.asBinder()); + mLocalColorCallbackDisplayId.remove(callback.asBinder()); + if (areas == null) areas = new ArraySet<>(); + for (RectF area : areas) { + RemoteCallbackList callbacks = mLocalColorAreaCallbacks.get(area); + if (callbacks == null) continue; + callbacksToRemove.add(new Pair<>(callbacks, callback)); + if (callbacks.getRegisteredCallbackCount() == 0) { + mLocalColorAreaCallbacks.remove(area); + removeAreas.add(area); + } + ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId); + if (displayAreas != null) { + displayAreas.remove(area); + } + } + } + for (int i = 0; i < callbacksToRemove.size(); i++) { + Pair<RemoteCallbackList, ILocalWallpaperColorConsumer> + pair = callbacksToRemove.get(i); + pair.first.unregister(pair.second); + } + } catch (Exception e) { + // ignore any exception + } finally { + Binder.restoreCallingIdentity(identity); + } + + if (removeAreas.size() == 0) return; + IWallpaperEngine engine = getEngine(which, userId, displayId); + if (engine == null) return; + engine.removeLocalColorsAreas(removeAreas); + } + @Override public WallpaperColors getWallpaperColors(int which, int userId, int displayId) throws RemoteException { diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index c63a0f093dea..c830ba9b61dd 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -43,6 +43,7 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.IActivityClientController; +import android.app.IRequestFinishCallback; import android.app.PictureInPictureParams; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.EnterPipRequestedItem; @@ -50,6 +51,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ParceledListSlice; +import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.os.Binder; import android.os.Bundle; @@ -232,7 +235,10 @@ class ActivityClientController extends IActivityClientController.Stub { public void activityRelaunched(IBinder token) { final long origId = Binder.clearCallingIdentity(); synchronized (mGlobalLock) { - mTaskSupervisor.activityRelaunchedLocked(token); + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r != null) { + r.finishRelaunching(); + } } Binder.restoreCallingIdentity(origId); } @@ -802,7 +808,7 @@ class ActivityClientController extends IActivityClientController.Stub { if (rootTask.inFreeformWindowingMode()) { rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - } else if (!mService.mSizeCompatFreeform && r.inSizeCompatMode()) { + } else if (!mService.mSupportsNonResizableMultiWindow && r.inSizeCompatMode()) { throw new IllegalStateException("Size-compat windows are currently not" + "freeform-enabled"); } else if (rootTask.getParent().inFreeformWindowingMode()) { @@ -1124,24 +1130,78 @@ class ActivityClientController extends IActivityClientController.Stub { } @Override - public void onBackPressedOnTaskRoot(IBinder token) { + public void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) { final long origId = Binder.clearCallingIdentity(); try { + final Intent baseActivityIntent; + final boolean launchedFromHome; + synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); - if (r == null) { - return; - } + if (r == null) return; + if (mService.mWindowOrganizerController.mTaskOrganizerController .handleInterceptBackPressedOnTaskRoot(r.getRootTask())) { // This task is handled by a task organizer that has requested the back pressed // callback. - } else { - moveActivityTaskToBack(token, false /* nonRoot */); + return; } + + final Intent baseIntent = r.getTask().getBaseIntent(); + final boolean activityIsBaseActivity = baseIntent != null + && r.mActivityComponent.equals(baseIntent.getComponent()); + baseActivityIntent = activityIsBaseActivity ? r.intent : null; + launchedFromHome = r.launchedFromHomeProcess; + } + + // If the activity is one of the main entry points for the application, then we should + // refrain from finishing the activity and instead move it to the back to keep it in + // memory. The requirements for this are: + // 1. The current activity is the base activity for the task. + // 2. a. If the activity was launched by the home process, we trust that its intent + // was resolved, so we check if the it is a main intent for the application. + // b. Otherwise, we query Package Manager to verify whether the activity is a + // launcher activity for the application. + if (baseActivityIntent != null + && ((launchedFromHome && ActivityRecord.isMainIntent(baseActivityIntent)) + || isLauncherActivity(baseActivityIntent.getComponent()))) { + moveActivityTaskToBack(token, false /* nonRoot */); + return; + } + + // The default option for handling the back button is to finish the Activity. + try { + callback.requestFinish(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to invoke request finish callback", e); } } finally { Binder.restoreCallingIdentity(origId); } } + + /** + * Queries PackageManager to see if the given activity is one of the main entry point for the + * application. This should not be called with the WM lock held. + */ + @SuppressWarnings("unchecked") + private boolean isLauncherActivity(@NonNull ComponentName activity) { + final Intent queryIntent = new Intent(Intent.ACTION_MAIN); + queryIntent.addCategory(Intent.CATEGORY_LAUNCHER); + queryIntent.setPackage(activity.getPackageName()); + try { + final ParceledListSlice<ResolveInfo> resolved = + mService.getPackageManager().queryIntentActivities( + queryIntent, null, 0, mContext.getUserId()); + if (resolved == null) return false; + for (final ResolveInfo ri : resolved.getList()) { + if (ri.getComponentInfo().getComponentName().equals(activity)) { + return true; + } + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to query intent activities", e); + } + return false; + } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 9bf6df41a93b..c1fe4887da57 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -40,8 +40,11 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.activityTypeToString; +import static android.app.WindowConfiguration.isSplitScreenWindowingMode; import static android.app.servertransaction.TransferSplashScreenViewStateItem.ATTACH_TO; import static android.app.servertransaction.TransferSplashScreenViewStateItem.HANDOVER_TO; import static android.content.Intent.ACTION_MAIN; @@ -209,6 +212,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND; import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR; +import static com.android.server.wm.WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY; @@ -436,6 +440,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final int launchedFromUid; // always the uid who started the activity. final String launchedFromPackage; // always the package who started the activity. final @Nullable String launchedFromFeatureId; // always the feature in launchedFromPackage + final boolean launchedFromHomeProcess; // as per original caller final Intent intent; // the original intent that generated us final String shortComponentName; // the short component name of the intent final String resolvedType; // as per original caller; @@ -556,7 +561,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** * The precomputed display insets for resolving configuration. It will be non-null if - * {@link #shouldUseSizeCompatMode} returns {@code true}. + * {@link #shouldCreateCompatDisplayInsets} returns {@code true}. */ private CompatDisplayInsets mCompatDisplayInsets; @@ -566,6 +571,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean mVoiceInteraction; private int mPendingRelaunchCount; + long mRelaunchStartTime; // True if we are current in the process of removing this app token from the display private boolean mRemovingFromDisplay = false; @@ -646,6 +652,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ private Rect mSizeCompatBounds; + // Whether this activity is in size compatibility mode because its bounds don't fit in parent + // naturally. + private boolean mInSizeCompatModeForBounds = false; + + // Whether this activity is letterboxed for fixed orientation. If letterboxed due to fixed + // orientation then aspect ratio restrictions are also already respected. + // This happens when an activity has fixed orientation which doesn't match orientation of the + // parent because a display is ignoring orientation request or fixed to user rotation. + // See WindowManagerService#getIgnoreOrientationRequest and + // WindowManagerService#getFixedToUserRotation for more context. + private boolean mIsLetterboxedForFixedOrientationAndAspectRatio = false; + // activity is not displayed? // TODO: rename to mNoDisplay @VisibleForTesting @@ -1294,9 +1312,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // TODO(b/36505427): Maybe this call should be moved inside // updateOverrideConfiguration() newTask.updateOverrideConfigurationFromLaunchBounds(); - // Make sure override configuration is up-to-date before using to create window - // controller. - updateSizeCompatMode(); // When an activity is started directly into a split-screen fullscreen root task, we // need to update the initial multi-window modes so that the callbacks are scheduled // correctly when the user leaves that mode. @@ -1667,6 +1682,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A launchedFromUid = _launchedFromUid; launchedFromPackage = _launchedFromPackage; launchedFromFeatureId = _launchedFromFeature; + launchedFromHomeProcess = _caller != null && _caller.isHomeProcess(); shortComponentName = _intent.getComponent().flattenToShortString(); resolvedType = _resolvedType; componentSpecified = _componentSpecified; @@ -2546,9 +2562,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * root task. */ boolean supportsFreeform() { - return mAtmService.mSupportsFreeformWindowManagement - // Either the activity is resizable, or we allow size compat in freeform. - && (supportsMultiWindow() || mAtmService.mSizeCompatFreeform); + return mAtmService.mSupportsFreeformWindowManagement && supportsMultiWindow(); } /** @@ -3355,7 +3369,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return task.isDragResizing(); } + @VisibleForTesting void startRelaunching() { + if (mPendingRelaunchCount == 0) { + mRelaunchStartTime = SystemClock.elapsedRealtime(); + } if (shouldFreezeBounds()) { freezeBounds(); } @@ -3394,6 +3412,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Update keyguard flags upon finishing relaunch. checkKeyguardFlagsChanged(); } + + final Task rootTask = getRootTask(); + if (rootTask != null && rootTask.shouldSleepOrShutDownActivities()) { + // Activity is always relaunched to either resumed or paused state. If it was + // relaunched while hidden (by keyguard or smth else), it should be stopped. + rootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + false /* preserveWindows */); + } } void clearRelaunching() { @@ -3402,6 +3428,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } unfreezeBounds(); mPendingRelaunchCount = 0; + mRelaunchStartTime = 0; } /** @@ -3608,7 +3635,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // Reset the last saved PiP snap fraction on removal. - mDisplayContent.mPinnedStackControllerLocked.onActivityHidden(mActivityComponent); + mDisplayContent.mPinnedTaskControllerLocked.onActivityHidden(mActivityComponent); mWmService.mEmbeddedWindowController.onActivityRemoved(this); mRemovingFromDisplay = false; } @@ -4960,7 +4987,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppStopped: %s", this); mAppStopped = true; // Reset the last saved PiP snap fraction on app stop. - mDisplayContent.mPinnedStackControllerLocked.onActivityHidden(mActivityComponent); + mDisplayContent.mPinnedTaskControllerLocked.onActivityHidden(mActivityComponent); destroySurfaces(); // Remove any starting window that was added for this app if they are still around. removeStartingWindow(); @@ -6702,12 +6729,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (onDescendantOrientationChanged(this)) { - // The app is just becoming visible, and the parent Task has updated with the - // orientation request. Update the size compat mode. - updateSizeCompatMode(); - // WM Shell can override WM Core positioning (e.g. for letterboxing) so ensure - // that WM Shell is called when an activity becomes visible. Without this, WM Core - // will handle positioning instead of WM Shell when an app is reopened. + // WM Shell can show additional UI elements, e.g. a restart button for size compat mode + // so ensure that WM Shell is called when an activity becomes visible. task.dispatchTaskInfoChangedIfNeeded(/* force= */ true); } } @@ -6772,7 +6795,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * density than its parent or its bounds don't fit in parent naturally. */ boolean inSizeCompatMode() { - if (mCompatDisplayInsets == null || !shouldUseSizeCompatMode() + if (mInSizeCompatModeForBounds) { + return true; + } + if (mCompatDisplayInsets == null || !shouldCreateCompatDisplayInsets() // The orientation is different from parent when transforming. || isFixedRotationTransforming()) { return false; @@ -6782,70 +6808,30 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The app bounds hasn't been computed yet. return false; } - final Configuration parentConfig = getParent().getConfiguration(); // Although colorMode, screenLayout, smallestScreenWidthDp are also fixed, generally these // fields should be changed with density and bounds, so here only compares the most // significant field. - if (parentConfig.densityDpi != getConfiguration().densityDpi) { - return true; - } - - final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds(); - final int appWidth = appBounds.width(); - final int appHeight = appBounds.height(); - final int parentAppWidth = parentAppBounds.width(); - final int parentAppHeight = parentAppBounds.height(); - if (parentAppWidth == appWidth && parentAppHeight == appHeight) { - // Matched the parent bounds. - return false; - } - if (parentAppWidth > appWidth && parentAppHeight > appHeight) { - // Both sides are smaller than the parent. - return true; - } - if (parentAppWidth < appWidth || parentAppHeight < appHeight) { - // One side is larger than the parent. - return true; - } - - // The rest of the condition is that only one side is smaller than the parent, but it still - // needs to exclude the cases where the size is limited by the fixed aspect ratio. - if (info.maxAspectRatio > 0) { - final float aspectRatio = (0.5f + Math.max(appWidth, appHeight)) - / Math.min(appWidth, appHeight); - if (aspectRatio >= info.maxAspectRatio) { - // The current size has reached the max aspect ratio. - return false; - } - } - if (info.minAspectRatio > 0) { - // The activity should have at least the min aspect ratio, so this checks if the parent - // still has available space to provide larger aspect ratio. - final float parentAspectRatio = (0.5f + Math.max(parentAppWidth, parentAppHeight)) - / Math.min(parentAppWidth, parentAppHeight); - if (parentAspectRatio <= info.minAspectRatio) { - // The long side has reached the parent. - return false; - } - } - return true; + return parentConfig.densityDpi != getConfiguration().densityDpi; } /** * Indicates the activity will keep the bounds and screen configuration when it was first * launched, no matter how its parent changes. * + * <p>If {@true}, then {@link CompatDisplayInsets} will be created in {@link + * #resolveOverrideConfiguration} to "freeze" activity bounds and insets. + * * @return {@code true} if this activity is declared as non-resizable and fixed orientation or * aspect ratio. */ - boolean shouldUseSizeCompatMode() { + boolean shouldCreateCompatDisplayInsets() { if (info.supportsSizeChanges() != ActivityInfo.SIZE_CHANGES_UNSUPPORTED) { return false; } if (inMultiWindowMode() || getWindowConfiguration().hasWindowDecorCaption()) { final ActivityRecord root = task != null ? task.getRootActivity() : null; - if (root != null && root != this && !root.shouldUseSizeCompatMode()) { + if (root != null && root != this && !root.shouldCreateCompatDisplayInsets()) { // If the root activity doesn't use size compatibility mode, the activities above // are forced to be the same for consistent visual appearance. return false; @@ -6867,25 +6853,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. - private void updateSizeCompatMode() { - if (mCompatDisplayInsets != null || !shouldUseSizeCompatMode()) { + private void updateCompatDisplayInsets(@Nullable Rect fixedOrientationBounds) { + if (mCompatDisplayInsets != null || !shouldCreateCompatDisplayInsets()) { // The override configuration is set only once in size compatibility mode. return; } - final Configuration parentConfig = getParent().getConfiguration(); - if (!hasProcess() && !isConfigurationCompatible(parentConfig)) { - // Don't compute when launching in fullscreen and the fixed orientation is not the - // current orientation. It is more accurately to compute the override bounds from - // the updated configuration after the fixed orientation is applied. - return; - } - - if (task == null || (!handlesOrientationChangeFromDescendant() - && task.getLastTaskBoundsComputeActivity() != this)) { - // Don't compute when Task hasn't computed its bounds for this app, because the Task can - // be letterboxed, and its bounds may not be accurate until then. - return; - } Configuration overrideConfig = getRequestedOverrideConfiguration(); final Configuration fullConfig = getConfiguration(); @@ -6908,17 +6880,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // The role of CompatDisplayInsets is like the override bounds. - mCompatDisplayInsets = new CompatDisplayInsets(mDisplayContent, this); + mCompatDisplayInsets = + new CompatDisplayInsets(mDisplayContent, this, fixedOrientationBounds); } @VisibleForTesting void clearSizeCompatMode() { + mInSizeCompatModeForBounds = false; mSizeCompatScale = 1f; mSizeCompatBounds = null; mCompatDisplayInsets = null; - // Recompute from Task because letterbox can also happen on Task level. - task.onRequestedOverrideConfigurationChanged(task.getRequestedOverrideConfiguration()); + onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration()); } @Override @@ -6953,23 +6926,41 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mTmpConfig.updateFrom(resolvedConfig); newParentConfiguration = mTmpConfig; } + + final int windowingMode = getWindowingMode(); + // TODO(b/181207944): Consider removing the if condition and always run + // resolveFixedOrientationConfiguration() since this should be applied for all cases. + if (isSplitScreenWindowingMode(windowingMode) + || windowingMode == WINDOWING_MODE_MULTI_WINDOW + || windowingMode == WINDOWING_MODE_FULLSCREEN) { + resolveFixedOrientationConfiguration(newParentConfiguration); + } + final Rect fixedOrientationBounds = isLetterboxedForFixedOrientationAndAspectRatio() + ? new Rect(resolvedConfig.windowConfiguration.getBounds()) : null; + if (mCompatDisplayInsets != null) { resolveSizeCompatModeConfiguration(newParentConfiguration); - } else { - if (inMultiWindowMode()) { - // We ignore activities' requested orientation in multi-window modes. Task level may - // take them into consideration when calculating bounds. - resolvedConfig.orientation = Configuration.ORIENTATION_UNDEFINED; - // If the activity has requested override bounds, the configuration needs to be - // computed accordingly. - if (!matchParentBounds()) { - task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration); - } - } else { - resolveFullscreenConfiguration(newParentConfiguration); + } else if (inMultiWindowMode()) { + // We ignore activities' requested orientation in multi-window modes. They may be + // taken into consideration in resolveFixedOrientationConfiguration call above. + resolvedConfig.orientation = Configuration.ORIENTATION_UNDEFINED; + // If the activity has requested override bounds, the configuration needs to be + // computed accordingly. + if (!matchParentBounds()) { + task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration); } + // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds + // are already calculated in resolveFixedOrientationConfiguration. + } else if (!isLetterboxedForFixedOrientationAndAspectRatio()) { + resolveFullscreenConfiguration(newParentConfiguration); + } + + if (mVisibleRequested) { + updateCompatDisplayInsets(fixedOrientationBounds); } + // TODO(b/175212232): Consolidate position logic from each "resolve" method above here. + // Assign configuration sequence number into hierarchy because there is a different way than // ensureActivityConfiguration() in this class that uses configuration in WindowState during // layout traversals. @@ -6978,6 +6969,109 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** + * Whether this activity is letterboxed for fixed orientation. If letterboxed due to fixed + * orientation then aspect ratio restrictions are also already respected. + * + * <p>This happens when an activity has fixed orientation which doesn't match orientation of the + * parent because a display setting 'ignoreOrientationRequest' is set to true. See {@link + * WindowManagerService#getIgnoreOrientationRequest} for more context. + */ + boolean isLetterboxedForFixedOrientationAndAspectRatio() { + return mIsLetterboxedForFixedOrientationAndAspectRatio; + } + + /** + * Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation + * change and the requested orientation is different from the parent. + * + * <p>If letterboxed due to fixed orientation then aspect ratio restrictions are also applied + * in this methiod. + */ + private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) { + mIsLetterboxedForFixedOrientationAndAspectRatio = false; + if (handlesOrientationChangeFromDescendant()) { + // No need to letterbox because of fixed orientation. Display will handle + // fixed-orientation requests. + return; + } + + final Rect resolvedBounds = + getResolvedOverrideConfiguration().windowConfiguration.getBounds(); + final int parentOrientation = newParentConfig.orientation; + + // If the activity requires a different orientation (either by override or activityInfo), + // make it fit the available bounds by scaling down its bounds. + final int forcedOrientation = getRequestedConfigurationOrientation(); + if (forcedOrientation == ORIENTATION_UNDEFINED || forcedOrientation == parentOrientation) { + return; + } + + if (mCompatDisplayInsets != null && !mCompatDisplayInsets.mIsInFixedOrientationLetterbox) { + // App prefers to keep its original size. + // If the size compat is from previous fixed orientation letterboxing, we may want to + // have fixed orientation letterbox again, otherwise it will show the size compat + // restart button even if the restart bounds will be the same. + return; + } + + final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); + final int parentWidth = parentBounds.width(); + final int parentHeight = parentBounds.height(); + float aspect = Math.max(parentWidth, parentHeight) + / (float) Math.min(parentWidth, parentHeight); + + // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in + // order to use the extra available space. + final float maxAspectRatio = info.maxAspectRatio; + final float minAspectRatio = info.minAspectRatio; + if (aspect > maxAspectRatio && maxAspectRatio != 0) { + aspect = maxAspectRatio; + } else if (aspect < minAspectRatio) { + aspect = minAspectRatio; + } + + // Override from config_letterboxAspectRatio or via ADB with set-letterbox-aspect-ratio. + // TODO(b/175212232): Rename getTaskLetterboxAspectRatio and all related methods since fixed + // orientation letterbox is on the activity level now. + final float letterboxAspectRatioOverride = mWmService.getTaskLetterboxAspectRatio(); + // Activity min/max aspect ratio restrictions will be respected by the activity-level + // letterboxing (size-compat mode). Therefore this override can control the maximum screen + // area that can be occupied by the app in the letterbox mode. + aspect = letterboxAspectRatioOverride > MIN_TASK_LETTERBOX_ASPECT_RATIO + ? letterboxAspectRatioOverride : aspect; + + // Store the current bounds to be able to revert to size compat mode values below if needed. + Rect mTmpFullBounds = new Rect(resolvedBounds); + if (forcedOrientation == ORIENTATION_LANDSCAPE) { + final int height = (int) Math.rint(parentWidth / aspect); + final int top = parentBounds.centerY() - height / 2; + resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + height); + } else { + final int width = (int) Math.rint(parentHeight / aspect); + final int left = parentBounds.centerX() - width / 2; + resolvedBounds.set(left, parentBounds.top, left + width, parentBounds.bottom); + } + + if (mCompatDisplayInsets != null) { + mCompatDisplayInsets.getBoundsByRotation( + mTmpBounds, newParentConfig.windowConfiguration.getRotation()); + if (resolvedBounds.width() != mTmpBounds.width() + || resolvedBounds.height() != mTmpBounds.height()) { + // The app shouldn't be resized, we only do fixed orientation letterboxing if the + // compat bounds are also from the same fixed orientation letterbox. Otherwise, + // clear the fixed orientation bounds to show app in size compat mode. + resolvedBounds.set(mTmpFullBounds); + return; + } + } + + // Calculate app bounds using fixed orientation bounds because they will be needed later + // for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}. + task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig); + mIsLetterboxedForFixedOrientationAndAspectRatio = true; + } + + /** * Resolves the configuration of activity in fullscreen mode. If the bounds are restricted by * aspect ratio, the position will be centered horizontally in parent's app bounds to balance * the visual appearance. The policy of aspect ratio has higher priority than the requested @@ -7021,6 +7115,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private void resolveSizeCompatModeConfiguration(Configuration newParentConfiguration) { final Configuration resolvedConfig = getResolvedOverrideConfiguration(); final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds(); + + // When an activity needs to be letterboxed because of fixed orientation, use fixed + // orientation bounds (stored in resolved bounds) instead of parent bounds since the + // activity will be displayed within them even if it is in size compat mode. They should be + // saved here before resolved bounds are overridden below. + final Rect containerBounds = isLetterboxedForFixedOrientationAndAspectRatio() + ? new Rect(resolvedBounds) + : newParentConfiguration.windowConfiguration.getBounds(); + final Rect containerAppBounds = isLetterboxedForFixedOrientationAndAspectRatio() + ? new Rect(getResolvedOverrideConfiguration().windowConfiguration.getAppBounds()) + : newParentConfiguration.windowConfiguration.getAppBounds(); + final int requestedOrientation = getRequestedConfigurationOrientation(); final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED; final int orientation = orientationRequested @@ -7079,7 +7185,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Below figure is an example that puts an activity which was launched in a larger container // into a smaller container. // The outermost rectangle is the real display bounds. - // "@" is the parent app bounds. + // "@" is the container app bounds (parent bounds or fixed orientation bouds) // "#" is the {@code resolvedBounds} that applies to application. // "*" is the {@code mSizeCompatBounds} that used to show on screen if scaled. // ------------------------------ @@ -7095,19 +7201,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The application is still layouted in "#" since it was launched, and it will be visually // scaled and positioned to "*". + final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds(); + // Calculates the scale and offset to horizontal center the size compatibility bounds into // the region which is available to application. - final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds(); - final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds(); - final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds(); final int contentW = resolvedAppBounds.width(); final int contentH = resolvedAppBounds.height(); - final int viewportW = parentAppBounds.width(); - final int viewportH = parentAppBounds.height(); + final int viewportW = containerAppBounds.width(); + final int viewportH = containerAppBounds.height(); // Only allow to scale down. mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH) ? 1f : Math.min((float) viewportW / contentW, (float) viewportH / contentH); - final int screenTopInset = parentAppBounds.top - parentBounds.top; + final int screenTopInset = containerAppBounds.top - containerBounds.top; final boolean topNotAligned = screenTopInset != resolvedAppBounds.top - resolvedBounds.top; if (mSizeCompatScale != 1f || topNotAligned) { if (mSizeCompatBounds == null) { @@ -7127,8 +7232,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final int offsetX = getHorizontalCenterOffset( (int) viewportW, (int) (contentW * mSizeCompatScale)); // Above coordinates are in "@" space, now place "*" and "#" to screen space. - final int screenPosX = (fillContainer ? parentBounds.left : parentAppBounds.left) + offsetX; - final int screenPosY = parentBounds.top; + final int screenPosX = (fillContainer + ? containerBounds.left : containerAppBounds.left) + offsetX; + final int screenPosY = containerBounds.top; if (screenPosX != 0 || screenPosY != 0) { if (mSizeCompatBounds != null) { mSizeCompatBounds.offset(screenPosX, screenPosY); @@ -7138,6 +7244,52 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final int dy = screenPosY - resolvedBounds.top; offsetBounds(resolvedConfig, dx, dy); } + + mInSizeCompatModeForBounds = + isInSizeCompatModeForBounds(resolvedAppBounds, containerAppBounds); + } + + private boolean isInSizeCompatModeForBounds(final Rect appBounds, final Rect containerBounds) { + final int appWidth = appBounds.width(); + final int appHeight = appBounds.height(); + final int containerAppWidth = containerBounds.width(); + final int containerAppHeight = containerBounds.height(); + + if (containerAppWidth == appWidth && containerAppHeight == appHeight) { + // Matched the container bounds. + return false; + } + if (containerAppWidth > appWidth && containerAppHeight > appHeight) { + // Both sides are smaller than the container. + return true; + } + if (containerAppWidth < appWidth || containerAppHeight < appHeight) { + // One side is larger than the container. + return true; + } + + // The rest of the condition is that only one side is smaller than the container, but it + // still needs to exclude the cases where the size is limited by the fixed aspect ratio. + if (info.maxAspectRatio > 0) { + final float aspectRatio = (0.5f + Math.max(appWidth, appHeight)) + / Math.min(appWidth, appHeight); + if (aspectRatio >= info.maxAspectRatio) { + // The current size has reached the max aspect ratio. + return false; + } + } + if (info.minAspectRatio > 0) { + // The activity should have at least the min aspect ratio, so this checks if the + // container still has available space to provide larger aspect ratio. + final float containerAspectRatio = + (0.5f + Math.max(containerAppWidth, containerAppHeight)) + / Math.min(containerAppWidth, containerAppHeight); + if (containerAspectRatio <= info.minAspectRatio) { + // The long side has reached the parent. + return false; + } + } + return true; } /** @return The horizontal offset of putting the content in the center of viewport. */ @@ -7289,7 +7441,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Task rootTask = getRootTask(); final float minAspectRatio = info.minAspectRatio; - if (task == null || rootTask == null || (inMultiWindowMode() && !shouldUseSizeCompatMode()) + if (task == null || rootTask == null + || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()) || (maxAspectRatio == 0 && minAspectRatio == 0) || isInVrUiMode(getConfiguration())) { // We don't enforce aspect ratio if the activity task is in multiwindow unless it @@ -7427,8 +7580,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (displayChanged) { mLastReportedDisplayId = newDisplayId; } - // TODO(b/36505427): Is there a better place to do this? - updateSizeCompatMode(); // Short circuit: if the two full configurations are equal (the common case), then there is // nothing to do. We test the full configuration instead of the global and merged override @@ -7707,11 +7858,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Reset the existing override configuration so it can be updated according to the latest // configuration. clearSizeCompatMode(); - if (mVisibleRequested) { - // Configuration will be ensured when becoming visible, so if it is already visible, - // then the manual update is needed. - updateSizeCompatMode(); - } if (!attachedToProcess()) { return; @@ -8118,8 +8264,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private final int mHeight; /** Whether the {@link Task} windowingMode represents a floating window*/ final boolean mIsFloating; - /** Whether the {@link Task} is letterboxed when the unresizable activity is first shown. */ - final boolean mIsTaskLetterboxed; + /** + * Whether is letterboxed because of fixed orientation when the unresizable activity is + * first shown. + */ + final boolean mIsInFixedOrientationLetterbox; /** * The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. It * is used to compute the appBounds. @@ -8133,7 +8282,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Rect[] mStableInsets = new Rect[4]; /** Constructs the environment to simulate the bounds behavior of the given container. */ - CompatDisplayInsets(DisplayContent display, ActivityRecord container) { + CompatDisplayInsets(DisplayContent display, ActivityRecord container, + @Nullable Rect fixedOrientationBounds) { mIsFloating = container.getWindowConfiguration().tasksAreFloating(); if (mIsFloating) { final Rect containerBounds = container.getWindowConfiguration().getBounds(); @@ -8146,24 +8296,34 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mNonDecorInsets[rotation] = emptyRect; mStableInsets[rotation] = emptyRect; } - mIsTaskLetterboxed = false; + mIsInFixedOrientationLetterbox = false; return; } final Task task = container.getTask(); - mIsTaskLetterboxed = task != null && task.isTaskLetterboxed(); + + mIsInFixedOrientationLetterbox = fixedOrientationBounds != null; // Store the bounds of the Task for the non-resizable activity to use in size compat // mode so that the activity will not be resized regardless the windowing mode it is // currently in. - final WindowContainer filledContainer = task != null ? task : display; - final Point dimensions = getRotationZeroDimensions(filledContainer); + // When an activity needs to be letterboxed because of fixed orientation, use fixed + // orientation bounds instead of task bounds since the activity will be displayed + // within these even if it is in size compat mode. + final Rect filledContainerBounds = mIsInFixedOrientationLetterbox + ? fixedOrientationBounds + : task != null ? task.getBounds() : display.getBounds(); + final int filledContainerRotation = task != null + ? task.getConfiguration().windowConfiguration.getRotation() + : display.getConfiguration().windowConfiguration.getRotation(); + final Point dimensions = getRotationZeroDimensions( + filledContainerBounds, filledContainerRotation); mWidth = dimensions.x; mHeight = dimensions.y; // Bounds of the filled container if it doesn't fill the display. final Rect unfilledContainerBounds = - filledContainer.getBounds().equals(display.getBounds()) ? null : new Rect(); + filledContainerBounds.equals(display.getBounds()) ? null : new Rect(); final DisplayPolicy policy = display.getDisplayPolicy(); for (int rotation = 0; rotation < 4; rotation++) { mNonDecorInsets[rotation] = new Rect(); @@ -8183,9 +8343,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The insets is based on the display, but the container may be smaller than the // display, so update the insets to exclude parts that are not intersected with the // container. - unfilledContainerBounds.set(filledContainer.getBounds()); + unfilledContainerBounds.set(filledContainerBounds); display.rotateBounds( - filledContainer.getConfiguration().windowConfiguration.getRotation(), + filledContainerRotation, rotation, unfilledContainerBounds); updateInsetsForBounds(unfilledContainerBounds, dw, dh, mNonDecorInsets[rotation]); @@ -8198,9 +8358,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * the display is rotated, we can calculate the bounds by rotating the dimensions. * @see #getBoundsByRotation */ - private static Point getRotationZeroDimensions(WindowContainer container) { - final Rect bounds = container.getBounds(); - final int rotation = container.getConfiguration().windowConfiguration.getRotation(); + private static Point getRotationZeroDimensions(final Rect bounds, int rotation) { final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); final int width = bounds.width(); final int height = bounds.height(); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 37fda4ce217a..6ce9048d79e2 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1708,7 +1708,7 @@ class ActivityStarter { // If the activity being launched is the same as the one currently at the top, then // we need to check if it should only be launched once. - final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); + final Task topRootTask = mPreferredTaskDisplayArea.getFocusedRootTask(); if (topRootTask != null) { startResult = deliverToCurrentTopIfNeeded(topRootTask, intentGrants); if (startResult != START_SUCCESS) { @@ -1989,6 +1989,13 @@ class ActivityStarter { return START_SUCCESS; } + // The reusedActivity could be finishing, for example of starting an activity with + // FLAG_ACTIVITY_CLEAR_TOP flag. In that case, use the top running activity in the + // task instead. + targetTaskTop = targetTaskTop.finishing + ? targetTask.getTopNonFinishingActivity() + : targetTaskTop; + // At this point we are certain we want the task moved to the front. If we need to dismiss // any other always-on-top root tasks, now is the time to do it. if (targetTaskTop.canTurnScreenOn() && mService.mInternal.isDreaming()) { @@ -2005,11 +2012,8 @@ class ActivityStarter { // We didn't do anything... but it was needed (a.k.a., client don't use that intent!) // And for paranoia, make sure we have correctly resumed the top activity. resumeTargetRootTaskIfNeeded(); - // The reusedActivity could be finishing, for example of starting an activity with - // FLAG_ACTIVITY_CLEAR_TOP flag. In that case, return the top running activity in the - // task instead. - mLastStartActivityRecord = - targetTaskTop.finishing ? targetTask.getTopNonFinishingActivity() : targetTaskTop; + + mLastStartActivityRecord = targetTaskTop; return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 2e98c2cbc5c2..db499158a5d9 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -58,7 +58,6 @@ import static android.os.Process.SYSTEM_UID; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW; -import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL; import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS; @@ -318,6 +317,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public static final String DUMP_CONTAINERS_CMD = "containers"; public static final String DUMP_RECENTS_CMD = "recents"; public static final String DUMP_RECENTS_SHORT_CMD = "r"; + public static final String DUMP_TOP_RESUMED_ACTIVITY = "top-resumed"; /** This activity is not being relaunched, or being relaunched for a non-resize reason. */ public static final int RELAUNCH_REASON_NONE = 0; @@ -556,7 +556,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { boolean mSupportsPictureInPicture; boolean mSupportsMultiDisplay; boolean mForceResizableActivities; - boolean mSizeCompatFreeform; boolean mSupportsNonResizableMultiWindow; final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>(); @@ -787,8 +786,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0; final boolean forceResizable = Settings.Global.getInt( resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0; - final boolean sizeCompatFreeform = Settings.Global.getInt( - resolver, DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0; final boolean supportsNonResizableMultiWindow = Settings.Global.getInt( resolver, DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 1) != 0; @@ -804,7 +801,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { mForceResizableActivities = forceResizable; - mSizeCompatFreeform = sizeCompatFreeform; mSupportsNonResizableMultiWindow = supportsNonResizableMultiWindow; final boolean multiWindowFormEnabled = freeformWindowManagement || supportsSplitScreenMultiWindow @@ -863,7 +859,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mTaskChangeNotificationController = new TaskChangeNotificationController(mGlobalLock, mTaskSupervisor, mH); - mLockTaskController = new LockTaskController(mContext, mTaskSupervisor, mH); + mLockTaskController = new LockTaskController(mContext, mTaskSupervisor, mH, + mTaskChangeNotificationController); mActivityStartController = new ActivityStartController(this); setRecentTasks(new RecentTasks(this, mTaskSupervisor)); mVrController = new VrController(mGlobalLock); @@ -2093,10 +2090,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - /** @return the max ANR delay from all registered {@link AnrController} instances */ - public long getMaxAnrDelayMillis(ApplicationInfo info) { + /** + * @return the controller with the max ANR delay from all registered + * {@link AnrController} instances + */ + @Nullable + public AnrController getAnrController(ApplicationInfo info) { if (info == null || info.packageName == null) { - return 0; + return null; } final ArrayList<AnrController> controllers; @@ -2105,12 +2106,19 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final String packageName = info.packageName; + final int uid = info.uid; long maxDelayMs = 0; + AnrController controllerWithMaxDelay = null; + for (AnrController controller : controllers) { - maxDelayMs = Math.max(maxDelayMs, controller.getAnrDelayMillis(packageName, info.uid)); + long delayMs = controller.getAnrDelayMillis(packageName, uid); + if (delayMs > 0 && delayMs > maxDelayMs) { + controllerWithMaxDelay = controller; + maxDelayMs = delayMs; + } } - maxDelayMs = Math.max(maxDelayMs, 0); - return maxDelayMs; + + return controllerWithMaxDelay; } @Override @@ -3756,6 +3764,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + void dumpTopResumedActivityLocked(PrintWriter pw) { + pw.println("ACTIVITY MANAGER TOP-RESUMED (dumpsys activity top-resumed)"); + ActivityRecord topRecord = mRootWindowContainer.getTopResumedActivity(); + if (topRecord != null) { + topRecord.dump(pw, "", true); + } + } + void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) { dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage, @@ -4005,8 +4021,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { deferWindowLayout(); try { if (values != null) { - changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId, - deferResume); + changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId); } if (!deferResume) { @@ -4025,7 +4040,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** Update default (global) configuration and notify listeners about changes. */ int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale, - boolean persistent, int userId, boolean deferResume) { + boolean persistent, int userId) { final DisplayContent defaultDisplay = mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY); @@ -4037,7 +4052,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // setting WindowManagerService.mWaitingForConfig to true, it is important that we call // performDisplayOverrideConfigUpdate in order to send the new display configuration // (even if there are no actual changes) to unfreeze the window. - defaultDisplay.performDisplayOverrideConfigUpdate(values, deferResume); + defaultDisplay.performDisplayOverrideConfigUpdate(values); return 0; } @@ -4085,9 +4100,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mTempConfig.seq = increaseConfigurationSeqLocked(); - // Update stored global config and notify everyone about the change. - mRootWindowContainer.onConfigurationChanged(mTempConfig); - Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig); // TODO(multi-display): Update UsageEvents#Event to include displayId. mUsageStatsInternal.reportConfigurationChange(mTempConfig, mAmInternal.getCurrentUserId()); @@ -4106,13 +4118,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // resources have that config before following boot code is executed. mSystemThread.applyConfigurationToResources(mTempConfig); - // We need another copy of global config because we're scheduling some calls instead of - // running them in place. We need to be sure that object we send will be handled unchanged. - final Configuration configCopy = new Configuration(mTempConfig); if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) { final Message msg = PooledLambda.obtainMessage( ActivityTaskManagerService::sendPutConfigurationForUserMsg, - this, userId, configCopy); + this, userId, new Configuration(mTempConfig)); mH.sendMessage(msg); } @@ -4121,8 +4130,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final int pid = pidMap.keyAt(i); final WindowProcessController app = pidMap.get(pid); ProtoLog.v(WM_DEBUG_CONFIGURATION, "Update process config of %s to new " - + "config %s", app.mName, configCopy); - app.onConfigurationChanged(configCopy); + + "config %s", app.mName, mTempConfig); + app.onConfigurationChanged(mTempConfig); } final Message msg = PooledLambda.obtainMessage( @@ -4130,10 +4139,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mAmInternal, changes, initLocale); mH.sendMessage(msg); - // Override configuration of the default display duplicates global config, so we need to - // update it also. This will also notify WindowManager about changes. - defaultDisplay.performDisplayOverrideConfigUpdate(mRootWindowContainer.getConfiguration(), - deferResume); + // Update stored global config and notify everyone about the change. + mRootWindowContainer.onConfigurationChanged(mTempConfig); return changes; } @@ -5896,6 +5903,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (getRecentTasks() != null) { getRecentTasks().dump(pw, dumpAll, dumpPackage); } + } else if (DUMP_TOP_RESUMED_ACTIVITY.equals(cmd)) { + dumpTopResumedActivityLocked(pw); } } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 1264d0c53e0c..bf2aae8867ba 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2204,19 +2204,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { task.mTaskId, reason, topActivity.info.applicationInfo.packageName); } - void activityRelaunchedLocked(IBinder token) { - final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); - if (r != null) { - r.finishRelaunching(); - if (r.getRootTask().shouldSleepOrShutDownActivities()) { - // Activity is always relaunched to either resumed or paused state. If it was - // relaunched while hidden (by keyguard or smth else), it should be stopped. - r.getRootTask().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, - false /* preserveWindows */); - } - } - } - void logRootTaskState() { mActivityMetricsLogger.logWindowState(); } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index efcaaa42ec13..309b5ec25f0f 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -141,6 +141,10 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { mChangeListeners.get(i).onMergedOverrideConfigurationChanged( mMergedOverrideConfiguration); } + dispatchConfigurationToChildren(); + } + + void dispatchConfigurationToChildren() { for (int i = getChildCount() - 1; i >= 0; --i) { final ConfigurationContainer child = getChildAt(i); child.onConfigurationChanged(mFullConfiguration); diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index f505daa9b628..a7312b321e4d 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -67,39 +67,40 @@ import java.util.function.BiFunction; * .setImeContainer(imeContainer) * .setTaskDisplayAreas(rootTdaList); * - * // Build root hierarchy of front and rear DisplayAreaGroup. - * RootDisplayArea frontRoot = new RootDisplayArea(wmService, "FrontRoot", FEATURE_FRONT_ROOT); - * DisplayAreaPolicyBuilder.HierarchyBuilder frontGroupHierarchy = - * new DisplayAreaPolicyBuilder.HierarchyBuilder(frontRoot) + * // Build root hierarchy of first and second DisplayAreaGroup. + * RootDisplayArea firstRoot = new RootDisplayArea(wmService, "FirstRoot", FEATURE_FIRST_ROOT); + * DisplayAreaPolicyBuilder.HierarchyBuilder firstGroupHierarchy = + * new DisplayAreaPolicyBuilder.HierarchyBuilder(firstRoot) * // (Optional) .addFeature(...) - * .setTaskDisplayAreas(frontTdaList); + * .setTaskDisplayAreas(firstTdaList); * - * RootDisplayArea rearRoot = new RootDisplayArea(wmService, "RearRoot", FEATURE_REAR_ROOT); - * DisplayAreaPolicyBuilder.HierarchyBuilder rearGroupHierarchy = - * new DisplayAreaPolicyBuilder.HierarchyBuilder(rearRoot) + * RootDisplayArea secondRoot = new RootDisplayArea(wmService, "SecondRoot", + * FEATURE_REAR_ROOT); + * DisplayAreaPolicyBuilder.HierarchyBuilder secondGroupHierarchy = + * new DisplayAreaPolicyBuilder.HierarchyBuilder(secondRoot) * // (Optional) .addFeature(...) - * .setTaskDisplayAreas(rearTdaList); + * .setTaskDisplayAreas(secondTdaList); * * // Define the function to select root for window to attach. * BiFunction<WindowToken, Bundle, RootDisplayArea> selectRootForWindowFunc = - * (windowToken, bundle) -> { - * if (bundle == null) { + * (windowToken, options) -> { + * if (options == null) { * return root; * } * // OEMs need to define the condition. * if (...) { - * return frontRoot; + * return firstRoot; * } * if (...) { - * return rearRoot; + * return secondRoot; * } * return root; * }; * * return new DisplayAreaPolicyBuilder() * .setRootHierarchy(rootHierarchy) - * .addDisplayAreaGroupHierarchy(frontGroupHierarchy) - * .addDisplayAreaGroupHierarchy(rearGroupHierarchy) + * .addDisplayAreaGroupHierarchy(firstGroupHierarchy) + * .addDisplayAreaGroupHierarchy(secondGroupHierarchy) * .setSelectRootForWindowFunc(selectRootForWindowFunc) * .build(wmService, content); * </pre> @@ -110,12 +111,12 @@ import java.util.function.BiFunction; * - WindowedMagnification * - DisplayArea.Tokens (Wallpapers can be attached here) * - TaskDisplayArea - * - RootDisplayArea (FrontRoot) + * - RootDisplayArea (FirstRoot) * - DisplayArea.Tokens (Wallpapers can be attached here) * - TaskDisplayArea * - DisplayArea.Tokens (windows above Tasks up to IME can be attached here) * - DisplayArea.Tokens (windows above IME can be attached here) - * - RootDisplayArea (RearRoot) + * - RootDisplayArea (SecondRoot) * - DisplayArea.Tokens (Wallpapers can be attached here) * - TaskDisplayArea * - DisplayArea.Tokens (windows above Tasks up to IME can be attached here) @@ -133,14 +134,23 @@ import java.util.function.BiFunction; * {@link RootDisplayArea}. */ class DisplayAreaPolicyBuilder { + + /** + * Key to specify the {@link RootDisplayArea} to attach the window to. Should be used by the + * function passed in from {@link #setSelectRootForWindowFunc(BiFunction)} + */ + static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id"; + @Nullable private HierarchyBuilder mRootHierarchyBuilder; - private ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = new ArrayList<>(); + private final ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders = + new ArrayList<>(); /** * When a window is created, the policy will use this function, which takes window type and * options, to select the {@link RootDisplayArea} to place that window in. The selected root * can be either the one of the {@link #mRootHierarchyBuilder} or the one of any of the * {@link #mDisplayAreaGroupHierarchyBuilders}. + * @see DefaultSelectRootForWindowFunction as an example. **/ @Nullable private BiFunction<Integer, Bundle, RootDisplayArea> mSelectRootForWindowFunc; @@ -160,7 +170,10 @@ class DisplayAreaPolicyBuilder { return this; } - /** The policy will use this function to find the root to place windows in. */ + /** + * The policy will use this function to find the root to place windows in. + * @see DefaultSelectRootForWindowFunction as an example. + */ DisplayAreaPolicyBuilder setSelectRootForWindowFunc( BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) { mSelectRootForWindowFunc = selectRootForWindowFunc; @@ -169,7 +182,7 @@ class DisplayAreaPolicyBuilder { /** * Makes sure the setting meets the requirement: - * 1. {@link mRootHierarchyBuilder} must be set. + * 1. {@link #mRootHierarchyBuilder} must be set. * 2. {@link RootDisplayArea} and {@link TaskDisplayArea} must have unique ids. * 3. {@link Feature} below the same {@link RootDisplayArea} must have unique ids. * 4. There must be exactly one {@link HierarchyBuilder} that contains the IME container. @@ -309,11 +322,57 @@ class DisplayAreaPolicyBuilder { hierarchyBuilder.build(); displayAreaGroupRoots.add(hierarchyBuilder.mRoot); } + // Use the default function if it is not specified otherwise. + if (mSelectRootForWindowFunc == null) { + mSelectRootForWindowFunc = new DefaultSelectRootForWindowFunction( + mRootHierarchyBuilder.mRoot, displayAreaGroupRoots); + } return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots, mSelectRootForWindowFunc); } /** + * The default function to be used for finding {@link RootDisplayArea} for window to be attached + * to if there is no other function set through {@link #setSelectRootForWindowFunc(BiFunction)}. + * + * When a window is created with {@link Bundle} options, this function will select the + * {@link RootDisplayArea} based on the options. Returns {@link #mDisplayRoot} if there is no + * match found. + */ + private static class DefaultSelectRootForWindowFunction implements + BiFunction<Integer, Bundle, RootDisplayArea> { + final RootDisplayArea mDisplayRoot; + final List<RootDisplayArea> mDisplayAreaGroupRoots; + + DefaultSelectRootForWindowFunction(RootDisplayArea displayRoot, + List<RootDisplayArea> displayAreaGroupRoots) { + mDisplayRoot = displayRoot; + mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots); + } + + @Override + public RootDisplayArea apply(Integer windowType, Bundle options) { + if (mDisplayAreaGroupRoots.isEmpty()) { + return mDisplayRoot; + } + + // Select the RootDisplayArea set in options. + if (options != null && options.containsKey(KEY_ROOT_DISPLAY_AREA_ID)) { + final int rootId = options.getInt(KEY_ROOT_DISPLAY_AREA_ID); + if (mDisplayRoot.mFeatureId == rootId) { + return mDisplayRoot; + } + for (int i = mDisplayAreaGroupRoots.size() - 1; i >= 0; i--) { + if (mDisplayAreaGroupRoots.get(i).mFeatureId == rootId) { + return mDisplayAreaGroupRoots.get(i); + } + } + } + return mDisplayRoot; + } + } + + /** * Builder to define {@link Feature} and {@link DisplayArea} hierarchy under a * {@link RootDisplayArea} */ @@ -672,15 +731,10 @@ class DisplayAreaPolicyBuilder { Result(WindowManagerService wmService, RootDisplayArea root, List<RootDisplayArea> displayAreaGroupRoots, - @Nullable BiFunction<Integer, Bundle, RootDisplayArea> - selectRootForWindowFunc) { + BiFunction<Integer, Bundle, RootDisplayArea> selectRootForWindowFunc) { super(wmService, root); mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots); - mSelectRootForWindowFunc = selectRootForWindowFunc == null - // Always return the highest level root of the logical display when the func is - // not specified. - ? (type, options) -> mRoot - : selectRootForWindowFunc; + mSelectRootForWindowFunc = selectRootForWindowFunc; // Cache the default TaskDisplayArea for quick access. mDefaultTaskDisplayArea = mRoot.getItemFromTaskDisplayAreas(taskDisplayArea -> diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 86968ed6d175..eff4ea6536bd 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -36,6 +36,7 @@ import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.os.Build.VERSION_CODES.N; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.util.DisplayMetrics.DENSITY_DEFAULT; +import static android.util.RotationUtils.deltaRotation; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; import static android.view.Display.FLAG_PRIVATE; @@ -47,7 +48,6 @@ import static android.view.InsetsState.ITYPE_LEFT_GESTURES; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_RIGHT_GESTURES; import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.View.GONE; @@ -150,7 +150,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; -import android.app.WindowConfiguration; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.ScreenOrientation; @@ -158,10 +157,8 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Insets; -import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.RectF; import android.graphics.Region; import android.graphics.Region.Op; import android.hardware.HardwareBuffer; @@ -206,6 +203,7 @@ import android.view.MagnificationSpec; import android.view.RemoteAnimationDefinition; import android.view.RoundedCorners; import android.view.Surface; +import android.view.Surface.Rotation; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; @@ -449,8 +447,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** Save allocating when calculating rects */ private final Rect mTmpRect = new Rect(); private final Rect mTmpRect2 = new Rect(); - private final RectF mTmpRectF = new RectF(); - private final Matrix mTmpMatrix = new Matrix(); private final Region mTmpRegion = new Region(); /** Used for handing back size of display */ @@ -461,8 +457,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** Remove this display when animation on it has completed. */ private boolean mDeferredRemoval; - final DockedStackDividerController mDividerControllerLocked; - final PinnedStackController mPinnedStackControllerLocked; + final DockedTaskDividerController mDividerControllerLocked; + final PinnedTaskController mPinnedTaskControllerLocked; final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>(); /** A collection of windows that provide tap exclude regions inside of them. */ @@ -673,10 +669,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Used in updating override configurations private final Configuration mTempConfig = new Configuration(); - // Used in performing layout, to record the insets provided by other windows above the current - // window. - private InsetsState mTmpAboveInsetsState = new InsetsState(); - /** * Used to prevent recursions when calling * {@link #ensureActivitiesVisible(ActivityRecord, int, boolean, boolean)} @@ -778,13 +770,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp + " parentHidden=" + w.isParentWindowHidden()); } - // Sets mAboveInsets for each window. Windows behind the window providing the insets can - // receive the insets. - if (!w.mAboveInsetsState.equals(mTmpAboveInsetsState)) { - w.mAboveInsetsState.set(mTmpAboveInsetsState); - mWinInsetsChanged.add(w); - } - // If this view is GONE, then skip it -- keep the current frame, and let the caller know // so they can ignore it if they want. (We do the normal layout for INVISIBLE windows, // since that means "perform layout as normal, just don't display"). @@ -818,16 +803,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp + " mContainingFrame=" + w.getContainingFrame() + " mDisplayFrame=" + w.getDisplayFrame()); } - provideInsetsByWindow(w); }; - private void provideInsetsByWindow(WindowState w) { - for (int i = 0; i < w.mProvidedInsetsSources.size(); i++) { - final InsetsSource providedSource = w.mProvidedInsetsSources.valueAt(i); - mTmpAboveInsetsState.addSource(providedSource); - } - } - private final Consumer<WindowState> mPerformLayoutAttached = w -> { if (w.mLayoutAttached) { if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame @@ -1002,7 +979,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final InputChannel inputChannel = mWmService.mInputManager.monitorInput( "PointerEventDispatcher" + mDisplayId, mDisplayId); - mPointerEventDispatcher = new PointerEventDispatcher(inputChannel); + mPointerEventDispatcher = new PointerEventDispatcher(inputChannel, this); // Tap Listeners are supported for: // 1. All physical displays (multi-display). @@ -1031,8 +1008,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mDisplayPolicy.systemReady(); } mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius(); - mDividerControllerLocked = new DockedStackDividerController(this); - mPinnedStackControllerLocked = new PinnedStackController(mWmService, this); + mDividerControllerLocked = new DockedTaskDividerController(this); + mPinnedTaskControllerLocked = new PinnedTaskController(mWmService, this); final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession) .setOpaque(true) @@ -1308,7 +1285,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mInsetsPolicy; } - @Surface.Rotation + @Rotation int getRotation() { return mDisplayRotation.getRotation(); } @@ -1498,7 +1475,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * Returns a valid rotation if the activity can use different orientation than the display. * Otherwise {@link #ROTATION_UNDEFINED}. */ - @Surface.Rotation + @Rotation int rotationForActivityInDifferentOrientation(@NonNull ActivityRecord r) { if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM) { return ROTATION_UNDEFINED; @@ -1572,12 +1549,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } final int rotation = rotationForActivityInDifferentOrientation(r); if (rotation == ROTATION_UNDEFINED) { - // The display rotation won't be changed by current top activity. If there was fixed - // rotation activity, its rotated state should be cleared to cancel the adjustments. - if (hasTopFixedRotationLaunchingApp() - // Avoid breaking recents animation. - && !mFixedRotationLaunchingApp.getTask().isAnimatingByRecents()) { - clearFixedRotationLaunchingApp(); + // The display rotation won't be changed by current top activity. The client side + // adjustments of previous rotated activity should be cleared earlier. Otherwise if + // the current top is in the same process, it may get the rotated state. The transform + // will be cleared later with transition callback to ensure smooth animation. + if (hasTopFixedRotationLaunchingApp()) { + mFixedRotationLaunchingApp.notifyFixedRotationTransform(false /* enabled */); } return false; } @@ -1586,7 +1563,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // has it own policy for bounds, the activity bounds based on parent is unknown. return false; } - if (mPinnedStackControllerLocked.isPipActiveOrWindowingModeChanging()) { + if (mPinnedTaskControllerLocked.isPipActiveOrWindowingModeChanging()) { // Use normal rotation animation because seamless PiP rotation is not supported yet. return false; } @@ -1633,7 +1610,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * Sets the provided record to {@link #mFixedRotationLaunchingApp} if possible to apply fixed * rotation transform to it and indicate that the display may be rotated after it is launched. */ - void setFixedRotationLaunchingApp(@NonNull ActivityRecord r, @Surface.Rotation int rotation) { + void setFixedRotationLaunchingApp(@NonNull ActivityRecord r, @Rotation int rotation) { final WindowToken prevRotatedLaunchingApp = mFixedRotationLaunchingApp; if (prevRotatedLaunchingApp == r && r.getWindowConfiguration().getRotation() == rotation) { @@ -2315,12 +2292,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } - DockedStackDividerController getDockedDividerController() { + DockedTaskDividerController getDockedDividerController() { return mDividerControllerLocked; } - PinnedStackController getPinnedStackController() { - return mPinnedStackControllerLocked; + PinnedTaskController getPinnedTaskController() { + return mPinnedTaskControllerLocked; } /** @@ -2401,10 +2378,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * for bounds calculations. */ void preOnConfigurationChanged() { - final PinnedStackController pinnedStackController = getPinnedStackController(); + final PinnedTaskController pinnedTaskController = getPinnedTaskController(); - if (pinnedStackController != null) { - getPinnedStackController().onConfigurationChanged(); + if (pinnedTaskController != null) { + getPinnedTaskController().onConfigurationChanged(); } } @@ -2511,8 +2488,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void onDisplayInfoChanged() { final DisplayInfo info = mDisplayInfo; - mDisplayFrames.onDisplayInfoUpdated(info, calculateDisplayCutoutForRotation(info.rotation), - calculateRoundedCornersForRotation(info.rotation)); + if (mDisplayFrames.onDisplayInfoUpdated(info, + calculateDisplayCutoutForRotation(info.rotation), + calculateRoundedCornersForRotation(info.rotation))) { + // TODO(b/161810301): Set notifyInsetsChange to true while the server no longer performs + // layout. + mInsetsStateController.onDisplayInfoUpdated(false /* notifyInsetsChange */); + } mInputMonitor.layoutInputConsumers(info.logicalWidth, info.logicalHeight); mDisplayPolicy.onDisplayInfoChanged(info); } @@ -2943,7 +2925,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final boolean imeVisible = imeWin != null && imeWin.isVisible() && imeWin.isDisplayed(); final int imeHeight = getInputMethodWindowVisibleHeight(); - mPinnedStackControllerLocked.setAdjustedForIme(imeVisible, imeHeight); + mPinnedTaskControllerLocked.setAdjustedForIme(imeVisible, imeHeight); } int getInputMethodWindowVisibleHeight() { @@ -2965,27 +2947,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp forAllRootTasks(Task::prepareFreezingTaskBounds); } - void rotateBounds(int oldRotation, int newRotation, Rect bounds) { - getBounds(mTmpRect, newRotation); - rotateBounds(mTmpRect, oldRotation, newRotation, bounds); - } - - void rotateBounds(Rect parentBounds, int oldRotation, int newRotation, Rect bounds) { - // Compute a transform matrix to undo the coordinate space transformation, - // and present the window at the same physical position it previously occupied. - final int deltaRotation = deltaRotation(newRotation, oldRotation); - createRotationMatrix( - deltaRotation, parentBounds.width(), parentBounds.height(), mTmpMatrix); - - mTmpRectF.set(bounds); - mTmpMatrix.mapRect(mTmpRectF); - mTmpRectF.round(bounds); - } - - static int deltaRotation(int oldRotation, int newRotation) { - int delta = newRotation - oldRotation; - if (delta < 0) delta += 4; - return delta; + void rotateBounds(@Rotation int oldRotation, @Rotation int newRotation, Rect inOutBounds) { + // Get display bounds on oldRotation as parent bounds for the rotation. + getBounds(mTmpRect, oldRotation); + RotationUtils.rotateBounds(inOutBounds, mTmpRect, oldRotation, newRotation); } public void setRotationAnimation(ScreenRotationAnimation screenRotationAnimation) { @@ -2999,35 +2964,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mScreenRotationAnimation; } - private static void createRotationMatrix(int rotation, float displayWidth, float displayHeight, - Matrix outMatrix) { - // For rotations without Z-ordering we don't need the target rectangle's position. - createRotationMatrix(rotation, 0 /* rectLeft */, 0 /* rectTop */, displayWidth, - displayHeight, outMatrix); - } - - static void createRotationMatrix(int rotation, float rectLeft, float rectTop, - float displayWidth, float displayHeight, Matrix outMatrix) { - switch (rotation) { - case ROTATION_0: - outMatrix.reset(); - break; - case ROTATION_270: - outMatrix.setRotate(270, 0, 0); - outMatrix.postTranslate(0, displayHeight); - outMatrix.postTranslate(rectTop, 0); - break; - case ROTATION_180: - outMatrix.reset(); - break; - case ROTATION_90: - outMatrix.setRotate(90, 0, 0); - outMatrix.postTranslate(displayWidth, 0); - outMatrix.postTranslate(-rectTop, rectLeft); - break; - } - } - @Override public void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) { @@ -3214,7 +3150,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } pw.println(); - mPinnedStackControllerLocked.dump(prefix, pw); + mPinnedTaskControllerLocked.dump(prefix, pw); pw.println(); mDisplayFrames.dump(prefix, pw); @@ -3653,8 +3589,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } private boolean isImeControlledByApp() { - return mImeInputTarget != null && !WindowConfiguration.isSplitScreenWindowingMode( - mImeInputTarget.getWindowingMode()); + return mImeInputTarget != null && !mImeInputTarget.inMultiWindowMode(); } boolean isImeAttachedToApp() { @@ -3767,8 +3702,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // 2. Assign window layers based on the IME surface parent to make sure it is on top of the // app. assignWindowLayers(true /* setLayoutNeeded */); - // 3. Update the IME control target to apply any inset change and animation. - // 4. Reparent the IME container surface to either the input target app, or the IME window + // 3. The z-order of IME might have been changed. Update the above insets state. + mInsetsStateController.updateAboveInsetsState( + mInputMethodWindow, true /* notifyInsetsChange */); + // 4. Update the IME control target to apply any inset change and animation. + // 5. Reparent the IME container surface to either the input target app, or the IME window // parent. updateImeControlTarget(); } @@ -4300,17 +4238,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp out.set(left, top, left + width, top + height); } - private void getBounds(Rect out, int orientation) { + private void getBounds(Rect out, @Rotation int rotation) { getBounds(out); // Rotate the Rect if needed. final int currentRotation = mDisplayInfo.rotation; - final int rotationDelta = deltaRotation(currentRotation, orientation); + final int rotationDelta = deltaRotation(currentRotation, rotation); if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) { - createRotationMatrix(rotationDelta, mBaseDisplayWidth, mBaseDisplayHeight, mTmpMatrix); - mTmpRectF.set(out); - mTmpMatrix.mapRect(mTmpRectF); - mTmpRectF.round(out); + out.set(0, 0, out.height(), out.width()); } } @@ -4341,14 +4276,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp + " dh=" + mDisplayInfo.logicalHeight); } - // Used to indicate that we have processed the insets windows. This needs to be after - // beginLayoutLw to ensure the raw insets state display related info is initialized. - final InsetsState rawInsetsState = getInsetsStateController().getRawInsetsState(); - mTmpAboveInsetsState = new InsetsState(); - mTmpAboveInsetsState.setDisplayFrame(rawInsetsState.getDisplayFrame()); - mTmpAboveInsetsState.setDisplayCutout(rawInsetsState.getDisplayCutout()); - mTmpAboveInsetsState.mirrorAlwaysVisibleInsetsSources(rawInsetsState); - int seq = mLayoutSeq + 1; if (seq < 0) seq = 0; mLayoutSeq = seq; @@ -5467,9 +5394,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // apply the correct override config. changes = mAtmService.updateGlobalConfigurationLocked(values, false /* initLocale */, false /* persistent */, - UserHandle.USER_NULL /* userId */, deferResume); + UserHandle.USER_NULL /* userId */); } else { - changes = performDisplayOverrideConfigUpdate(values, deferResume); + changes = performDisplayOverrideConfigUpdate(values); } } @@ -5487,7 +5414,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return kept; } - int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume) { + int performDisplayOverrideConfigUpdate(Configuration values) { mTempConfig.setTo(getRequestedOverrideConfiguration()); final int changes = mTempConfig.updateFrom(values); if (changes != 0) { diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java index e4230a2c8760..a37f3f254276 100644 --- a/services/core/java/com/android/server/wm/DisplayFrames.java +++ b/services/core/java/com/android/server/wm/DisplayFrames.java @@ -21,6 +21,7 @@ import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT; +import android.annotation.NonNull; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; import android.view.DisplayCutout; @@ -70,25 +71,28 @@ public class DisplayFrames { * @param info the updated {@link DisplayInfo}. * @param displayCutout the updated {@link DisplayCutout}. * @param roundedCorners the updated {@link RoundedCorners}. + * @return {@code true} if the insets state has been changed; {@code false} otherwise. */ - public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout, - RoundedCorners roundedCorners) { - mDisplayWidth = info.logicalWidth; - mDisplayHeight = info.logicalHeight; + public boolean onDisplayInfoUpdated(DisplayInfo info, @NonNull WmDisplayCutout displayCutout, + @NonNull RoundedCorners roundedCorners) { mRotation = info.rotation; - final WmDisplayCutout wmDisplayCutout = - displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT; final InsetsState state = mInsetsState; - final Rect unrestricted = mUnrestricted; final Rect safe = mDisplayCutoutSafe; - final DisplayCutout cutout = wmDisplayCutout.getDisplayCutout(); + final DisplayCutout cutout = displayCutout.getDisplayCutout(); + if (mDisplayWidth == info.logicalWidth && mDisplayHeight == info.logicalHeight + && state.getDisplayCutout().equals(cutout) + && state.getRoundedCorners().equals(roundedCorners)) { + return false; + } + mDisplayWidth = info.logicalWidth; + mDisplayHeight = info.logicalHeight; + final Rect unrestricted = mUnrestricted; unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight); safe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); state.setDisplayFrame(unrestricted); state.setDisplayCutout(cutout); - state.setRoundedCorners(roundedCorners != null ? roundedCorners - : RoundedCorners.NO_ROUNDED_CORNERS); + state.setRoundedCorners(roundedCorners); if (!cutout.isEmpty()) { if (cutout.getSafeInsetLeft() > 0) { safe.left = unrestricted.left + cutout.getSafeInsetLeft(); @@ -116,6 +120,7 @@ public class DisplayFrames { state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT); state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT); } + return true; } public void dumpDebug(ProtoOutputStream proto, long fieldId) { diff --git a/services/core/java/com/android/server/wm/ScreenshotHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java index 03f4e2891d19..7e16c22ee15e 100644 --- a/services/core/java/com/android/server/wm/ScreenshotHashController.java +++ b/services/core/java/com/android/server/wm/DisplayHashController.java @@ -16,9 +16,8 @@ package com.android.server.wm; -import static android.service.screenshot.ScreenshotHasherService.EXTRA_SCREENSHOT_HASH; -import static android.service.screenshot.ScreenshotHasherService.EXTRA_VERIFICATION_STATUS; -import static android.service.screenshot.ScreenshotHasherService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS; +import static android.service.displayhash.DisplayHasherService.EXTRA_VERIFIED_DISPLAY_HASH; +import static android.service.displayhash.DisplayHasherService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -46,11 +45,12 @@ import android.os.Message; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; -import android.service.screenshot.IScreenshotHasherService; -import android.service.screenshot.ScreenshotHash; -import android.service.screenshot.ScreenshotHasherService; +import android.service.displayhash.DisplayHasherService; +import android.service.displayhash.IDisplayHasherService; import android.util.Slog; import android.view.MagnificationSpec; +import android.view.displayhash.DisplayHash; +import android.view.displayhash.VerifiedDisplayHash; import com.android.internal.annotations.GuardedBy; @@ -61,29 +61,29 @@ import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; /** - * Handles requests into {@link android.service.screenshot.ScreenshotHasherService} + * Handles requests into {@link android.service.displayhash.DisplayHasherService} * * Do not hold the {@link WindowManagerService#mGlobalLock} when calling methods since they are * blocking calls into another service. */ -public class ScreenshotHashController { - private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenshotHashController" : TAG_WM; +public class DisplayHashController { + private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayHashController" : TAG_WM; private static final boolean DEBUG = false; private final Object mServiceConnectionLock = new Object(); @GuardedBy("mServiceConnectionLock") - private ScreenshotHasherServiceConnection mServiceConnection; + private DisplayHasherServiceConnection mServiceConnection; private final Context mContext; /** - * Lock used for the cached {@link #mHashingAlgorithms} array + * Lock used for the cached {@link #mHashAlgorithms} array */ - private final Object mHashingAlgorithmsLock = new Object(); + private final Object mHashAlgorithmsLock = new Object(); @GuardedBy("mHashingAlgorithmsLock") - private String[] mHashingAlgorithms; + private String[] mHashAlgorithms; private final Handler mHandler; @@ -94,24 +94,24 @@ public class ScreenshotHashController { private final RectF mTmpRectF = new RectF(); private interface Command { - void run(IScreenshotHasherService service) throws RemoteException; + void run(IDisplayHasherService service) throws RemoteException; } - ScreenshotHashController(Context context) { + DisplayHashController(Context context) { mContext = context; mHandler = new Handler(Looper.getMainLooper()); mSalt = UUID.randomUUID().toString().getBytes(); } - String[] getSupportedHashingAlgorithms() { + String[] getSupportedHashAlgorithms() { // We have a separate lock for the hashing algorithm array since it doesn't need to make // the request through the service connection. Instead, we have a lock to ensure we can // properly cache the hashing algorithms array so we don't need to call into the // ExtServices process for each request. - synchronized (mHashingAlgorithmsLock) { + synchronized (mHashAlgorithmsLock) { // Already have cached values - if (mHashingAlgorithms != null) { - return mHashingAlgorithms; + if (mHashAlgorithms != null) { + return mHashAlgorithms; } final ServiceInfo serviceInfo = getServiceInfo(); @@ -128,55 +128,48 @@ public class ScreenshotHashController { final int resourceId = serviceInfo.metaData.getInt( SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS); - mHashingAlgorithms = res.getStringArray(resourceId); + mHashAlgorithms = res.getStringArray(resourceId); - return mHashingAlgorithms; + return mHashAlgorithms; } } - boolean verifyScreenshotHash(ScreenshotHash screenshotHash) { + @Nullable + VerifiedDisplayHash verifyDisplayHash(DisplayHash displayHash) { final SyncCommand syncCommand = new SyncCommand(); Bundle results = syncCommand.run((service, remoteCallback) -> { try { - service.verifyScreenshotHash(mSalt, screenshotHash, remoteCallback); + service.verifyDisplayHash(mSalt, displayHash, remoteCallback); } catch (RemoteException e) { - Slog.e(TAG, "Failed to invoke verifyScreenshotHash command"); + Slog.e(TAG, "Failed to invoke verifyDisplayHash command"); } }); - return results.getBoolean(EXTRA_VERIFICATION_STATUS); + return results.getParcelable(EXTRA_VERIFIED_DISPLAY_HASH); } - ScreenshotHash generateScreenshotHash(HardwareBuffer screenshot, Rect bounds, - String hashAlgorithm) { - final SyncCommand syncCommand = new SyncCommand(); - Bundle results = syncCommand.run((service, remoteCallback) -> { - try { - service.generateScreenshotHash(mSalt, screenshot, bounds, hashAlgorithm, - remoteCallback); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to invoke generateScreenshotHash command", e); - } - }); - - return results.getParcelable(EXTRA_SCREENSHOT_HASH); + void generateDisplayHash(HardwareBuffer buffer, Rect bounds, + String hashAlgorithm, RemoteCallback callback) { + connectAndRun( + service -> service.generateDisplayHash(mSalt, buffer, bounds, hashAlgorithm, + callback)); } /** - * Calculate the bounds to take the screenshot when generating the ScreenshotHash. This - * takes into account window transform, magnification, and display bounds. + * Calculate the bounds to generate the hash for. This takes into account window transform, + * magnification, and display bounds. * * Call while holding {@link WindowManagerService#mGlobalLock} * - * @param win Window that the ScreenshotHash is generated for. - * @param boundsInWindow The bounds passed in about where in the window to take the screenshot. - * @param outBounds The result of the calculated bounds + * @param win Window that the DisplayHash is generated for. + * @param boundsInWindow The bounds in the window where to generate the hash. + * @param outBounds The result of the calculated bounds */ - void calculateScreenshotHashBoundsLocked(WindowState win, Rect boundsInWindow, + void calculateDisplayHashBoundsLocked(WindowState win, Rect boundsInWindow, Rect outBounds) { if (DEBUG) { Slog.d(TAG, - "calculateScreenshotHashBoundsLocked: boundsInWindow=" + boundsInWindow); + "calculateDisplayHashBoundsLocked: boundsInWindow=" + boundsInWindow); } outBounds.set(boundsInWindow); @@ -195,7 +188,7 @@ public class ScreenshotHashController { if (DEBUG) { Slog.d(TAG, - "calculateScreenshotHashBoundsLocked: boundsIntersectWindow=" + outBounds); + "calculateDisplayHashBoundsLocked: boundsIntersectWindow=" + outBounds); } if (outBounds.isEmpty()) { @@ -210,7 +203,7 @@ public class ScreenshotHashController { outBounds.set((int) mTmpRectF.left, (int) mTmpRectF.top, (int) mTmpRectF.right, (int) mTmpRectF.bottom); if (DEBUG) { - Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsInDisplay=" + outBounds); + Slog.d(TAG, "calculateDisplayHashBoundsLocked: boundsInDisplay=" + outBounds); } // Apply the magnification spec values to the bounds since the content could be magnified @@ -221,7 +214,7 @@ public class ScreenshotHashController { } if (DEBUG) { - Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsWithMagnification=" + Slog.d(TAG, "calculateDisplayHashBoundsLocked: boundsWithMagnification=" + outBounds); } @@ -229,12 +222,12 @@ public class ScreenshotHashController { return; } - // Intersect with the display bounds since it shouldn't take a screenshot of content - // outside the display since it's not visible to the user. + // Intersect with the display bounds since content outside the display are not visible to + // the user. final Rect displayBounds = displayContent.getBounds(); outBounds.intersectUnchecked(displayBounds); if (DEBUG) { - Slog.d(TAG, "calculateScreenshotHashBoundsLocked: finalBounds=" + outBounds); + Slog.d(TAG, "calculateDisplayHashBoundsLocked: finalBounds=" + outBounds); } } @@ -248,7 +241,7 @@ public class ScreenshotHashController { if (DEBUG) Slog.v(TAG, "creating connection"); // Create the connection - mServiceConnection = new ScreenshotHasherServiceConnection(); + mServiceConnection = new DisplayHasherServiceConnection(); final ComponentName component = getServiceComponentName(); if (DEBUG) Slog.v(TAG, "binding to: " + component); @@ -279,7 +272,7 @@ public class ScreenshotHashController { return null; } - final Intent intent = new Intent(ScreenshotHasherService.SERVICE_INTERFACE); + final Intent intent = new Intent(DisplayHasherService.SERVICE_INTERFACE); intent.setPackage(packageName); final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); @@ -296,10 +289,10 @@ public class ScreenshotHashController { if (serviceInfo == null) return null; final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name); - if (!Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE + if (!Manifest.permission.BIND_DISPLAY_HASHER_SERVICE .equals(serviceInfo.permission)) { Slog.w(TAG, name.flattenToShortString() + " requires permission " - + Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE); + + Manifest.permission.BIND_DISPLAY_HASHER_SERVICE); return null; } @@ -312,7 +305,7 @@ public class ScreenshotHashController { private Bundle mResult; private final CountDownLatch mCountDownLatch = new CountDownLatch(1); - public Bundle run(BiConsumer<IScreenshotHasherService, RemoteCallback> func) { + public Bundle run(BiConsumer<IDisplayHasherService, RemoteCallback> func) { connectAndRun(service -> { RemoteCallback callback = new RemoteCallback(result -> { mResult = result; @@ -331,9 +324,9 @@ public class ScreenshotHashController { } } - private class ScreenshotHasherServiceConnection implements ServiceConnection { + private class DisplayHasherServiceConnection implements ServiceConnection { @GuardedBy("mServiceConnectionLock") - private IScreenshotHasherService mRemoteService; + private IDisplayHasherService mRemoteService; @GuardedBy("mServiceConnectionLock") private ArrayList<Command> mQueuedCommands; @@ -342,7 +335,7 @@ public class ScreenshotHashController { public void onServiceConnected(ComponentName name, IBinder service) { if (DEBUG) Slog.v(TAG, "onServiceConnected(): " + name); synchronized (mServiceConnectionLock) { - mRemoteService = IScreenshotHasherService.Stub.asInterface(service); + mRemoteService = IDisplayHasherService.Stub.asInterface(service); if (mQueuedCommands != null) { final int size = mQueuedCommands.size(); if (DEBUG) Slog.d(TAG, "running " + size + " queued commands"); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 5460e36a5f1b..32152ec85493 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -24,6 +24,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; +import static android.util.RotationUtils.deltaRotation; +import static android.util.RotationUtils.rotateBounds; import static android.view.Display.TYPE_INTERNAL; import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; @@ -1046,11 +1048,17 @@ public class DisplayPolicy { return; } - // Get displayFrames bounds - sTmpDisplayFrameBounds.set(0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight); + // Get displayFrames bounds as it is on WindowState's rotation. + final int deltaRotation = deltaRotation(windowRotation, displayFrames.mRotation); + if (deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270) { + sTmpDisplayFrameBounds.set( + 0, 0, displayFrames.mDisplayHeight, displayFrames.mDisplayWidth); + } else { + sTmpDisplayFrameBounds.set( + 0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight); + } // Rotate the WindowState's bounds based on the displayFrames rotation - mDisplayContent.rotateBounds(sTmpDisplayFrameBounds, windowRotation, - displayFrames.mRotation, outBounds); + rotateBounds(outBounds, sTmpDisplayFrameBounds, deltaRotation); } /** @@ -2678,8 +2686,9 @@ public class DisplayPolicy { boolean newImmersiveMode = isImmersiveMode(win); if (oldImmersiveMode != newImmersiveMode) { mLastImmersiveMode = newImmersiveMode; - final String pkg = win.getOwningPackage(); - mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode, + // The immersive confirmation window should be attached to the immersive window root. + final int rootDisplayAreaId = win.getRootDisplayArea().mFeatureId; + mImmersiveModeConfirmation.immersiveModeChangedLw(rootDisplayAreaId, newImmersiveMode, mService.mPolicy.isUserSetupComplete(), isNavBarEmpty(disableFlags)); } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index b106657dee99..63cb38a59349 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.util.RotationUtils.deltaRotation; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; @@ -475,7 +476,7 @@ public class DisplayRotation { "Display id=%d rotation changed to %d from %d, lastOrientation=%d", displayId, rotation, oldRotation, lastOrientation); - if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) { + if (deltaRotation(oldRotation, rotation) != Surface.ROTATION_180) { mDisplayContent.mWaitingForConfig = true; } @@ -788,8 +789,13 @@ public class DisplayRotation { mFixedToUserRotation = fixedToUserRotation; mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, fixedToUserRotation); - mService.updateRotation(true /* alwaysSendConfiguration */, - false /* forceRelayout */); + if (mDisplayContent.mFocusedApp != null) { + // We record the last focused TDA that respects orientation request, check if this + // change may affect it. + mDisplayContent.onLastFocusedTaskDisplayAreaChanged( + mDisplayContent.mFocusedApp.getDisplayArea()); + } + mDisplayContent.updateOrientation(); } @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedTaskDividerController.java index de4bdaa57efa..fb9d06441536 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedTaskDividerController.java @@ -19,16 +19,16 @@ package com.android.server.wm; import android.graphics.Rect; /** - * Keeps information about the docked stack divider. + * Keeps information about the docked task divider. */ -public class DockedStackDividerController { +public class DockedTaskDividerController { private final DisplayContent mDisplayContent; private boolean mResizing; private final Rect mTouchRegion = new Rect(); - DockedStackDividerController(DisplayContent displayContent) { + DockedTaskDividerController(DisplayContent displayContent) { mDisplayContent = displayContent; } @@ -58,6 +58,6 @@ public class DockedStackDividerController { private void resetDragResizingChangeReported() { mDisplayContent.forAllWindows(WindowState::resetDragResizingChangeReported, - true /* traverseTopToBottom */ ); + true /* traverseTopToBottom */); } } diff --git a/services/core/java/com/android/server/wm/FactoryErrorDialog.java b/services/core/java/com/android/server/wm/FactoryErrorDialog.java index 88b5475cc3a2..afdf1ee4de7f 100644 --- a/services/core/java/com/android/server/wm/FactoryErrorDialog.java +++ b/services/core/java/com/android/server/wm/FactoryErrorDialog.java @@ -41,6 +41,11 @@ final class FactoryErrorDialog extends BaseErrorDialog { public void onStop() { } + @Override + protected void closeDialog() { + /* Do nothing */ + } + private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { throw new RuntimeException("Rebooting from failed factory test"); diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java index 9286a4693711..567b6c2b964e 100644 --- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java +++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java @@ -19,9 +19,11 @@ package com.android.server.wm; import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.view.Display.DEFAULT_DISPLAY; +import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityThread; import android.content.BroadcastReceiver; @@ -32,10 +34,12 @@ import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.drawable.ColorDrawable; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -43,6 +47,7 @@ import android.util.DisplayMetrics; import android.util.Slog; import android.view.Display; import android.view.Gravity; +import android.view.IWindowManager; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -50,6 +55,7 @@ import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowInsets.Type; import android.view.WindowManager; +import android.view.WindowManagerGlobal; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -67,6 +73,8 @@ public class ImmersiveModeConfirmation { private static final boolean DEBUG = false; private static final boolean DEBUG_SHOW_EVERY_TIME = false; // super annoying, use with caution private static final String CONFIRMED = "confirmed"; + private static final int IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE = + WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; private static boolean sConfirmed; @@ -78,7 +86,15 @@ public class ImmersiveModeConfirmation { private ClingWindowView mClingWindow; private long mPanicTime; + /** The last {@link WindowManager} that is used to add the confirmation window. */ + @Nullable private WindowManager mWindowManager; + /** + * The WindowContext that is registered with {@link #mWindowManager} with options to specify the + * {@link RootDisplayArea} to attach the confirmation window. + */ + @Nullable + private Context mWindowContext; // Local copy of vr mode enabled state, to avoid calling into VrManager with // the lock held. private boolean mVrModeEnabled; @@ -132,7 +148,7 @@ public class ImmersiveModeConfirmation { } } - void immersiveModeChangedLw(String pkg, boolean isImmersiveMode, + void immersiveModeChangedLw(int rootDisplayAreaId, boolean isImmersiveMode, boolean userSetupComplete, boolean navBarEmpty) { mHandler.removeMessages(H.SHOW); if (isImmersiveMode) { @@ -143,7 +159,9 @@ public class ImmersiveModeConfirmation { && !navBarEmpty && !UserManager.isDeviceInDemoMode(mContext) && (mLockTaskState != LOCK_TASK_MODE_LOCKED)) { - mHandler.sendEmptyMessageDelayed(H.SHOW, mShowDelayMs); + final Message msg = mHandler.obtainMessage(H.SHOW); + msg.arg1 = rootDisplayAreaId; + mHandler.sendMessageDelayed(msg, mShowDelayMs); } } else { mHandler.sendEmptyMessage(H.HIDE); @@ -175,7 +193,8 @@ public class ImmersiveModeConfirmation { private void handleHide() { if (mClingWindow != null) { if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation"); - getWindowManager().removeView(mClingWindow); + // We don't care which root display area the window manager is specifying for removal. + getWindowManager(FEATURE_UNDEFINED).removeView(mClingWindow); mClingWindow = null; } } @@ -184,7 +203,7 @@ public class ImmersiveModeConfirmation { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL, + IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, @@ -353,23 +372,57 @@ public class ImmersiveModeConfirmation { * DO HOLD THE WINDOW MANAGER LOCK WHEN CALLING THIS METHOD * The reason why we add this method is to avoid the deadlock of WMG->WMS and WMS->WMG * when ImmersiveModeConfirmation object is created. + * + * @return the WindowManager specifying with the {@code rootDisplayAreaId} to attach the + * confirmation window. */ - private WindowManager getWindowManager() { - if (mWindowManager == null) { - mWindowManager = (WindowManager) - mContext.getSystemService(Context.WINDOW_SERVICE); + private WindowManager getWindowManager(int rootDisplayAreaId) { + if (mWindowManager == null || mWindowContext == null) { + // Create window context to specify the RootDisplayArea + final Bundle options = getOptionsForWindowContext(rootDisplayAreaId); + mWindowContext = mContext.createWindowContext( + IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options); + mWindowManager = mWindowContext.getSystemService(WindowManager.class); + return mWindowManager; + } + + // Update the window context and window manager to specify the RootDisplayArea + final Bundle options = getOptionsForWindowContext(rootDisplayAreaId); + final IWindowManager wms = WindowManagerGlobal.getWindowManagerService(); + try { + wms.registerWindowContextListener(mWindowContext.getWindowContextToken(), + IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, mContext.getDisplayId(), options); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } + return mWindowManager; } - private void handleShow() { + /** + * Returns options that specify the {@link RootDisplayArea} to attach the confirmation window. + * {@code null} if the {@code rootDisplayAreaId} is {@link FEATURE_UNDEFINED}. + */ + @Nullable + private Bundle getOptionsForWindowContext(int rootDisplayAreaId) { + // In case we don't care which root display area the window manager is specifying. + if (rootDisplayAreaId == FEATURE_UNDEFINED) { + return null; + } + + final Bundle options = new Bundle(); + options.putInt(DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId); + return options; + } + + private void handleShow(int rootDisplayAreaId) { if (DEBUG) Slog.d(TAG, "Showing immersive mode confirmation"); mClingWindow = new ClingWindowView(mContext, mConfirm); // show the confirmation WindowManager.LayoutParams lp = getClingWindowLayoutParams(); - getWindowManager().addView(mClingWindow, lp); + getWindowManager(rootDisplayAreaId).addView(mClingWindow, lp); } private final Runnable mConfirm = new Runnable() { @@ -396,7 +449,7 @@ public class ImmersiveModeConfirmation { public void handleMessage(Message msg) { switch(msg.what) { case SHOW: - handleShow(); + handleShow(msg.arg1); break; case HIDE: handleHide(); diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 580d3285c6a5..75176df6aaf7 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -27,6 +27,9 @@ import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_INVALID; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; +import static android.view.WindowInsets.Type.displayCutout; +import static android.view.WindowInsets.Type.mandatorySystemGestures; +import static android.view.WindowInsets.Type.systemGestures; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; @@ -293,6 +296,76 @@ class InsetsStateController { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } + /** + * Updates {@link WindowState#mAboveInsetsState} for all windows in the display while the + * z-order of a window is changed. + * + * @param win The window whose z-order has changed. + * @param notifyInsetsChange {@code true} if the clients should be notified about the change. + */ + void updateAboveInsetsState(WindowState win, boolean notifyInsetsChange) { + if (win == null || win.getDisplayContent() != mDisplayContent) { + return; + } + final boolean[] aboveWin = { true }; + final InsetsState aboveInsetsState = new InsetsState(); + aboveInsetsState.set(mState, + displayCutout() | systemGestures() | mandatorySystemGestures()); + final SparseArray<InsetsSource> winProvidedSources = win.mProvidedInsetsSources; + final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>(); + mDisplayContent.forAllWindows(w -> { + if (aboveWin[0]) { + if (w == win) { + aboveWin[0] = false; + if (!win.mAboveInsetsState.equals(aboveInsetsState)) { + win.mAboveInsetsState.set(aboveInsetsState); + insetsChangedWindows.add(win); + } + return winProvidedSources.size() == 0; + } else { + final SparseArray<InsetsSource> providedSources = w.mProvidedInsetsSources; + for (int i = providedSources.size() - 1; i >= 0; i--) { + aboveInsetsState.addSource(providedSources.valueAt(i)); + } + if (winProvidedSources.size() == 0) { + return false; + } + boolean changed = false; + for (int i = winProvidedSources.size() - 1; i >= 0; i--) { + changed |= w.mAboveInsetsState.removeSource(winProvidedSources.keyAt(i)); + } + if (changed) { + insetsChangedWindows.add(w); + } + } + } else { + for (int i = winProvidedSources.size() - 1; i >= 0; i--) { + w.mAboveInsetsState.addSource(winProvidedSources.valueAt(i)); + } + insetsChangedWindows.add(w); + } + return false; + }, true /* traverseTopToBottom */); + if (notifyInsetsChange) { + for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) { + mDispatchInsetsChanged.accept(insetsChangedWindows.get(i)); + } + } + } + + void onDisplayInfoUpdated(boolean notifyInsetsChange) { + final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>(); + mDisplayContent.forAllWindows(w -> { + w.mAboveInsetsState.set(mState, displayCutout()); + insetsChangedWindows.add(w); + }, true /* traverseTopToBottom */); + if (notifyInsetsChange) { + for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) { + mDispatchInsetsChanged.accept(insetsChangedWindows.get(i)); + } + } + } + void onInsetsModified(InsetsControlTarget caller) { boolean changed = false; for (int i = mProviders.size() - 1; i >= 0; i--) { diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 4b3a43432fc5..e18516d7bc3a 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -141,6 +141,7 @@ public class LockTaskController { private final IBinder mToken = new LockTaskToken(); private final ActivityTaskSupervisor mSupervisor; private final Context mContext; + private final TaskChangeNotificationController mTaskChangeNotificationController; // The following system services cannot be final, because they do not exist when this class // is instantiated during device boot @@ -204,10 +205,11 @@ public class LockTaskController { private int mPendingDisableFromDismiss = UserHandle.USER_NULL; LockTaskController(Context context, ActivityTaskSupervisor supervisor, - Handler handler) { + Handler handler, TaskChangeNotificationController taskChangeNotificationController) { mContext = context; mSupervisor = supervisor; mHandler = handler; + mTaskChangeNotificationController = taskChangeNotificationController; } /** @@ -532,6 +534,7 @@ public class LockTaskController { // thread, which makes it guarded by ATMS#mGlobalLock as ATMS#getLockTaskModeState. final int oldLockTaskModeState = mLockTaskModeState; mLockTaskModeState = LOCK_TASK_MODE_NONE; + mTaskChangeNotificationController.notifyLockTaskModeChanged(mLockTaskModeState); // When lock task ends, we enable the status bars. try { setStatusBarState(mLockTaskModeState, userId); @@ -661,6 +664,7 @@ public class LockTaskController { } mWindowManager.onLockTaskStateChanged(lockTaskModeState); mLockTaskModeState = lockTaskModeState; + mTaskChangeNotificationController.notifyLockTaskModeChanged(mLockTaskModeState); setStatusBarState(lockTaskModeState, userId); setKeyguardState(lockTaskModeState, userId); if (getDevicePolicyManager() != null) { diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java new file mode 100644 index 000000000000..5d6d51377c3f --- /dev/null +++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java @@ -0,0 +1,143 @@ +/* + * 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.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS; +import static com.android.server.wm.AnimationAdapterProto.REMOTE; +import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; + +import android.graphics.Rect; +import android.os.SystemClock; +import android.util.proto.ProtoOutputStream; +import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.server.policy.WindowManagerPolicy; + +import java.io.PrintWriter; +import java.util.ArrayList; + +class NonAppWindowAnimationAdapter implements AnimationAdapter { + + private final WindowState mWindow; + private RemoteAnimationTarget mTarget; + private SurfaceControl mCapturedLeash; + + private long mDurationHint; + private long mStatusBarTransitionDelay; + + @Override + public boolean getShowWallpaper() { + return false; + } + + NonAppWindowAnimationAdapter(WindowState w, + long durationHint, long statusBarTransitionDelay) { + mWindow = w; + mDurationHint = durationHint; + mStatusBarTransitionDelay = statusBarTransitionDelay; + } + + /** + * Creates and starts remote animations for all the visible non app windows. + * + * @return RemoteAnimationTarget[] targets for all the visible non app windows + */ + public static RemoteAnimationTarget[] startNonAppWindowAnimationsForKeyguardExit( + WindowManagerService service, long durationHint, long statusBarTransitionDelay) { + final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); + + final WindowManagerPolicy policy = service.mPolicy; + service.mRoot.forAllWindows(nonAppWindow -> { + if (nonAppWindow.mActivityRecord == null && policy.canBeHiddenByKeyguardLw(nonAppWindow) + && nonAppWindow.wouldBeVisibleIfPolicyIgnored() && !nonAppWindow.isVisible()) { + final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter( + nonAppWindow, durationHint, statusBarTransitionDelay); + nonAppWindow.startAnimation(nonAppWindow.getPendingTransaction(), + nonAppAdapter, false /* hidden */, ANIMATION_TYPE_WINDOW_ANIMATION); + targets.add(nonAppAdapter.createRemoteAnimationTarget()); + } + }, true /* traverseTopToBottom */); + return targets.toArray(new RemoteAnimationTarget[targets.size()]); + } + + /** + * Create a remote animation target for this animation adapter. + */ + RemoteAnimationTarget createRemoteAnimationTarget() { + mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false, + new Rect(), null, mWindow.getPrefixOrderIndex(), mWindow.getLastSurfacePosition(), + mWindow.getBounds(), null, mWindow.getWindowConfiguration(), true, null, null, + null); + return mTarget; + } + + @Override + public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, + int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) { + mCapturedLeash = animationLeash; + } + + @Override + public long getDurationHint() { + return mDurationHint; + } + + @Override + public long getStatusBarTransitionsStartTime() { + return SystemClock.uptimeMillis() + mStatusBarTransitionDelay; + } + + /** + * @return the leash for this animation (only valid after the non app window surface animation + * has started). + */ + SurfaceControl getLeash() { + return mCapturedLeash; + } + + @Override + public void onAnimationCancelled(SurfaceControl animationLeash) { + ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationCancelled"); + } + + @Override + public void dump(PrintWriter pw, String prefix) { + pw.print(prefix); + pw.print("token="); + pw.println(mWindow.mToken); + if (mTarget != null) { + pw.print(prefix); + pw.println("Target:"); + mTarget.dump(pw, prefix + " "); + } else { + pw.print(prefix); + pw.println("Target: null"); + } + } + + @Override + public void dumpDebug(ProtoOutputStream proto) { + final long token = proto.start(REMOTE); + if (mTarget != null) { + mTarget.dumpDebug(proto, TARGET); + } + proto.end(token); + } +} diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java index 8fe2481f9eda..15e078b478b8 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedTaskController.java @@ -29,18 +29,18 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.Slog; import android.view.DisplayInfo; -import android.view.IPinnedStackListener; +import android.view.IPinnedTaskListener; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; /** - * Holds the common state of the pinned stack between the system and SystemUI. If SystemUI ever + * Holds the common state of the pinned task between the system and SystemUI. If SystemUI ever * needs to be restarted, it will be notified with the last known state. * - * Changes to the pinned stack also flow through this controller, and generally, the system only - * changes the pinned stack bounds through this controller in two ways: + * Changes to the pinned task also flow through this controller, and generally, the system only + * changes the pinned task bounds through this controller in two ways: * * 1) When first entering PiP: the controller returns the valid bounds given, taking aspect ratio * and IME state into account. @@ -49,18 +49,18 @@ import java.util.List; * SystemUI adjustments (ie. expanded for menu, interaction, etc). * * Other changes in the system, including adjustment of IME, configuration change, and more are - * handled by SystemUI (similar to the docked stack divider). + * handled by SystemUI (similar to the docked task divider). */ -class PinnedStackController { +class PinnedTaskController { - private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedStackController" : TAG_WM; + private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedTaskController" : TAG_WM; private final WindowManagerService mService; private final DisplayContent mDisplayContent; - private IPinnedStackListener mPinnedStackListener; - private final PinnedStackListenerDeathHandler mPinnedStackListenerDeathHandler = - new PinnedStackListenerDeathHandler(); + private IPinnedTaskListener mPinnedTaskListener; + private final PinnedTaskListenerDeathHandler mPinnedTaskListenerDeathHandler = + new PinnedTaskListenerDeathHandler(); /** Whether the PiP is entering or leaving. */ private boolean mIsPipWindowingModeChanging; @@ -72,7 +72,7 @@ class PinnedStackController { private ArrayList<RemoteAction> mActions = new ArrayList<>(); private float mAspectRatio = -1f; - // Used to calculate stack bounds across rotations + // Used to calculate task bounds across rotations private final DisplayInfo mDisplayInfo = new DisplayInfo(); // The aspect ratio bounds of the PIP. @@ -85,19 +85,19 @@ class PinnedStackController { /** * Handler for the case where the listener dies. */ - private class PinnedStackListenerDeathHandler implements IBinder.DeathRecipient { + private class PinnedTaskListenerDeathHandler implements IBinder.DeathRecipient { @Override public void binderDied() { // Clean up the state if the listener dies - if (mPinnedStackListener != null) { - mPinnedStackListener.asBinder().unlinkToDeath(mPinnedStackListenerDeathHandler, 0); + if (mPinnedTaskListener != null) { + mPinnedTaskListener.asBinder().unlinkToDeath(mPinnedTaskListenerDeathHandler, 0); } - mPinnedStackListener = null; + mPinnedTaskListener = null; } } - PinnedStackController(WindowManagerService service, DisplayContent displayContent) { + PinnedTaskController(WindowManagerService service, DisplayContent displayContent) { mService = service; mDisplayContent = displayContent; mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo()); @@ -121,17 +121,17 @@ class PinnedStackController { } /** - * Registers a pinned stack listener. + * Registers a pinned task listener. */ - void registerPinnedStackListener(IPinnedStackListener listener) { + void registerPinnedTaskListener(IPinnedTaskListener listener) { try { - listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0); - mPinnedStackListener = listener; + listener.asBinder().linkToDeath(mPinnedTaskListenerDeathHandler, 0); + mPinnedTaskListener = listener; notifyImeVisibilityChanged(mIsImeShowing, mImeHeight); notifyMovementBoundsChanged(false /* fromImeAdjustment */); notifyActionsChanged(mActions); } catch (RemoteException e) { - Log.e(TAG, "Failed to register pinned stack listener", e); + Log.e(TAG, "Failed to register pinned task listener", e); } } @@ -139,8 +139,8 @@ class PinnedStackController { * @return whether the given {@param aspectRatio} is valid. */ public boolean isValidPictureInPictureAspectRatio(float aspectRatio) { - return Float.compare(mMinAspectRatio, aspectRatio) <= 0 && - Float.compare(aspectRatio, mMaxAspectRatio) <= 0; + return Float.compare(mMinAspectRatio, aspectRatio) <= 0 + && Float.compare(aspectRatio, mMaxAspectRatio) <= 0; } /** Returns {@code true} if the PiP is on screen or is changing windowing mode. */ @@ -152,7 +152,7 @@ class PinnedStackController { return pinnedTask != null && pinnedTask.hasChild(); } - /** Sets whether a visible stack is changing from or to pinned mode. */ + /** Sets whether a visible task is changing from or to pinned mode. */ void setPipWindowingModeChanging(boolean isPipWindowingModeChanging) { mIsPipWindowingModeChanging = isPipWindowingModeChanging; } @@ -162,9 +162,9 @@ class PinnedStackController { * so that the default bounds will be returned for the next session. */ void onActivityHidden(ComponentName componentName) { - if (mPinnedStackListener == null) return; + if (mPinnedTaskListener == null) return; try { - mPinnedStackListener.onActivityHidden(componentName); + mPinnedTaskListener.onActivityHidden(componentName); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering reset reentry fraction event.", e); } @@ -223,9 +223,9 @@ class PinnedStackController { * Notifies listeners that the PIP needs to be adjusted for the IME. */ private void notifyImeVisibilityChanged(boolean imeVisible, int imeHeight) { - if (mPinnedStackListener != null) { + if (mPinnedTaskListener != null) { try { - mPinnedStackListener.onImeVisibilityChanged(imeVisible, imeHeight); + mPinnedTaskListener.onImeVisibilityChanged(imeVisible, imeHeight); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering bounds changed event.", e); } @@ -233,9 +233,9 @@ class PinnedStackController { } private void notifyAspectRatioChanged(float aspectRatio) { - if (mPinnedStackListener == null) return; + if (mPinnedTaskListener == null) return; try { - mPinnedStackListener.onAspectRatioChanged(aspectRatio); + mPinnedTaskListener.onAspectRatioChanged(aspectRatio); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering aspect ratio changed event.", e); } @@ -245,9 +245,9 @@ class PinnedStackController { * Notifies listeners that the PIP actions have changed. */ private void notifyActionsChanged(List<RemoteAction> actions) { - if (mPinnedStackListener != null) { + if (mPinnedTaskListener != null) { try { - mPinnedStackListener.onActionsChanged(new ParceledListSlice(actions)); + mPinnedTaskListener.onActionsChanged(new ParceledListSlice(actions)); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering actions changed event.", e); } @@ -259,11 +259,11 @@ class PinnedStackController { */ private void notifyMovementBoundsChanged(boolean fromImeAdjustment) { synchronized (mService.mGlobalLock) { - if (mPinnedStackListener == null) { + if (mPinnedTaskListener == null) { return; } try { - mPinnedStackListener.onMovementBoundsChanged(fromImeAdjustment); + mPinnedTaskListener.onMovementBoundsChanged(fromImeAdjustment); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering actions changed event.", e); } @@ -271,7 +271,7 @@ class PinnedStackController { } void dump(String prefix, PrintWriter pw) { - pw.println(prefix + "PinnedStackController"); + pw.println(prefix + "PinnedTaskController"); pw.println(prefix + " mIsImeShowing=" + mIsImeShowing); pw.println(prefix + " mImeHeight=" + mImeHeight); pw.println(prefix + " mAspectRatio=" + mAspectRatio); diff --git a/services/core/java/com/android/server/wm/PointerEventDispatcher.java b/services/core/java/com/android/server/wm/PointerEventDispatcher.java index 6b8144c69079..9bc7e93443cb 100644 --- a/services/core/java/com/android/server/wm/PointerEventDispatcher.java +++ b/services/core/java/com/android/server/wm/PointerEventDispatcher.java @@ -16,11 +16,15 @@ package com.android.server.wm; +import static com.android.server.input.InputManagerService.ENABLE_PER_WINDOW_INPUT_ROTATION; + +import android.graphics.Point; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.MotionEvent; +import android.view.Surface; import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.server.UiThread; @@ -31,8 +35,12 @@ public class PointerEventDispatcher extends InputEventReceiver { private final ArrayList<PointerEventListener> mListeners = new ArrayList<>(); private PointerEventListener[] mListenersArray = new PointerEventListener[0]; - public PointerEventDispatcher(InputChannel inputChannel) { + private final DisplayContent mDisplayContent; + private final Point mTmpSize = new Point(); + + public PointerEventDispatcher(InputChannel inputChannel, DisplayContent dc) { super(inputChannel, UiThread.getHandler().getLooper()); + mDisplayContent = dc; } @Override @@ -40,7 +48,16 @@ public class PointerEventDispatcher extends InputEventReceiver { try { if (event instanceof MotionEvent && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { - final MotionEvent motionEvent = (MotionEvent) event; + MotionEvent motionEvent = (MotionEvent) event; + if (ENABLE_PER_WINDOW_INPUT_ROTATION) { + final int rotation = mDisplayContent.getRotation(); + if (rotation != Surface.ROTATION_0) { + mDisplayContent.getDisplay().getRealSize(mTmpSize); + motionEvent = MotionEvent.obtain(motionEvent); + motionEvent.transform(MotionEvent.createRotateMatrix( + rotation, mTmpSize.x, mTmpSize.y)); + } + } PointerEventListener[] listeners; synchronized (mListeners) { if (mListenersArray == null) { diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 392f27ead2bb..42cb96f65738 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; + import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS; import static com.android.server.wm.AnimationAdapterProto.REMOTE; import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; @@ -126,7 +129,7 @@ class RemoteAnimationController implements DeathRecipient { final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations(); // TODO(bc-unlock): Create the remote non app animation targets (if any) - final RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0]; + final RemoteAnimationTarget[] nonAppTargets = createNonAppWindowAnimations(transit); mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { try { @@ -215,6 +218,17 @@ class RemoteAnimationController implements DeathRecipient { }, mPendingWallpaperAnimations); } + private RemoteAnimationTarget[] createNonAppWindowAnimations( + @WindowManager.TransitionOldType int transit) { + ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createNonAppWindowAnimations()"); + return (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY + || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER) + ? NonAppWindowAnimationAdapter.startNonAppWindowAnimationsForKeyguardExit(mService, + mRemoteAnimationAdapter.getDuration(), + mRemoteAnimationAdapter.getStatusBarTransitionDelay()) + : new RemoteAnimationTarget[0]; + } + private void onAnimationFinished() { ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationFinished(): mPendingAnimations=%d", mPendingAnimations.size()); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index bb5e8bf486f4..3f9ea1fd2afd 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -650,6 +650,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } @Override + void dispatchConfigurationToChildren() { + final Configuration configuration = getConfiguration(); + for (int i = getChildCount() - 1; i >= 0; i--) { + final DisplayContent displayContent = getChildAt(i); + if (displayContent.isDefaultDisplay) { + // The global configuration is also the override configuration of default display. + displayContent.performDisplayOverrideConfigUpdate(configuration); + } else { + displayContent.onConfigurationChanged(configuration); + } + } + } + + @Override public void onConfigurationChanged(Configuration newParentConfig) { prepareFreezingTaskBounds(); super.onConfigurationChanged(newParentConfig); diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 533c82e599c9..95a4f69edd57 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.util.RotationUtils.deltaRotation; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC; @@ -174,7 +175,7 @@ class ScreenRotationAnimation { // apply rotation animation because there should be a top app shown as rotated. So the // specified original rotation customizes the direction of animation to have better look // when restoring the rotated app to the same rotation as current display. - final int delta = DisplayContent.deltaRotation(originalRotation, realOriginalRotation); + final int delta = deltaRotation(originalRotation, realOriginalRotation); final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270; mOriginalWidth = flipped ? originalHeight : originalWidth; mOriginalHeight = flipped ? originalWidth : originalHeight; @@ -330,7 +331,7 @@ class ScreenRotationAnimation { // Compute the transformation matrix that must be applied // to the snapshot to make it stay in the same original position // with the current screen rotation. - int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0); + int delta = deltaRotation(rotation, Surface.ROTATION_0); RotationAnimationUtils.createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix); setRotationTransform(t, mSnapshotInitialMatrix); @@ -352,8 +353,7 @@ class ScreenRotationAnimation { mStarted = true; // Figure out how the screen has moved from the original rotation. - int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation); - + int delta = deltaRotation(mCurRotation, mOriginalRotation); final boolean customAnim; if (exitAnim != 0 && enterAnim != 0) { diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 8b186796db8d..b82a30847c8d 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -54,10 +54,10 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.Process; +import android.os.RemoteCallback; import android.os.RemoteException; import android.os.Trace; import android.os.UserHandle; -import android.service.screenshot.ScreenshotHash; import android.text.TextUtils; import android.util.ArraySet; import android.util.MergedConfiguration; @@ -850,11 +850,11 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override - public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow, - String hashAlgorithm) { + public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm, + RemoteCallback callback) { final long origId = Binder.clearCallingIdentity(); try { - return mService.generateScreenshotHash(this, window, boundsInWindow, hashAlgorithm); + mService.generateDisplayHash(this, window, boundsInWindow, hashAlgorithm, callback); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index d36091623207..80173b5942e6 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -35,7 +35,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.activityTypeToString; -import static android.app.WindowConfiguration.isSplitScreenWindowingMode; import static android.app.WindowConfiguration.windowingModeToString; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -148,7 +147,6 @@ import static com.android.server.wm.WindowContainerChildProto.TASK; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static com.android.server.wm.WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO; import static com.android.server.wm.WindowManagerService.dipToPixel; import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM; @@ -597,10 +595,6 @@ class Task extends WindowContainer<WindowContainer> { @Nullable private ActivityRecord mResumedActivity = null; - /** Last activity that is used to compute the Task bounds. */ - @Nullable - private ActivityRecord mLastTaskBoundsComputeActivity; - private boolean mForceShowForAllUsers; /** When set, will force the task to report as invisible. */ @@ -841,6 +835,9 @@ class Task extends WindowContainer<WindowContainer> { // Tracking cookie for the creation of this task. IBinder mLaunchCookie; + // The task will be removed when TaskOrganizer, which is managing the task, is destroyed. + boolean mRemoveWithTaskOrganizer; + private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent, Intent _affinityIntent, String _affinity, String _rootAffinity, ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset, @@ -852,7 +849,8 @@ class Task extends WindowContainer<WindowContainer> { boolean supportsPictureInPicture, boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight, ActivityInfo info, IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, - boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear) { + boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear, + boolean _removeWithTaskOrganizer) { super(atmService.mWindowManager); mAtmService = atmService; @@ -911,6 +909,7 @@ class Task extends WindowContainer<WindowContainer> { mCreatedByOrganizer = _createdByOrganizer; mLaunchCookie = _launchCookie; mDeferTaskAppear = _deferTaskAppear; + mRemoveWithTaskOrganizer = _removeWithTaskOrganizer; EventLogTags.writeWmTaskCreated(mTaskId, isRootTask() ? INVALID_TASK_ID : getRootTaskId()); } @@ -1261,10 +1260,13 @@ class Task extends WindowContainer<WindowContainer> { * @param info The activity info which could be different from {@code r.info} if set. */ void setIntent(ActivityRecord r, @Nullable Intent intent, @Nullable ActivityInfo info) { - mCallingUid = r.launchedFromUid; - mCallingPackage = r.launchedFromPackage; - mCallingFeatureId = r.launchedFromFeatureId; - setIntent(intent != null ? intent : r.intent, info != null ? info : r.info); + if (this.intent == null || !mNeverRelinquishIdentity) { + mCallingUid = r.launchedFromUid; + mCallingPackage = r.launchedFromPackage; + mCallingFeatureId = r.launchedFromFeatureId; + setIntent(intent != null ? intent : r.intent, info != null ? info : r.info); + return; + } setLockTaskAuth(r); } @@ -1272,13 +1274,7 @@ class Task extends WindowContainer<WindowContainer> { private void setIntent(Intent _intent, ActivityInfo info) { if (!isLeafTask()) return; - if (intent == null) { - mNeverRelinquishIdentity = - (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; - } else if (mNeverRelinquishIdentity) { - return; - } - + mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; affinity = info.taskAffinity; if (intent == null) { // If this task already has an intent associated with it, don't set the root @@ -1496,11 +1492,6 @@ class Task extends WindowContainer<WindowContainer> { } void cleanUpActivityReferences(ActivityRecord r) { - // mLastTaskBoundsComputeActivity is set at leaf Task - if (mLastTaskBoundsComputeActivity == r) { - mLastTaskBoundsComputeActivity = null; - } - // mPausingActivity is set at leaf task if (mPausingActivity != null && mPausingActivity == r) { mPausingActivity = null; @@ -2267,7 +2258,7 @@ class Task extends WindowContainer<WindowContainer> { } if (pipChanging) { - mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(true); + mDisplayContent.getPinnedTaskController().setPipWindowingModeChanging(true); // If the top activity is using fixed rotation, it should be changing from PiP to // fullscreen with display orientation change. Do not notify fullscreen task organizer // because the restoration of task surface and the transformation of activity surface @@ -2297,7 +2288,7 @@ class Task extends WindowContainer<WindowContainer> { } } finally { if (pipChanging) { - mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(false); + mDisplayContent.getPinnedTaskController().setPipWindowingModeChanging(false); } } @@ -2361,9 +2352,7 @@ class Task extends WindowContainer<WindowContainer> { final int newRotation = getWindowConfiguration().getRotation(); final boolean rotationChanged = prevRotation != newRotation; if (rotationChanged) { - mDisplayContent.rotateBounds( - newParentConfig.windowConfiguration.getBounds(), prevRotation, newRotation, - newBounds); + mDisplayContent.rotateBounds(prevRotation, newRotation, newBounds); setBounds(newBounds); } } @@ -2849,8 +2838,6 @@ class Task extends WindowContainer<WindowContainer> { windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode; if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode) && candidateWindowingMode != WINDOWING_MODE_PINNED - && (candidateWindowingMode != WINDOWING_MODE_FREEFORM - || !mTaskSupervisor.mService.mSizeCompatFreeform) && !mTaskSupervisor.mService.mSupportsNonResizableMultiWindow) { getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode( WINDOWING_MODE_FULLSCREEN); @@ -2865,7 +2852,6 @@ class Task extends WindowContainer<WindowContainer> { private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig, Rect previousBounds) { - mLastTaskBoundsComputeActivity = getTopNonFinishingActivity(false /* includeOverlays */); int windowingMode = getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode(); @@ -2879,7 +2865,8 @@ class Task extends WindowContainer<WindowContainer> { getResolvedOverrideConfiguration().windowConfiguration.getBounds(); if (windowingMode == WINDOWING_MODE_FULLSCREEN) { - computeFullscreenBounds(outOverrideBounds, newParentConfig); + // Use empty bounds to indicate "fill parent". + outOverrideBounds.setEmpty(); // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if // the parent or display is smaller than the size, the content may be cropped. return; @@ -2890,21 +2877,6 @@ class Task extends WindowContainer<WindowContainer> { computeFreeformBounds(outOverrideBounds, newParentConfig); return; } - - if (isSplitScreenWindowingMode(windowingMode) - || windowingMode == WINDOWING_MODE_MULTI_WINDOW) { - // This is to compute whether the task should be letterboxed to handle non-resizable app - // in multi window. There is no split screen only logic. - computeLetterboxBounds(outOverrideBounds, newParentConfig); - } - } - - /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}. */ - @VisibleForTesting - void computeFullscreenBounds(@NonNull Rect outBounds, @NonNull Configuration newParentConfig) { - // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent". - outBounds.setEmpty(); - computeLetterboxBounds(outBounds, newParentConfig); } /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */ @@ -2936,94 +2908,6 @@ class Task extends WindowContainer<WindowContainer> { } } - /** - * Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation - * change and the requested orientation is different from the parent. - */ - private void computeLetterboxBounds(@NonNull Rect outBounds, - @NonNull Configuration newParentConfig) { - if (handlesOrientationChangeFromDescendant()) { - // No need to letterbox at task level. Display will handle fixed-orientation requests. - return; - } - - final int parentOrientation = newParentConfig.orientation; - // Use the top activity as the reference of orientation. Don't include overlays because - // it is usually not the actual content or just temporarily shown. - // E.g. ForcedResizableInfoActivity. - final ActivityRecord refActivity = getTopNonFinishingActivity(false /* includeOverlays */); - - // If the task or the reference activity requires a different orientation (either by - // override or activityInfo), make it fit the available bounds by scaling down its bounds. - final int overrideOrientation = getRequestedOverrideConfiguration().orientation; - final int forcedOrientation = - (overrideOrientation != ORIENTATION_UNDEFINED || refActivity == null) - ? overrideOrientation : refActivity.getRequestedConfigurationOrientation(); - if (forcedOrientation == ORIENTATION_UNDEFINED || forcedOrientation == parentOrientation) { - return; - } - - final ActivityRecord.CompatDisplayInsets compatDisplayInsets = - refActivity == null ? null : refActivity.getCompatDisplayInsets(); - if (compatDisplayInsets != null && !compatDisplayInsets.mIsTaskLetterboxed) { - // App prefers to keep its original size. - // If the size compat is from previous task letterboxing, we may want to have task - // letterbox again, otherwise it will show the size compat restart button even if the - // restart bounds will be the same. - return; - } - - final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); - final int parentWidth = parentBounds.width(); - final int parentHeight = parentBounds.height(); - float aspect = Math.max(parentWidth, parentHeight) - / (float) Math.min(parentWidth, parentHeight); - - // Adjust the Task letterbox bounds to fit the app request aspect ratio in order to use the - // extra available space. - if (refActivity != null) { - final float maxAspectRatio = refActivity.info.maxAspectRatio; - final float minAspectRatio = refActivity.info.minAspectRatio; - if (aspect > maxAspectRatio && maxAspectRatio != 0) { - aspect = maxAspectRatio; - } else if (aspect < minAspectRatio) { - aspect = minAspectRatio; - } - } - - // Override from config_letterboxAspectRatio or via ADB with set-letterbox-aspect-ratio. - final float letterboxAspectRatioOverride = mWmService.getTaskLetterboxAspectRatio(); - // Activity min/max aspect ratio restrictions will be respected by the activity-level - // letterboxing (size-compat mode). Therefore this override can control the maximum screen - // area that can be occupied by the app in the letterbox mode. - aspect = letterboxAspectRatioOverride > MIN_TASK_LETTERBOX_ASPECT_RATIO - ? letterboxAspectRatioOverride : aspect; - - // Store the current bounds to be able to revert to size compat mode values below if needed. - mTmpFullBounds.set(outBounds); - if (forcedOrientation == ORIENTATION_LANDSCAPE) { - final int height = (int) Math.rint(parentWidth / aspect); - final int top = parentBounds.centerY() - height / 2; - outBounds.set(parentBounds.left, top, parentBounds.right, top + height); - } else { - final int width = (int) Math.rint(parentHeight / aspect); - final int left = parentBounds.centerX() - width / 2; - outBounds.set(left, parentBounds.top, left + width, parentBounds.bottom); - } - - if (compatDisplayInsets != null) { - compatDisplayInsets.getBoundsByRotation( - mTmpBounds, newParentConfig.windowConfiguration.getRotation()); - if (outBounds.width() != mTmpBounds.width() - || outBounds.height() != mTmpBounds.height()) { - // The app shouldn't be resized, we only do task letterboxing if the compat bounds - // is also from the same task letterbox. Otherwise, clear the task bounds to show - // app in size compat mode. - outBounds.set(mTmpFullBounds); - } - } - } - Rect updateOverrideConfigurationFromLaunchBounds() { // If the task is controlled by another organized task, do not set override // configurations and let its parent (organized task) to control it; @@ -3038,11 +2922,6 @@ class Task extends WindowContainer<WindowContainer> { return bounds; } - @Nullable - ActivityRecord getLastTaskBoundsComputeActivity() { - return mLastTaskBoundsComputeActivity; - } - /** Updates the task's bounds and override configuration to match what is expected for the * input root task. */ void updateOverrideConfigurationForRootTask(Task inRootTask) { @@ -3941,12 +3820,6 @@ class Task extends WindowContainer<WindowContainer> { || activityType == ACTIVITY_TYPE_ASSISTANT; } - boolean isTaskLetterboxed() { - // No letterbox for multi window root task - return !matchParentBounds() - && (getWindowingMode() == WINDOWING_MODE_FULLSCREEN || !isRootTask()); - } - @Override boolean fillsParent() { // From the perspective of policy, we still want to report that this task fills parent @@ -5373,11 +5246,11 @@ class Task extends WindowContainer<WindowContainer> { } final boolean wasHidden = isForceHidden(); mForceHiddenFlags = newFlags; - if (wasHidden && isFocusableAndVisible()) { + if (wasHidden != isForceHidden() && isTopActivityFocusable()) { // The change in force-hidden state will change visibility without triggering a root // task order change, so we should reset the preferred top focusable root task to ensure // it's not used if a new activity is started from this task. - getDisplayArea().resetPreferredTopFocusableRootTaskIfBelow(this); + getDisplayArea().resetPreferredTopFocusableRootTaskIfNeeded(this); } return true; } @@ -7792,18 +7665,18 @@ class Task extends WindowContainer<WindowContainer> { return; } - final PinnedStackController pinnedStackController = - getDisplayContent().getPinnedStackController(); + final PinnedTaskController pinnedTaskController = + getDisplayContent().getPinnedTaskController(); - if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) { + if (Float.compare(aspectRatio, pinnedTaskController.getAspectRatio()) == 0) { return; } // Notify the pinned stack controller about aspect ratio change. // This would result a callback delivered from SystemUI to WM to start animation, // if the bounds are ought to be altered due to aspect ratio change. - pinnedStackController.setAspectRatio( - pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio) + pinnedTaskController.setAspectRatio( + pinnedTaskController.isValidPictureInPictureAspectRatio(aspectRatio) ? aspectRatio : -1f); } @@ -7819,7 +7692,7 @@ class Task extends WindowContainer<WindowContainer> { return; } - getDisplayContent().getPinnedStackController().setActions(actions); + getDisplayContent().getPinnedTaskController().setActions(actions); } /** Returns true if a removal action is still being deferred. */ @@ -7972,6 +7845,7 @@ class Task extends WindowContainer<WindowContainer> { private IBinder mLaunchCookie; private boolean mOnTop; private boolean mHasBeenVisible; + private boolean mRemoveWithTaskOrganizer; Builder(ActivityTaskManagerService atm) { mAtmService = atm; @@ -8267,6 +8141,9 @@ class Task extends WindowContainer<WindowContainer> { mCallingPackage = mActivityInfo.packageName; mResizeMode = mActivityInfo.resizeMode; mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture(); + if (mActivityOptions != null) { + mRemoveWithTaskOrganizer = mActivityOptions.getRemoveWithTaskOranizer(); + } final Task task = buildInner(); task.mHasBeenVisible = mHasBeenVisible; @@ -8305,7 +8182,7 @@ class Task extends WindowContainer<WindowContainer> { mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture, mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight, mActivityInfo, mVoiceSession, mVoiceInteractor, mCreatedByOrganizer, - mLaunchCookie, mDeferTaskAppear); + mLaunchCookie, mDeferTaskAppear, mRemoveWithTaskOrganizer); } } } diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 622fe05b6170..04ec4bd83511 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -59,6 +59,7 @@ class TaskChangeNotificationController { private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 25; private static final int NOTIFY_ACTIVITY_ROTATED_MSG = 26; private static final int NOTIFY_TASK_MOVED_TO_BACK_LISTENERS_MSG = 27; + private static final int NOTIFY_LOCK_TASK_MODE_CHANGED_MSG = 28; // Delay in notifying task stack change listeners (in millis) private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; @@ -129,8 +130,8 @@ class TaskChangeNotificationController { l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2); }; - private final TaskStackConsumer mNotifyActivityDismissingDockedStack = (l, m) -> { - l.onActivityDismissingDockedStack(); + private final TaskStackConsumer mNotifyActivityDismissingDockedTask = (l, m) -> { + l.onActivityDismissingDockedTask(); }; private final TaskStackConsumer mNotifyActivityLaunchOnSecondaryDisplayFailed = (l, m) -> { @@ -177,6 +178,10 @@ class TaskChangeNotificationController { l.onTaskMovedToBack((RunningTaskInfo) m.obj); }; + private final TaskStackConsumer mNotifyLockTaskModeChanged = (l, m) -> { + l.onLockTaskModeChanged(m.arg1); + }; + @FunctionalInterface public interface TaskStackConsumer { void accept(ITaskStackListener t, Message m) throws RemoteException; @@ -230,7 +235,7 @@ class TaskChangeNotificationController { forAllRemoteListeners(mNotifyActivityForcedResizable, msg); break; case NOTIFY_ACTIVITY_DISMISSING_DOCKED_ROOT_TASK_MSG: - forAllRemoteListeners(mNotifyActivityDismissingDockedStack, msg); + forAllRemoteListeners(mNotifyActivityDismissingDockedTask, msg); break; case NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG: forAllRemoteListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg); @@ -268,6 +273,9 @@ class TaskChangeNotificationController { case NOTIFY_TASK_MOVED_TO_BACK_LISTENERS_MSG: forAllRemoteListeners(mNotifyTaskMovedToBack, msg); break; + case NOTIFY_LOCK_TASK_MODE_CHANGED_MSG: + forAllRemoteListeners(mNotifyLockTaskModeChanged, msg); + break; } if (msg.obj instanceof SomeArgs) { ((SomeArgs) msg.obj).recycle(); @@ -383,7 +391,7 @@ class TaskChangeNotificationController { void notifyActivityDismissingDockedRootTask() { mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_ROOT_TASK_MSG); final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_ROOT_TASK_MSG); - forAllLocalListeners(mNotifyActivityDismissingDockedStack, msg); + forAllLocalListeners(mNotifyActivityDismissingDockedTask, msg); msg.sendToTarget(); } @@ -550,4 +558,11 @@ class TaskChangeNotificationController { forAllLocalListeners(mNotifyTaskMovedToBack, msg); msg.sendToTarget(); } + + void notifyLockTaskModeChanged(int lockTaskModeState) { + final Message msg = mHandler.obtainMessage(NOTIFY_LOCK_TASK_MODE_CHANGED_MSG, + lockTaskModeState, 0 /* unused */); + forAllLocalListeners(mNotifyLockTaskModeChanged, msg); + msg.sendToTarget(); + } } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 40248c43fe5d..badd7fda2897 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -974,9 +974,10 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { onRootTaskOrderChanged(rootTask); } - void resetPreferredTopFocusableRootTaskIfBelow(Task task) { + /** Reset the mPreferredTopFocusableRootTask if it is or below the given task. */ + void resetPreferredTopFocusableRootTaskIfNeeded(Task task) { if (mPreferredTopFocusableRootTask != null - && mPreferredTopFocusableRootTask.compareTo(task) < 0) { + && mPreferredTopFocusableRootTask.compareTo(task) <= 0) { mPreferredTopFocusableRootTask = null; } } diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 106db0b15c46..f3b69e30b40a 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -234,19 +234,28 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } } - // STEP 2.3: Adjust launch parameters as needed for freeform display. We enforce the policy - // that legacy (pre-D) apps and those apps that can't handle multiple screen density well - // are forced to be maximized. The rest of this step is to define the default policy when - // there is no initial bounds or a fully resolved current params from callers. + // STEP 2.3: Adjust launch parameters as needed for freeform display. We enforce the + // policies related to unresizable apps here. If an app is unresizable and the freeform + // size-compat mode is enabled, it can be launched in freeform depending on other properties + // such as orientation. Otherwise, the app is forcefully launched in maximized. The rest of + // this step is to define the default policy when there is no initial bounds or a fully + // resolved current params from callers. if (display.inFreeformWindowingMode()) { if (launchMode == WINDOWING_MODE_PINNED) { if (DEBUG) appendLog("picture-in-picture"); - } else if (!mSupervisor.mService.mSizeCompatFreeform && !root.isResizeable()) { - // We're launching an activity in size-compat mode and they aren't allowed in - // freeform, so force it to be maximized. - launchMode = WINDOWING_MODE_FULLSCREEN; - outParams.mBounds.setEmpty(); - if (DEBUG) appendLog("forced-maximize"); + } else if (!root.isResizeable()) { + if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea)) { + launchMode = WINDOWING_MODE_FREEFORM; + if (outParams.mBounds.isEmpty()) { + getTaskBounds(root, display, layout, launchMode, hasInitialBounds, + outParams.mBounds); + } + if (DEBUG) appendLog("unresizable-freeform"); + } else { + launchMode = WINDOWING_MODE_FULLSCREEN; + outParams.mBounds.setEmpty(); + if (DEBUG) appendLog("unresizable-forced-maximize"); + } } } else { if (DEBUG) appendLog("non-freeform-display"); @@ -322,7 +331,6 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { } getTaskBounds(root, display, layout, resolvedMode, hasInitialBounds, outParams.mBounds); } - return RESULT_CONTINUE; } @@ -562,6 +570,27 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { outBounds.offset(xOffset, yOffset); } + private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity, + TaskDisplayArea displayArea) { + if (!mSupervisor.mService.mSupportsNonResizableMultiWindow || activity.isResizeable()) { + return false; + } + final DisplayContent display = displayArea.getDisplayContent(); + if (display == null) { + return false; + } + + final int displayOrientation = orientationFromBounds(displayArea.getBounds()); + final int activityOrientation = resolveOrientation(activity, display, + displayArea.getBounds()); + if (displayArea.getWindowingMode() == WINDOWING_MODE_FREEFORM + && displayOrientation != activityOrientation) { + return true; + } + + return false; + } + /** * Resolves activity requested orientation to 4 categories: * 1) {@link ActivityInfo#SCREEN_ORIENTATION_LOCKED} indicating app wants to lock down diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index c0bce6be8c54..fc6db61bdbcd 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -285,18 +285,20 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return false; } - private boolean removeTask(Task t) { + private boolean removeTask(Task t, boolean removeFromSystem) { mOrganizedTasks.remove(t); mInterceptBackPressedOnRootTasks.remove(t.mTaskId); - - if (t.mTaskAppearedSent) { + boolean taskAppearedSent = t.mTaskAppearedSent; + if (taskAppearedSent) { if (t.getSurfaceControl() != null) { t.migrateToNewSurfaceControl(); } t.mTaskAppearedSent = false; - return true; } - return false; + if (removeFromSystem) { + mService.removeTask(t.mTaskId); + } + return taskAppearedSent; } void dispose() { @@ -311,7 +313,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (mOrganizedTasks.contains(t)) { // updateTaskOrganizerState should remove the task from the list, but still // check it again to avoid while-loop isn't terminate. - if (removeTask(t)) { + if (removeTask(t, t.mRemoveWithTaskOrganizer)) { TaskOrganizerController.this.onTaskVanishedInternal( mOrganizer.mTaskOrganizer, t); } @@ -527,7 +529,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { void onTaskVanished(ITaskOrganizer organizer, Task task) { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); - if (state != null && state.removeTask(task)) { + if (state != null && state.removeTask(task, false /* removeFromSystem */)) { onTaskVanishedInternal(organizer, task); } } @@ -596,9 +598,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { try { synchronized (mGlobalLock) { final WindowContainer wc = WindowContainer.fromBinder(token.asBinder()); - if (wc == null) { - throw new IllegalArgumentException("Can't resolve window from token"); - } + if (wc == null) return false; final Task task = wc.asTask(); if (task == null) return false; if (!task.mCreatedByOrganizer) { diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java index 361694b325f9..9245f8c3efe5 100644 --- a/services/core/java/com/android/server/wm/WindowFrames.java +++ b/services/core/java/com/android/server/wm/WindowFrames.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static com.android.server.wm.WindowFramesProto.COMPAT_FRAME; import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME; import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME; import static com.android.server.wm.WindowFramesProto.FRAME; @@ -177,7 +178,7 @@ public class WindowFrames { mDisplayFrame.dumpDebug(proto, DISPLAY_FRAME); mContainingFrame.dumpDebug(proto, CONTAINING_FRAME); mFrame.dumpDebug(proto, FRAME); - + mCompatFrame.dumpDebug(proto, COMPAT_FRAME); proto.end(token); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5dc5ab767b2e..518176b2ef75 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -39,7 +39,6 @@ import static android.os.Process.myPid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW; -import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR; @@ -83,6 +82,10 @@ import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; +import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW; +import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN; +import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN; +import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT; @@ -187,6 +190,7 @@ import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; +import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -199,7 +203,6 @@ import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; -import android.service.screenshot.ScreenshotHash; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.sysprop.SurfaceFlingerProperties; @@ -226,7 +229,7 @@ import android.view.IDisplayWindowListener; import android.view.IDisplayWindowRotationController; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; -import android.view.IPinnedStackListener; +import android.view.IPinnedTaskListener; import android.view.IRecentsAnimationRunner; import android.view.IRotationWatcher; import android.view.IScrollCaptureCallbacks; @@ -249,6 +252,7 @@ import android.view.MagnificationSpec; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.RemoteAnimationAdapter; +import android.view.ScrollCaptureResponse; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -261,6 +265,8 @@ import android.view.WindowManager.LayoutParams; import android.view.WindowManager.RemoveContentMode; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicyConstants.PointerEventListener; +import android.view.displayhash.DisplayHash; +import android.view.displayhash.VerifiedDisplayHash; import android.window.ClientWindowFrames; import android.window.TaskSnapshot; @@ -768,7 +774,8 @@ public class WindowManagerService extends IWindowManager.Stub final EmbeddedWindowController mEmbeddedWindowController; final AnrController mAnrController; - private final ScreenshotHashController mScreenshotHashController; + private final DisplayHashController mDisplayHashController; + @VisibleForTesting final WindowContextListenerController mWindowContextListenerController = new WindowContextListenerController(); @@ -795,8 +802,6 @@ public class WindowManagerService extends IWindowManager.Stub Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT); private final Uri mForceResizableUri = Settings.Global.getUriFor( DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES); - private final Uri mSizeCompatFreeformUri = Settings.Global.getUriFor( - DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM); private final Uri mSupportsNonResizableMultiWindowUri = Settings.Global.getUriFor( DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW); private final Uri mRenderShadowsInCompositorUri = Settings.Global.getUriFor( @@ -823,8 +828,6 @@ public class WindowManagerService extends IWindowManager.Stub UserHandle.USER_ALL); resolver.registerContentObserver(mFreeformWindowUri, false, this, UserHandle.USER_ALL); resolver.registerContentObserver(mForceResizableUri, false, this, UserHandle.USER_ALL); - resolver.registerContentObserver(mSizeCompatFreeformUri, false, this, - UserHandle.USER_ALL); resolver.registerContentObserver(mSupportsNonResizableMultiWindowUri, false, this, UserHandle.USER_ALL); resolver.registerContentObserver(mRenderShadowsInCompositorUri, false, this, @@ -864,11 +867,6 @@ public class WindowManagerService extends IWindowManager.Stub return; } - if (mSizeCompatFreeformUri.equals(uri)) { - updateSizeCompatFreeform(); - return; - } - if (mSupportsNonResizableMultiWindowUri.equals(uri)) { updateSupportsNonResizableMultiWindow(); return; @@ -966,14 +964,6 @@ public class WindowManagerService extends IWindowManager.Stub mAtmService.mForceResizableActivities = forceResizable; } - void updateSizeCompatFreeform() { - ContentResolver resolver = mContext.getContentResolver(); - final boolean sizeCompatFreeform = Settings.Global.getInt(resolver, - DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0; - - mAtmService.mSizeCompatFreeform = sizeCompatFreeform; - } - void updateSupportsNonResizableMultiWindow() { ContentResolver resolver = mContext.getContentResolver(); final boolean supportsNonResizableMultiWindow = Settings.Global.getInt(resolver, @@ -1419,7 +1409,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources( mContext.getResources()); - mScreenshotHashController = new ScreenshotHashController(mContext); + mDisplayHashController = new DisplayHashController(mContext); setGlobalShadowSettings(); mAnrController = new AnrController(this); mStartingSurfaceController = new StartingSurfaceController(this); @@ -1874,6 +1864,10 @@ public class WindowManagerService extends IWindowManager.Stub displayContent.sendNewConfiguration(); } + // This window doesn't have a frame yet. Don't let this window cause the insets change. + displayContent.getInsetsStateController().updateAboveInsetsState( + win, false /* notifyInsetsChanged */); + getInsetsSourceControls(win, outActiveControls); } @@ -2382,15 +2376,9 @@ public class WindowManagerService extends IWindowManager.Stub } } - // We may be deferring layout passes at the moment, but since the client is interested - // in the new out values right now we need to force a layout. - mWindowPlacerLocked.performSurfacePlacement(true /* force */); - + // Create surfaceControl before surface placement otherwise layout will be skipped + // (because WS.isGoneForLayout() is true when there is no surface. if (shouldRelayout) { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); - - result = win.relayoutVisibleWindow(result); - try { result = createSurfaceControl(outSurfaceControl, result, win, winAnimator); } catch (Exception e) { @@ -2402,6 +2390,17 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); return 0; } + } + + // We may be deferring layout passes at the moment, but since the client is interested + // in the new out values right now we need to force a layout. + mWindowPlacerLocked.performSurfacePlacement(true /* force */); + + if (shouldRelayout) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); + + result = win.relayoutVisibleWindow(result); + if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { focusMayChange = true; } @@ -2970,7 +2969,7 @@ public class WindowManagerService extends IWindowManager.Stub } boolean isValidPictureInPictureAspectRatio(DisplayContent displayContent, float aspectRatio) { - return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio( + return displayContent.getPinnedTaskController().isValidPictureInPictureAspectRatio( aspectRatio); } @@ -6869,7 +6868,7 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void setDockedStackDividerTouchRegion(Rect touchRegion) { + public void setDockedTaskDividerTouchRegion(Rect touchRegion) { synchronized (mGlobalLock) { final DisplayContent dc = getDefaultDisplayContentLocked(); dc.getDockedDividerController().setTouchRegion(touchRegion); @@ -6894,10 +6893,9 @@ public class WindowManagerService extends IWindowManager.Stub return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics); } - @Override - public void registerPinnedStackListener(int displayId, IPinnedStackListener listener) { + public void registerPinnedTaskListener(int displayId, IPinnedTaskListener listener) { if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS, - "registerPinnedStackListener()")) { + "registerPinnedTaskListener()")) { return; } if (!mAtmService.mSupportsPictureInPicture) { @@ -6905,7 +6903,7 @@ public class WindowManagerService extends IWindowManager.Stub } synchronized (mGlobalLock) { final DisplayContent displayContent = mRoot.getDisplayContent(displayId); - displayContent.getPinnedStackController().registerPinnedStackListener(listener); + displayContent.getPinnedTaskController().registerPinnedTaskListener(listener); } } @@ -7144,12 +7142,14 @@ public class WindowManagerService extends IWindowManager.Stub } final long token = Binder.clearCallingIdentity(); try { + ScrollCaptureResponse.Builder responseBuilder = new ScrollCaptureResponse.Builder(); synchronized (mGlobalLock) { DisplayContent dc = mRoot.getDisplayContent(displayId); if (dc == null) { ProtoLog.e(WM_ERROR, "Invalid displayId for requestScrollCapture: %d", displayId); - callbacks.onUnavailable(); + responseBuilder.setDescription(String.format("bad displayId: %d", displayId)); + callbacks.onScrollCaptureResponse(responseBuilder.build()); return; } WindowState topWindow = null; @@ -7158,17 +7158,20 @@ public class WindowManagerService extends IWindowManager.Stub } WindowState targetWindow = dc.findScrollCaptureTargetWindow(topWindow, taskId); if (targetWindow == null) { - callbacks.onUnavailable(); + responseBuilder.setDescription("findScrollCaptureTargetWindow returned null"); + callbacks.onScrollCaptureResponse(responseBuilder.build()); return; } - // Forward to the window for handling. try { + // Forward to the window for handling, which will respond using the callback. targetWindow.mClient.requestScrollCapture(callbacks); } catch (RemoteException e) { ProtoLog.w(WM_ERROR, "requestScrollCapture: caught exception dispatching to window." + "token=%s", targetWindow.mClient.asBinder()); - callbacks.onUnavailable(); + responseBuilder.setWindowTitle(targetWindow.getName()); + responseBuilder.setDescription(String.format("caught exception: %s", e)); + callbacks.onScrollCaptureResponse(responseBuilder.build()); } } } catch (RemoteException e) { @@ -8599,39 +8602,42 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public String[] getSupportedScreenshotHashingAlgorithms() { - return mScreenshotHashController.getSupportedHashingAlgorithms(); + public String[] getSupportedDisplayHashAlgorithms() { + return mDisplayHashController.getSupportedHashAlgorithms(); } @Override - public boolean verifyScreenshotHash(ScreenshotHash screenshotHash) { - return mScreenshotHashController.verifyScreenshotHash(screenshotHash); + public VerifiedDisplayHash verifyDisplayHash(DisplayHash displayHash) { + return mDisplayHashController.verifyDisplayHash(displayHash); } - ScreenshotHash generateScreenshotHash(Session session, IWindow window, - Rect boundsInWindow, String hashAlgorithm) { + void generateDisplayHash(Session session, IWindow window, Rect boundsInWindow, + String hashAlgorithm, RemoteCallback callback) { final SurfaceControl displaySurfaceControl; final Rect boundsInDisplay = new Rect(boundsInWindow); synchronized (mGlobalLock) { final WindowState win = windowForClientLocked(session, window, false); if (win == null) { - Slog.w(TAG, "Failed to generate ScreenshotHash. Invalid window"); - return null; + Slog.w(TAG, "Failed to generate DisplayHash. Invalid window"); + sendDisplayHashError(callback, DISPLAY_HASH_ERROR_MISSING_WINDOW); + return; } DisplayContent displayContent = win.getDisplayContent(); if (displayContent == null) { - Slog.w(TAG, "Failed to generate ScreenshotHash. Window is not on a display"); - return null; + Slog.w(TAG, "Failed to generate DisplayHash. Window is not on a display"); + sendDisplayHashError(callback, DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN); + return; } displaySurfaceControl = displayContent.getSurfaceControl(); - mScreenshotHashController.calculateScreenshotHashBoundsLocked(win, - boundsInWindow, boundsInDisplay); + mDisplayHashController.calculateDisplayHashBoundsLocked(win, boundsInWindow, + boundsInDisplay); if (boundsInDisplay.isEmpty()) { - Slog.w(TAG, "Failed to generate ScreenshotHash. Bounds are not on screen"); - return null; + Slog.w(TAG, "Failed to generate DisplayHash. Bounds are not on screen"); + sendDisplayHashError(callback, DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN); + return; } } @@ -8650,11 +8656,18 @@ public class WindowManagerService extends IWindowManager.Stub SurfaceControl.captureLayers(args); if (screenshotHardwareBuffer == null || screenshotHardwareBuffer.getHardwareBuffer() == null) { - Slog.w(TAG, "Failed to generate ScreenshotHash. Failed to take screenshot"); - return null; + Slog.w(TAG, "Failed to generate DisplayHash. Couldn't capture content"); + sendDisplayHashError(callback, DISPLAY_HASH_ERROR_UNKNOWN); + return; } - return mScreenshotHashController.generateScreenshotHash( - screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow, hashAlgorithm); + mDisplayHashController.generateDisplayHash(screenshotHardwareBuffer.getHardwareBuffer(), + boundsInWindow, hashAlgorithm, callback); + } + + private void sendDisplayHashError(RemoteCallback callback, int errorCode) { + Bundle bundle = new Bundle(); + bundle.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode); + callback.sendResult(bundle); } } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 63a083261614..9973664346f0 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -17,7 +17,6 @@ package com.android.server.wm; import static android.Manifest.permission.READ_FRAME_BUFFER; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; @@ -426,8 +425,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } if (windowingMode > -1) { - if (mService.isInLockTaskMode() && windowingMode != WINDOWING_MODE_FULLSCREEN) { - throw new UnsupportedOperationException("Not supported to set non-fullscreen" + if (mService.isInLockTaskMode() + && WindowConfiguration.inMultiWindowMode(windowingMode)) { + throw new UnsupportedOperationException("Not supported to set multi-window" + " windowing mode during locked task mode."); } container.setWindowingMode(windowingMode); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index a94b0aa9b72f..1fc7041c0fe2 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -169,7 +169,9 @@ import static com.android.server.wm.WindowStateProto.DISPLAY_ID; import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_FRAME; import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION; import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS; +import static com.android.server.wm.WindowStateProto.GLOBAL_SCALE; import static com.android.server.wm.WindowStateProto.HAS_SURFACE; +import static com.android.server.wm.WindowStateProto.IN_SIZE_COMPAT_MODE; import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN; import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY; import static com.android.server.wm.WindowStateProto.IS_VISIBLE; @@ -211,11 +213,11 @@ import android.os.Trace; import android.os.WorkSource; import android.provider.Settings; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.MergedConfiguration; import android.util.Slog; +import android.util.SparseArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.view.Display; @@ -533,6 +535,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ private boolean mOrientationChanging; + /** The time when the window was last requested to redraw for orientation change. */ + private long mOrientationChangeRedrawRequestTime; + /** * Sometimes in addition to the mOrientationChanging * flag we report that the orientation is changing @@ -648,12 +653,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** * The insets state of sources provided by windows above the current window. */ - InsetsState mAboveInsetsState = new InsetsState(); + final InsetsState mAboveInsetsState = new InsetsState(); /** * The insets sources provided by this window. */ - ArrayMap<Integer, InsetsSource> mProvidedInsetsSources = new ArrayMap<>(); + final SparseArray<InsetsSource> mProvidedInsetsSources = new SparseArray<>(); /** * Surface insets from the previous call to relayout(), used to track @@ -1219,7 +1224,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // But in docked we want to behave like fullscreen and behave as if the task // were given smaller bounds for the purposes of layout. Skip adjustments for // the root pinned task, they are handled separately in the - // PinnedStackController. + // PinnedTaskController. windowFrames.mContainingFrame.bottom = windowFrames.mParentFrame.bottom; } } @@ -1441,11 +1446,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // redrawn; to do that, we need to go through the process of getting informed by the // application when it has finished drawing. if (getOrientationChanging() || dragResizingChanged) { - if (getOrientationChanging()) { - Slog.v(TAG_WM, "Orientation start waiting for draw" - + ", mDrawState=DRAW_PENDING in " + this - + ", surfaceController " + winAnimator.mSurfaceController); - } if (dragResizingChanged) { ProtoLog.v(WM_DEBUG_RESIZE, "Resize start waiting for draw, " @@ -2204,7 +2204,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - @Override + @Override void removeImmediately() { super.removeImmediately(); @@ -3651,7 +3651,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP ProtoLog.v(WM_DEBUG_RESIZE, "Reporting new frame to %s: %s", this, mWindowFrames.mCompatFrame); - if (mWinAnimator.mDrawState == DRAW_PENDING) { + final boolean drawPending = mWinAnimator.mDrawState == DRAW_PENDING; + if (drawPending) { ProtoLog.i(WM_DEBUG_ORIENTATION, "Resizing %s WITH DRAW PENDING", this); } @@ -3668,7 +3669,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWindowFrames.clearReportResizeHints(); final MergedConfiguration mergedConfiguration = mLastReportedConfiguration; - final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING || useBLASTSync() || !mRedrawForSyncReported; + final boolean reportDraw = drawPending || useBLASTSync() || !mRedrawForSyncReported; final boolean forceRelayout = reportOrientation || isDragResizeChanged() || !mRedrawForSyncReported; final int displayId = getDisplayId(); fillClientWindowFrames(mClientWindowFrames); @@ -3679,6 +3680,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mClient.resized(mClientWindowFrames, reportDraw, mergedConfiguration, forceRelayout, getDisplayContent().getDisplayPolicy().areSystemBarsForcedShownLw(this), displayId); + if (drawPending && reportOrientation && mOrientationChanging) { + mOrientationChangeRedrawRequestTime = SystemClock.elapsedRealtime(); + ProtoLog.v(WM_DEBUG_ORIENTATION, + "Requested redraw for orientation change: %s", this); + } if (mWmService.mAccessibilityController != null) { mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(displayId); @@ -3821,13 +3827,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** @return true when the window should be letterboxed. */ boolean isLetterboxedAppWindow() { // Fullscreen mode but doesn't fill display area. - return (!inMultiWindowMode() && !matchesDisplayAreaBounds()) - // Activity in size compat. - || (mActivityRecord != null && mActivityRecord.inSizeCompatMode()) - // Task letterboxed. - || (getTask() != null && getTask().isTaskLetterboxed()) - // Letterboxed for display cutout. - || isLetterboxedForDisplayCutout(); + if (!inMultiWindowMode() && !matchesDisplayAreaBounds()) { + return true; + } + if (mActivityRecord != null) { + // Activity in size compat. + if (mActivityRecord.inSizeCompatMode()) { + return true; + } + // Letterbox for fixed orientation. + if (mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio()) { + return true; + } + } + // Letterboxed for display cutout. + return isLetterboxedForDisplayCutout(); } /** Returns {@code true} if the window is letterboxed for the display cutout. */ @@ -3998,6 +4012,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null); proto.write(FINISHED_SEAMLESS_ROTATION_FRAME, mFinishSeamlessRotateFrameNumber); proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate); + proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode()); + proto.write(GLOBAL_SCALE, mGlobalScale); proto.end(token); } @@ -5732,6 +5748,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) { + if (mOrientationChangeRedrawRequestTime > 0) { + final long duration = + SystemClock.elapsedRealtime() - mOrientationChangeRedrawRequestTime; + Slog.i(TAG, "finishDrawing of orientation change: " + this + " " + duration + "ms"); + mOrientationChangeRedrawRequestTime = 0; + } else if (mActivityRecord != null && mActivityRecord.mRelaunchStartTime != 0 + && mActivityRecord.findMainWindow() == this) { + final long duration = + SystemClock.elapsedRealtime() - mActivityRecord.mRelaunchStartTime; + Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms"); + mActivityRecord.mRelaunchStartTime = 0; + } if (!onSyncFinishedDrawing()) { return mWinAnimator.finishDrawingLocked(postDrawTransaction); } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 1c3fe029a56b..e87ee918e0f0 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -549,7 +549,7 @@ class WindowToken extends WindowContainer<WindowState> { } /** Notifies application side to enable or disable the rotation adjustment of display info. */ - private void notifyFixedRotationTransform(boolean enabled) { + void notifyFixedRotationTransform(boolean enabled) { FixedRotationAdjustments adjustments = null; // A token may contain windows of the same processes or different processes. The list is // used to avoid sending the same adjustments to a process multiple times. diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 11e3ecfbb90d..29bce792fe30 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_library_static { name: "libservices.core", defaults: ["libservices.core-libs"], @@ -88,6 +97,7 @@ cc_defaults { "libbase", "libappfuse", "libbinder", + "libbinder_ndk", "libcutils", "libcrypto", "liblog", @@ -163,6 +173,7 @@ cc_defaults { "android.frameworks.schedulerservice@1.0", "android.frameworks.sensorservice@1.0", "android.frameworks.stats@1.0", + "android.frameworks.stats-V1-ndk_platform", "android.system.suspend.control-V1-cpp", "android.system.suspend.control.internal-cpp", "android.system.suspend@1.0", diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp index 729fa71af169..a73f6c6d8c2d 100644 --- a/services/core/jni/com_android_server_SystemServer.cpp +++ b/services/core/jni/com_android_server_SystemServer.cpp @@ -23,6 +23,7 @@ #include <jni.h> #include <nativehelper/JNIHelp.h> +#include <android/binder_manager.h> #include <android/hidl/manager/1.2/IServiceManager.h> #include <binder/IServiceManager.h> #include <hidl/HidlTransportSupport.h> @@ -31,6 +32,7 @@ #include <schedulerservice/SchedulingPolicyService.h> #include <sensorservice/SensorService.h> #include <sensorservicehidl/SensorManager.h> +#include <stats/StatsAidl.h> #include <stats/StatsHal.h> #include <bionic/malloc.h> @@ -45,6 +47,31 @@ using android::base::GetIntProperty; using namespace std::chrono_literals; +namespace { + +static void startStatsAidlService() { + using aidl::android::frameworks::stats::IStats; + using aidl::android::frameworks::stats::StatsHal; + + std::shared_ptr<StatsHal> statsService = ndk::SharedRefBase::make<StatsHal>(); + + const std::string instance = std::string() + IStats::descriptor + "/default"; + const binder_exception_t err = + AServiceManager_addService(statsService->asBinder().get(), instance.c_str()); + LOG_ALWAYS_FATAL_IF(err != EX_NONE, "Cannot register %s: %d", instance.c_str(), err); +} + +static void startStatsHidlService() { + using android::frameworks::stats::V1_0::IStats; + using android::frameworks::stats::V1_0::implementation::StatsHal; + + android::sp<IStats> statsHal = new StatsHal(); + const android::status_t err = statsHal->registerAsService(); + LOG_ALWAYS_FATAL_IF(err != android::OK, "Cannot register %s: %d", IStats::descriptor, err); +} + +} // namespace + namespace android { static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) { @@ -54,7 +81,6 @@ static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jo SensorService::publish(false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL); } - } static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject /* clazz */) { @@ -62,8 +88,6 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject / using ::android::frameworks::schedulerservice::V1_0::implementation::SchedulingPolicyService; using ::android::frameworks::sensorservice::V1_0::ISensorManager; using ::android::frameworks::sensorservice::V1_0::implementation::SensorManager; - using ::android::frameworks::stats::V1_0::IStats; - using ::android::frameworks::stats::V1_0::implementation::StatsHal; using ::android::hardware::configureRpcThreadpool; using ::android::hidl::manager::V1_0::IServiceManager; @@ -89,9 +113,8 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject / ALOGW("%s is deprecated. Skipping registration.", ISchedulingPolicyService::descriptor); } - sp<IStats> statsHal = new StatsHal(); - err = statsHal->registerAsService(); - LOG_ALWAYS_FATAL_IF(err != OK, "Cannot register %s: %d", IStats::descriptor, err); + startStatsAidlService(); + startStatsHidlService(); } static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */, diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 4551d49d9e58..8c6d084fba99 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -86,7 +86,7 @@ static int compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) int pidfd = syscall(__NR_pidfd_open, pid, 0); err = -errno; - if (err < 0) { + if (pidfd < 0) { // Skip compaction if failed to open pidfd with any error return err; } @@ -232,21 +232,6 @@ static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, job compactProcessOrFallback(pid, compactionFlags); } -static void com_android_server_am_CachedAppOptimizer_enableFreezerInternal( - JNIEnv *env, jobject clazz, jboolean enable) { - bool success = true; - - if (enable) { - success = SetTaskProfiles(0, {"FreezerEnabled"}, true); - } else { - success = SetTaskProfiles(0, {"FreezerDisabled"}, true); - } - - if (!success) { - jniThrowException(env, "java/lang/RuntimeException", "Unknown error"); - } -} - static void com_android_server_am_CachedAppOptimizer_freezeBinder( JNIEnv *env, jobject clazz, jint pid, jboolean freeze) { @@ -280,15 +265,19 @@ static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIEnv* env, jobject clazz) { - return env->NewStringUTF(CGROUP_FREEZE_PATH); + std::string path; + + if (!getAttributePathForTask("FreezerState", getpid(), &path)) { + path = ""; + } + + return env->NewStringUTF(path.c_str()); } static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem}, {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess}, - {"enableFreezerInternal", "(Z)V", - (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal}, {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder}, {"getBinderFreezeInfo", "(I)I", (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}, diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 643503d18bed..10705af9ac38 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -158,6 +158,20 @@ static struct { static struct { jclass clazz; jmethodID constructor; + jfieldID lightTypeSingle; + jfieldID lightTypePlayerId; + jfieldID lightTypeRgb; +} gLightClassInfo; + +static struct { + jclass clazz; + jmethodID constructor; + jmethodID add; +} gArrayListClassInfo; + +static struct { + jclass clazz; + jmethodID constructor; jmethodID keyAt; jmethodID valueAt; jmethodID size; @@ -317,7 +331,7 @@ public: uint32_t policyFlags) override; bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override; - void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override; + void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override; bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override; void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override; void setPointerCapture(bool enabled) override; @@ -1311,9 +1325,9 @@ bool NativeInputManager::dispatchUnhandledKey(const sp<IBinder>& token, return result; } -void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) { +void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) { ATRACE_CALL(); - android_server_PowerManagerService_userActivity(eventTime, eventType); + android_server_PowerManagerService_userActivity(eventTime, eventType, displayId); } bool NativeInputManager::checkInjectEventsPermissionNonReentrant( @@ -1923,6 +1937,79 @@ static jintArray nativeGetVibratorIds(JNIEnv* env, jclass clazz, jlong ptr, jint return vibIdArray; } +static jobject nativeGetLights(JNIEnv* env, jclass clazz, jlong ptr, jint deviceId) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + jobject jLights = env->NewObject(gArrayListClassInfo.clazz, gArrayListClassInfo.constructor); + + std::vector<int> lightIds = im->getInputManager()->getReader()->getLightIds(deviceId); + + for (size_t i = 0; i < lightIds.size(); i++) { + const InputDeviceLightInfo* lightInfo = + im->getInputManager()->getReader()->getLightInfo(deviceId, lightIds[i]); + if (lightInfo == nullptr) { + ALOGW("Failed to get input device %d light info for id %d", deviceId, lightIds[i]); + continue; + } + + jint jTypeId = 0; + if (lightInfo->type == InputDeviceLightType::SINGLE) { + jTypeId = + env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeSingle); + } else if (lightInfo->type == InputDeviceLightType::PLAYER_ID) { + jTypeId = env->GetStaticIntField(gLightClassInfo.clazz, + gLightClassInfo.lightTypePlayerId); + } else if (lightInfo->type == InputDeviceLightType::RGB || + lightInfo->type == InputDeviceLightType::MULTI_COLOR) { + jTypeId = env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeRgb); + } else { + ALOGW("Unknown light type %d", lightInfo->type); + continue; + } + ScopedLocalRef<jobject> + lightObj(env, + env->NewObject(gLightClassInfo.clazz, gLightClassInfo.constructor, + (jint)lightInfo->id, (jint)lightInfo->ordinal, jTypeId, + env->NewStringUTF(lightInfo->name.c_str()))); + // Add light object to list + env->CallBooleanMethod(jLights, gArrayListClassInfo.add, lightObj.get()); + } + + return jLights; +} + +static jint nativeGetLightPlayerId(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, + jint lightId) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + std::optional<int32_t> ret = + im->getInputManager()->getReader()->getLightPlayerId(deviceId, lightId); + + return static_cast<jint>(ret.value_or(0)); +} + +static jint nativeGetLightColor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, + jint lightId) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + std::optional<int32_t> ret = + im->getInputManager()->getReader()->getLightColor(deviceId, lightId); + return static_cast<jint>(ret.value_or(0)); +} + +static void nativeSetLightPlayerId(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, + jint lightId, jint playerId) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + 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); +} + static jint nativeGetBatteryCapacity(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); @@ -2192,6 +2279,11 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate}, {"nativeIsVibrating", "(JI)Z", (void*)nativeIsVibrating}, {"nativeGetVibratorIds", "(JI)[I", (void*)nativeGetVibratorIds}, + {"nativeGetLights", "(JI)Ljava/util/List;", (void*)nativeGetLights}, + {"nativeGetLightPlayerId", "(JII)I", (void*)nativeGetLightPlayerId}, + {"nativeGetLightColor", "(JII)I", (void*)nativeGetLightColor}, + {"nativeSetLightPlayerId", "(JIII)V", (void*)nativeSetLightPlayerId}, + {"nativeSetLightColor", "(JIII)V", (void*)nativeSetLightColor}, {"nativeGetBatteryCapacity", "(JI)I", (void*)nativeGetBatteryCapacity}, {"nativeGetBatteryStatus", "(JI)I", (void*)nativeGetBatteryStatus}, {"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts}, @@ -2386,6 +2478,27 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gTouchCalibrationClassInfo.getAffineTransform, gTouchCalibrationClassInfo.clazz, "getAffineTransform", "()[F"); + // Light + FIND_CLASS(gLightClassInfo.clazz, "android/hardware/lights/Light"); + gLightClassInfo.clazz = jclass(env->NewGlobalRef(gLightClassInfo.clazz)); + GET_METHOD_ID(gLightClassInfo.constructor, gLightClassInfo.clazz, "<init>", + "(IIILjava/lang/String;)V"); + + gLightClassInfo.clazz = jclass(env->NewGlobalRef(gLightClassInfo.clazz)); + gLightClassInfo.lightTypeSingle = + env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT_SINGLE", "I"); + gLightClassInfo.lightTypePlayerId = + env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT_PLAYER_ID", "I"); + gLightClassInfo.lightTypeRgb = + env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT_RGB", "I"); + + // ArrayList + FIND_CLASS(gArrayListClassInfo.clazz, "java/util/ArrayList"); + gArrayListClassInfo.clazz = jclass(env->NewGlobalRef(gArrayListClassInfo.clazz)); + GET_METHOD_ID(gArrayListClassInfo.constructor, gArrayListClassInfo.clazz, "<init>", "()V"); + GET_METHOD_ID(gArrayListClassInfo.add, gArrayListClassInfo.clazz, "add", + "(Ljava/lang/Object;)Z"); + // SparseArray FIND_CLASS(gSparseArrayClassInfo.clazz, "android/util/SparseArray"); gSparseArrayClassInfo.clazz = jclass(env->NewGlobalRef(gSparseArrayClassInfo.clazz)); diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp index 5a5b0a8d30aa..7b78b8d23b4b 100644 --- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -44,7 +44,6 @@ namespace android { namespace { using android::base::borrowed_fd; -using android::base::ReadFully; using android::base::unique_fd; using namespace std::literals; @@ -173,7 +172,7 @@ static inline int32_t readLEInt32(borrowed_fd fd) { static inline std::vector<char> readBytes(borrowed_fd fd) { int32_t size = readLEInt32(fd); std::vector<char> result(size); - ReadFully(fd, result.data(), size); + android::base::ReadFully(fd, result.data(), size); return result; } @@ -569,7 +568,7 @@ private: // Awaiting adb handshake. char okay_buf[OKAY.size()]; if (!android::base::ReadFully(inout, okay_buf, OKAY.size())) { - ALOGE("Failed to receive OKAY. Abort."); + ALOGE("Failed to receive OKAY. Abort. Error %d", errno); return false; } if (std::string_view(okay_buf, OKAY.size()) != OKAY) { @@ -693,12 +692,12 @@ private: continue; } if (res < 0) { - ALOGE("Failed to poll. Abort."); + ALOGE("Failed to poll. Abort. Error %d", res); mStatusListener->reportStatus(DATA_LOADER_UNRECOVERABLE); break; } if (res == mEventFd) { - ALOGE("Received stop signal. Sending EXIT to server."); + ALOGE("DataLoader requested to stop. Sending EXIT to server."); sendRequest(inout, EXIT); break; } @@ -712,7 +711,7 @@ private: auto header = readHeader(remainingData); if (header.fileIdx == -1 && header.blockType == 0 && header.compressionType == 0 && header.blockIdx == 0 && header.blockSize == 0) { - ALOGI("Stop signal received. Sending exit command (remaining bytes: %d).", + ALOGI("Stop command received. Sending exit command (remaining bytes: %d).", int(remainingData.size())); sendRequest(inout, EXIT); @@ -721,16 +720,15 @@ private: } if (header.fileIdx < 0 || header.blockSize <= 0 || header.blockType < 0 || header.compressionType < 0 || header.blockIdx < 0) { - ALOGE("invalid header received. Abort."); + ALOGE("Invalid header received. Abort."); mStopReceiving = true; break; } + const FileIdx fileIdx = header.fileIdx; const android::dataloader::FileId fileId = convertFileIndexToFileId(mode, fileIdx); if (!android::incfs::isValidFileId(fileId)) { - ALOGE("Unknown data destination for file ID %d. " - "Ignore.", - header.fileIdx); + ALOGE("Unknown data destination for file ID %d. Ignore.", header.fileIdx); continue; } @@ -738,7 +736,7 @@ private: if (writeFd < 0) { writeFd.reset(this->mIfs->openForSpecialOps(fileId).release()); if (writeFd < 0) { - ALOGE("Failed to open file %d for writing (%d). Aborting.", header.fileIdx, + ALOGE("Failed to open file %d for writing (%d). Abort.", header.fileIdx, -writeFd); break; } diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp index 63a6eedd9e66..9b7e27d891c4 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp @@ -103,7 +103,8 @@ static bool setPowerMode(Mode mode, bool enabled) { return result == power::HalResult::SUCCESSFUL; } -void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) { +void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType, + int32_t displayId) { if (gPowerManagerServiceObj) { // Throttle calls into user activity by event type. // We're a little conservative about argument checking here in case the caller @@ -127,7 +128,7 @@ void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.userActivityFromNative, - nanoseconds_to_milliseconds(eventTime), eventType, 0); + nanoseconds_to_milliseconds(eventTime), eventType, displayId, 0); checkAndClearExceptionFromCallback(env, "userActivityFromNative"); } } @@ -285,7 +286,7 @@ int register_android_server_PowerManagerService(JNIEnv* env) { FIND_CLASS(clazz, "com/android/server/power/PowerManagerService"); GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivityFromNative, clazz, - "userActivityFromNative", "(JII)V"); + "userActivityFromNative", "(JIII)V"); // Initialize for (int i = 0; i <= USER_ACTIVITY_EVENT_LAST; i++) { diff --git a/services/core/jni/com_android_server_power_PowerManagerService.h b/services/core/jni/com_android_server_power_PowerManagerService.h index a17fd650522b..a2f335c74870 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.h +++ b/services/core/jni/com_android_server_power_PowerManagerService.h @@ -24,7 +24,8 @@ namespace android { -extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType); +extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType, + int32_t displayId); } // namespace android diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp index 3f54529211dd..0e21aaf33ec7 100644 --- a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp +++ b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp @@ -217,41 +217,30 @@ static jobjectArray nativeGetStateResidency(JNIEnv *env, jclass clazz, jintArray jobjectArray stateResidencyResultArray = nullptr; Return<void> ret = gPowerStatsHalV1_0_ptr->getPowerEntityStateResidencyData( powerEntityIdVector, [&env, &stateResidencyResultArray](auto results, auto status) { - if (status != Status::SUCCESS) { - ALOGE("Error getting power entity state residency data"); - } else { - stateResidencyResultArray = - env->NewObjectArray(results.size(), class_SRR, nullptr); - for (int i = 0; i < results.size(); i++) { - jobjectArray stateResidencyArray = - env->NewObjectArray(results[i].stateResidencyData.size(), class_SR, - nullptr); - for (int j = 0; j < results[i].stateResidencyData.size(); j++) { - jobject stateResidency = env->NewObject(class_SR, method_SR_init); - env->SetIntField(stateResidency, field_SR_id, - results[i].stateResidencyData[j].powerEntityStateId); - env->SetLongField(stateResidency, field_SR_totalTimeInStateMs, - results[i].stateResidencyData[j].totalTimeInStateMs); - env->SetLongField(stateResidency, field_SR_totalStateEntryCount, - results[i] - .stateResidencyData[j] - .totalStateEntryCount); - env->SetLongField(stateResidency, field_SR_lastEntryTimestampMs, - results[i] - .stateResidencyData[j] - .lastEntryTimestampMs); - env->SetObjectArrayElement(stateResidencyArray, j, stateResidency); - env->DeleteLocalRef(stateResidency); - } - jobject stateResidencyResult = env->NewObject(class_SRR, method_SRR_init); - env->SetIntField(stateResidencyResult, field_SRR_id, - results[i].powerEntityId); - env->SetObjectField(stateResidencyResult, field_SRR_stateResidencyData, - stateResidencyArray); - env->SetObjectArrayElement(stateResidencyResultArray, i, - stateResidencyResult); - env->DeleteLocalRef(stateResidencyResult); + stateResidencyResultArray = env->NewObjectArray(results.size(), class_SRR, nullptr); + for (int i = 0; i < results.size(); i++) { + jobjectArray stateResidencyArray = + env->NewObjectArray(results[i].stateResidencyData.size(), class_SR, + nullptr); + for (int j = 0; j < results[i].stateResidencyData.size(); j++) { + jobject stateResidency = env->NewObject(class_SR, method_SR_init); + env->SetIntField(stateResidency, field_SR_id, + results[i].stateResidencyData[j].powerEntityStateId); + env->SetLongField(stateResidency, field_SR_totalTimeInStateMs, + results[i].stateResidencyData[j].totalTimeInStateMs); + env->SetLongField(stateResidency, field_SR_totalStateEntryCount, + results[i].stateResidencyData[j].totalStateEntryCount); + env->SetLongField(stateResidency, field_SR_lastEntryTimestampMs, + results[i].stateResidencyData[j].lastEntryTimestampMs); + env->SetObjectArrayElement(stateResidencyArray, j, stateResidency); + env->DeleteLocalRef(stateResidency); } + jobject stateResidencyResult = env->NewObject(class_SRR, method_SRR_init); + env->SetIntField(stateResidencyResult, field_SRR_id, results[i].powerEntityId); + env->SetObjectField(stateResidencyResult, field_SRR_stateResidencyData, + stateResidencyArray); + env->SetObjectArrayElement(stateResidencyResultArray, i, stateResidencyResult); + env->DeleteLocalRef(stateResidencyResult); } }); if (!checkResult(ret, __func__)) { @@ -320,30 +309,25 @@ static jobjectArray nativeReadEnergyMeters(JNIEnv *env, jclass clazz, jintArray gPowerStatsHalV1_0_ptr ->getEnergyData(channelIdVector, [&env, &energyMeasurementArray](auto energyData, auto status) { - if (status != Status::SUCCESS) { - ALOGW("Error getting energy data"); - } else { - energyMeasurementArray = - env->NewObjectArray(energyData.size(), class_EM, - nullptr); - for (int i = 0; i < energyData.size(); i++) { - jobject energyMeasurement = - env->NewObject(class_EM, method_EM_init); - env->SetIntField(energyMeasurement, field_EM_id, - energyData[i].index); - env->SetLongField(energyMeasurement, - field_EM_timestampMs, - energyData[i].timestamp); - env->SetLongField(energyMeasurement, - field_EM_durationMs, - energyData[i].timestamp); - env->SetLongField(energyMeasurement, - field_EM_energyUWs, - energyData[i].energy); - env->SetObjectArrayElement(energyMeasurementArray, - i, energyMeasurement); - env->DeleteLocalRef(energyMeasurement); - } + energyMeasurementArray = + env->NewObjectArray(energyData.size(), class_EM, + nullptr); + for (int i = 0; i < energyData.size(); i++) { + jobject energyMeasurement = + env->NewObject(class_EM, method_EM_init); + env->SetIntField(energyMeasurement, field_EM_id, + energyData[i].index); + env->SetLongField(energyMeasurement, + field_EM_timestampMs, + energyData[i].timestamp); + env->SetLongField(energyMeasurement, + field_EM_durationMs, + energyData[i].timestamp); + env->SetLongField(energyMeasurement, field_EM_energyUWs, + energyData[i].energy); + env->SetObjectArrayElement(energyMeasurementArray, i, + energyMeasurement); + env->DeleteLocalRef(energyMeasurement); } }); diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp index bdccf45627f4..c285ef519e44 100644 --- a/services/core/xsd/Android.bp +++ b/services/core/xsd/Android.bp @@ -1,3 +1,12 @@ +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"], +} + xsd_config { name: "default-permissions", srcs: ["default-permissions.xsd"], diff --git a/services/core/xsd/vts/Android.bp b/services/core/xsd/vts/Android.bp index a942108667a5..4d3c79eb28ae 100644 --- a/services/core/xsd/vts/Android.bp +++ b/services/core/xsd/vts/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + cc_test { name: "vts_defaultPermissions_validate_test", srcs: [ diff --git a/services/coverage/Android.bp b/services/coverage/Android.bp index b3cee375f95c..82feafecdc56 100644 --- a/services/coverage/Android.bp +++ b/services/coverage/Android.bp @@ -1,3 +1,12 @@ +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: "services.coverage-sources", srcs: ["java/**/*.java"], diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp index 5de48aefe28e..3a9853d66d10 100644 --- a/services/devicepolicy/Android.bp +++ b/services/devicepolicy/Android.bp @@ -1,3 +1,12 @@ +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: "services.devicepolicy-sources", srcs: ["java/**/*.java"], diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index b52347f509f8..ef7afc8d1894 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -129,6 +129,15 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) { } + @Override + public void setDeviceOwnerType(@NonNull ComponentName admin, int deviceOwnerType) { + } + + @Override + public int getDeviceOwnerType(@NonNull ComponentName admin) { + return 0; + } + public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {} public boolean canAdminGrantSensorsPermissionsForUser(int userId) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 1cf4ce163640..28e9acf8d883 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -156,6 +156,7 @@ import android.app.admin.DeviceAdminReceiver; import android.app.admin.DevicePolicyCache; import android.app.admin.DevicePolicyEventLogger; import android.app.admin.DevicePolicyManager; +import android.app.admin.DevicePolicyManager.DeviceOwnerType; import android.app.admin.DevicePolicyManager.DevicePolicyOperation; import android.app.admin.DevicePolicyManager.OperationSafetyReason; import android.app.admin.DevicePolicyManager.PasswordComplexity; @@ -222,6 +223,7 @@ import android.net.ConnectivityManager; import android.net.IIpConnectivityMetrics; import android.net.ProxyInfo; import android.net.Uri; +import android.net.VpnManager; import android.net.metrics.IpConnectivityLog; import android.net.wifi.WifiManager; import android.os.Binder; @@ -332,6 +334,7 @@ import com.google.android.collect.Sets; import org.xmlpull.v1.XmlPullParserException; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -339,6 +342,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Constructor; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.text.DateFormat; import java.time.LocalDate; import java.util.ArrayList; @@ -709,6 +715,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Nullable private DevicePolicySafetyChecker mSafetyChecker; + @GuardedBy("getLockObject()") + private final ArrayList<Object> mPendingUserCreatedCallbackTokens = new ArrayList<>(); + public static final class Lifecycle extends SystemService { private BaseIDevicePolicyManager mService; @@ -747,21 +756,25 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void onUserStarting(@NonNull TargetUser user) { + if (user.isPreCreated()) return; mService.handleStartUser(user.getUserIdentifier()); } @Override public void onUserUnlocking(@NonNull TargetUser user) { + if (user.isPreCreated()) return; mService.handleUnlockUser(user.getUserIdentifier()); } @Override public void onUserStopping(@NonNull TargetUser user) { + if (user.isPreCreated()) return; mService.handleStopUser(user.getUserIdentifier()); } @Override public void onUserUnlocked(@NonNull TargetUser user) { + if (user.isPreCreated()) return; mService.handleOnUserUnlocked(user.getUserIdentifier()); } } @@ -959,8 +972,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private final class UserLifecycleListener implements UserManagerInternal.UserLifecycleListener { @Override - public void onUserCreated(UserInfo user) { - mHandler.post(() -> handleNewUserCreated(user)); + public void onUserCreated(UserInfo user, Object token) { + mHandler.post(() -> handleNewUserCreated(user, token)); } } @@ -1138,6 +1151,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isSafeOperation(@OperationSafetyReason int reason) { + if (VERBOSE_LOG) { + Slog.v(LOG_TAG, "checking isSafeOperation(" + + DevicePolicyManager.operationSafetyReasonToString(reason) + + ") using mSafetyChecker " + mSafetyChecker); + } return mSafetyChecker == null ? true : mSafetyChecker.isSafeOperation(reason); } @@ -1267,6 +1285,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mContext.getSystemService(ConnectivityManager.class); } + VpnManager getVpnManager() { + return mContext.getSystemService(VpnManager.class); + } + LocationManager getLocationManager() { return mContext.getSystemService(LocationManager.class); } @@ -3136,11 +3158,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return; } - setActiveAdmin(adminReceiver, refreshing, userHandle, null); - } - - private void setActiveAdmin(ComponentName adminReceiver, boolean refreshing, int userHandle, - Bundle onEnableData) { Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); final CallerIdentity caller = getCallerIdentity(); @@ -3183,7 +3200,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } saveSettingsLocked(userHandle); sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED, - onEnableData, null); + /* adminExtras= */ null, /* result= */ null); }); } } @@ -3389,8 +3406,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(adminReceiver, "ComponentName is null"); - Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), - "Non-shell user attempted to call forceRemoveActiveAdmin"); + Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()) + || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS), + "Caller must be shell or hold MANAGE_PROFILE_AND_DEVICE_OWNERS to call " + + "forceRemoveActiveAdmin"); mInjector.binderWithCleanCallingIdentity(() -> { synchronized (getLockObject()) { if (!isAdminTestOnlyLocked(adminReceiver, userHandle)) { @@ -5612,7 +5631,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Get attestation flags, if any. final int[] attestationUtilsFlags = translateIdAttestationFlags(idAttestationFlags); final boolean deviceIdAttestationRequired = attestationUtilsFlags != null; - final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec(); + KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec(); final String alias = keySpec.getKeystoreAlias(); Preconditions.checkStringNotEmpty(alias, "Empty alias provided"); @@ -5633,6 +5652,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp))); } + if (TextUtils.isEmpty(alias)) { + throw new IllegalArgumentException("Empty alias provided."); + } // As the caller will be granted access to the key, ensure no UID was specified, as // it will not have the desired effect. if (keySpec.getUid() != KeyStore.UID_SELF) { @@ -5641,19 +5663,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } + if (deviceIdAttestationRequired) { + if (keySpec.getAttestationChallenge() == null) { + throw new IllegalArgumentException( + "Requested Device ID attestation but challenge is empty."); + } + KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder(keySpec); + specBuilder.setAttestationIds(attestationUtilsFlags); + specBuilder.setDevicePropertiesAttestationIncluded(true); + keySpec = specBuilder.build(); + } + + final UserHandle userHandle = mInjector.binderGetCallingUserHandle(); final long id = mInjector.binderClearCallingIdentity(); try { try (KeyChainConnection keyChainConnection = - KeyChain.bindAsUser(mContext, caller.getUserHandle())) { + KeyChain.bindAsUser(mContext, userHandle)) { IKeyChainService keyChain = keyChainConnection.getService(); - // Copy the provided keySpec, excluding the attestation challenge, which will be - // used later for requesting key attestation record. - final KeyGenParameterSpec noAttestationSpec = new KeyGenParameterSpec.Builder( - keySpec).setAttestationChallenge(null).build(); - final int generationResult = keyChain.generateKeyPair(algorithm, - new ParcelableKeyGenParameterSpec(noAttestationSpec)); + new ParcelableKeyGenParameterSpec(keySpec)); if (generationResult != KeyChain.KEY_GEN_SUCCESS) { Log.e(LOG_TAG, String.format( "KeyChain failed to generate a keypair, error %d.", generationResult)); @@ -5662,6 +5691,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new ServiceSpecificException( DevicePolicyManager.KEY_GEN_STRONGBOX_UNAVAILABLE, String.format("KeyChain error: %d", generationResult)); + case KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS: + throw new UnsupportedOperationException( + "Device does not support Device ID attestation."); default: logGenerateKeyPairFailure(caller, isCredentialManagementApp); return false; @@ -5675,23 +5707,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // that UID. keyChain.setGrant(caller.getUid(), alias, true); - final byte[] attestationChallenge = keySpec.getAttestationChallenge(); - if (attestationChallenge != null) { - final int attestationResult = keyChain.attestKey( - alias, attestationChallenge, attestationUtilsFlags, attestationChain); - if (attestationResult != KeyChain.KEY_ATTESTATION_SUCCESS) { - Log.e(LOG_TAG, String.format( - "Attestation for %s failed (rc=%d), deleting key.", - alias, attestationResult)); - keyChain.removeKeyPair(alias); - if (attestationResult == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) { - throw new UnsupportedOperationException( - "Device does not support Device ID attestation."); + try { + final List<byte[]> encodedCerts = new ArrayList(); + final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + final byte[] certChainBytes = keyChain.getCaCertificates(alias); + encodedCerts.add(keyChain.getCertificate(alias)); + if (certChainBytes != null) { + final Collection<X509Certificate> certs = + (Collection<X509Certificate>) certFactory.generateCertificates( + new ByteArrayInputStream(certChainBytes)); + for (X509Certificate cert : certs) { + encodedCerts.add(cert.getEncoded()); } - logGenerateKeyPairFailure(caller, isCredentialManagementApp); - return false; } + + attestationChain.shallowCopyFrom(new KeymasterCertificateChain(encodedCerts)); + } catch (CertificateException e) { + logGenerateKeyPairFailure(caller, isCredentialManagementApp); + Log.e(LOG_TAG, "While retrieving certificate chain.", e); + return false; } + DevicePolicyEventLogger .createEvent(DevicePolicyEnums.GENERATE_KEY_PAIR) .setAdmin(caller.getPackageName()) @@ -6302,7 +6338,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } // If some package is uninstalled after the check above, it will be ignored by CM. - if (!mInjector.getConnectivityManager().setAlwaysOnVpnPackageForUser( + if (!mInjector.getVpnManager().setAlwaysOnVpnPackageForUser( userId, vpnPackage, lockdown, lockdownAllowlist)) { throw new UnsupportedOperationException(); } @@ -6334,8 +6370,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); return mInjector.binderWithCleanCallingIdentity( - () -> mInjector.getConnectivityManager().getAlwaysOnVpnPackageForUser( - caller.getUserId())); + () -> mInjector.getVpnManager().getAlwaysOnVpnPackageForUser(caller.getUserId())); } @Override @@ -6361,7 +6396,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } return mInjector.binderWithCleanCallingIdentity( - () -> mInjector.getConnectivityManager().isVpnLockdownEnabled(caller.getUserId())); + () -> mInjector.getVpnManager().isVpnLockdownEnabled(caller.getUserId())); } @Override @@ -6382,8 +6417,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); return mInjector.binderWithCleanCallingIdentity( - () -> mInjector.getConnectivityManager().getVpnLockdownAllowlist( - caller.getUserId())); + () -> mInjector.getVpnManager().getVpnLockdownAllowlist(caller.getUserId())); } private void forceWipeDeviceNoLock(boolean wipeExtRequested, String reason, boolean wipeEuicc, @@ -7552,8 +7586,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private void sendProfileOwnerCommand(String action, Bundle extras, @UserIdInt int userId) { - sendActiveAdminCommand(action, extras, userId, - mOwners.getProfileOwnerComponent(userId)); + sendActiveAdminCommand(action, extras, userId, mOwners.getProfileOwnerComponent(userId)); } private void sendActiveAdminCommand(String action, Bundle extras, @@ -8081,7 +8114,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } if (!callingUserOnly) { - Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity())); + Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()) + || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); } synchronized (getLockObject()) { if (!mOwners.hasDeviceOwner()) { @@ -9155,13 +9189,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { pw.println(); mStatLogger.dump(pw); pw.println(); - pw.println("Encryption Status: " + getEncryptionStatusName(getEncryptionStatus())); pw.println(); + + if (mPendingUserCreatedCallbackTokens.isEmpty()) { + pw.println("no pending user created callback tokens"); + } else { + int size = mPendingUserCreatedCallbackTokens.size(); + pw.printf("%d pending user created callback token%s\n", size, + (size == 1 ? "" : "s")); + } + pw.println(); + mPolicyCache.dump(pw); pw.println(); mStateCache.dump(pw); + pw.println(); } + dumpResources(pw); } } @@ -9172,8 +9217,33 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { pw.printf("mIsWatch=%b\n", mIsWatch); pw.printf("mIsAutomotive=%b\n", mIsAutomotive); pw.printf("mHasTelephonyFeature=%b\n", mHasTelephonyFeature); - String safetyChecker = mSafetyChecker == null ? "N/A" : mSafetyChecker.getClass().getName(); - pw.printf("mSafetyChecker=%b\n", safetyChecker); + pw.printf("mSafetyChecker=%s\n", mSafetyChecker); + pw.decreaseIndent(); + } + + private void dumpResources(IndentingPrintWriter pw) { + mOverlayPackagesProvider.dump(pw); + pw.println(); + + pw.println("Other overlayable app resources"); + pw.increaseIndent(); + dumpResources(pw, mContext, "cross_profile_apps", R.array.cross_profile_apps); + dumpResources(pw, mContext, "vendor_cross_profile_apps", R.array.vendor_cross_profile_apps); + pw.decreaseIndent(); + pw.println(); + } + + static void dumpResources(IndentingPrintWriter pw, Context context, String resName, int resId) { + String[] apps = context.getResources().getStringArray(resId); + if (apps == null || apps.length == 0) { + pw.printf("%s: empty\n", resName); + return; + } + pw.printf("%s: %d app%s\n", resName, apps.length, apps.length == 1 ? "" : "s"); + pw.increaseIndent(); + for (int i = 0; i < apps.length; i++) { + pw.printf("%d: %s\n", i, apps[i]); + } pw.decreaseIndent(); } @@ -9915,8 +9985,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { clearInitBundle = sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED, initBundle == null ? null : new Bundle(initBundle), - null /* result receiver */, - true /* send in foreground */); + /* result= */ null , + /* inForeground= */ true); } if (clearInitBundle) { // If there's no admin or we've successfully called the admin, clear the init bundle @@ -9989,9 +10059,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { UserHandle.myUserId(), ACTION_PROVISION_MANAGED_USER).toArray( new String[0]); } + + Object token = new Object(); + Slog.d(LOG_TAG, "Adding new pending token: " + token); + mPendingUserCreatedCallbackTokens.add(token); try { UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name, - userType, userInfoFlags, disallowedPackages); + userType, userInfoFlags, disallowedPackages, token); if (userInfo != null) { user = userInfo.getUserHandle(); } @@ -10001,7 +10075,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } finally { mInjector.binderRestoreCallingIdentity(id); } - } + } // synchronized + if (user == null) { if (targetSdkVersion >= Build.VERSION_CODES.P) { throw new ServiceSpecificException(UserManager.USER_OPERATION_ERROR_UNKNOWN, @@ -10023,14 +10098,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final long id = mInjector.binderClearCallingIdentity(); try { - if (!mInjector.userManagerIsHeadlessSystemUserMode()) { - manageUserUnchecked(admin, profileOwner, userHandle, adminExtras, - /* showDisclaimer= */ true); - } else if (VERBOSE_LOG) { - Slog.v(LOG_TAG, "createAndManageUser(): skipping manageUserUnchecked() on user " - + userHandle + " on headless system user as it will be called by " - + "handleNewUserCreated()"); - } + manageUserUnchecked(admin, profileOwner, userHandle, adminExtras, + /* showDisclaimer= */ true); if ((flags & DevicePolicyManager.SKIP_SETUP_WIZARD) != 0) { Settings.Secure.putIntForUser(mContext.getContentResolver(), @@ -10052,7 +10121,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private void manageUserUnchecked(ComponentName admin, ComponentName profileOwner, - @UserIdInt int userId, PersistableBundle adminExtras, boolean showDisclaimer) { + @UserIdInt int userId, @Nullable PersistableBundle adminExtras, + boolean showDisclaimer) { + synchronized (getLockObject()) { + if (VERBOSE_LOG) { + Slog.v(LOG_TAG, "manageUserUnchecked(): admin=" + admin + ", po=" + profileOwner + + ", userId=" + userId + ", hasAdminExtras=" + (adminExtras != null) + + ", showDisclaimer=" + showDisclaimer); + } + } final String adminPkg = admin.getPackageName(); try { // Install the profile owner if not present. @@ -10082,24 +10159,37 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ? DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED : DevicePolicyData.NEW_USER_DISCLAIMER_NOT_NEEDED; saveSettingsLocked(userId); + } } - private void handleNewUserCreated(UserInfo user) { - if (VERBOSE_LOG) Slog.v(LOG_TAG, "handleNewUserCreated(): " + user.toFullString()); - - if (!mOwners.hasDeviceOwner() || !user.isFull() || user.isManagedProfile()) return; + private void handleNewUserCreated(UserInfo user, @Nullable Object token) { + if (VERBOSE_LOG) { + Slog.v(LOG_TAG, "handleNewUserCreated(): user=" + user.toFullString() + + ", token=" + token); + } final int userId = user.id; + if (token != null) { + synchronized (getLockObject()) { + if (mPendingUserCreatedCallbackTokens.contains(token)) { + // Ignore because it was triggered by createAndManageUser() + Slog.d(LOG_TAG, "handleNewUserCreated(): ignoring for user " + userId + + " due to token" + token); + mPendingUserCreatedCallbackTokens.remove(token); + return; + } + } + } + + if (!mOwners.hasDeviceOwner() || !user.isFull() || user.isManagedProfile()) return; - // TODO(b/177547285): add CTS test if (mInjector.userManagerIsHeadlessSystemUserMode()) { ComponentName admin = mOwners.getDeviceOwnerComponent(); Slog.i(LOG_TAG, "Automatically setting profile owner (" + admin + ") on new user " + userId); manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin, - /* managedUser= */ userId, /* adminExtras= */ null, - /* showDisclaimer= */ true); + /* managedUser= */ userId, /* adminExtras= */ null, /* showDisclaimer= */ true); } else { Log.i(LOG_TAG, "User " + userId + " added on DO mode; setting ShowNewUserDisclaimer"); setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED); @@ -10220,11 +10310,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final long id = mInjector.binderClearCallingIdentity(); try { if (!mInjector.getActivityManagerInternal().canStartMoreUsers()) { - Log.w(LOG_TAG, "Cannot start more users in background"); + Log.w(LOG_TAG, "Cannot start user " + userId + ", too many users in background"); return UserManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS; } if (mInjector.getIActivityManager().startUserInBackground(userId)) { + Log.i(LOG_TAG, "Started used " + userId + " in background"); return UserManager.USER_OPERATION_SUCCESS; } else { return UserManager.USER_OPERATION_ERROR_UNKNOWN; @@ -12338,7 +12429,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slog.v(LOG_TAG, String.format("notifyUnsafeOperationStateChanged(): %s=%b", DevicePolicyManager.operationSafetyReasonToString(reason), isSafe)); } - Preconditions.checkArgument(mSafetyChecker == checker, "invalid checker: should be %s, was %s", mSafetyChecker, checker); @@ -12346,9 +12436,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { extras.putInt(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_REASON, reason); extras.putBoolean(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_STATE, isSafe); - // TODO(b/178494483): add CTS test - sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED, - extras); + if (mOwners.hasDeviceOwner()) { + sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED, + extras); + } for (int profileOwnerId : mOwners.getProfileOwnerKeys()) { sendProfileOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED, extras, profileOwnerId); @@ -12539,8 +12630,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void clearSystemUpdatePolicyFreezePeriodRecord() { - Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), - "Non-shell user attempted to call clearSystemUpdatePolicyFreezePeriodRecord"); + Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()) + || hasCallingOrSelfPermission(permission.CLEAR_FREEZE_PERIOD), + "Caller must be shell, or hold CLEAR_FREEZE_PERIOD permission to call " + + "clearSystemUpdatePolicyFreezePeriodRecord"); synchronized (getLockObject()) { // Print out current record to help diagnosed CTS failures Slog.i(LOG_TAG, "Clear freeze period record: " @@ -12911,8 +13004,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (mOwners.hasProfileOwner(deviceOwnerUserId)) { return CODE_USER_HAS_PROFILE_OWNER; } + + boolean isHeadlessSystemUserMode = mInjector.userManagerIsHeadlessSystemUserMode(); // System user is always running in headless system user mode. - if (!mInjector.userManagerIsHeadlessSystemUserMode() + if (!isHeadlessSystemUserMode && !mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) { return CODE_USER_NOT_RUNNING; } @@ -12920,7 +13015,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return CODE_HAS_PAIRED; } - if (mInjector.userManagerIsHeadlessSystemUserMode()) { + if (isHeadlessSystemUserMode) { if (deviceOwnerUserId != UserHandle.USER_SYSTEM) { Slog.e(LOG_TAG, "In headless system user mode, " + "device owner can only be set on headless system user."); @@ -12932,9 +13027,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // If shell command runs after user setup completed check device status. Otherwise, OK. if (mIsWatch || hasUserSetupCompleted(UserHandle.USER_SYSTEM)) { // In non-headless system user mode, DO can be setup only if - // there's no non-system user - if (!mInjector.userManagerIsHeadlessSystemUserMode() - && mUserManager.getUserCount() > 1) { + // there's no non-system user. + // In headless system user mode, DO can be setup only if there are + // two users: the headless system user and the foreground user. + // If there could be multiple foreground users, this constraint should be modified. + + int maxNumberOfExistingUsers = isHeadlessSystemUserMode ? 2 : 1; + if (mUserManager.getUserCount() > maxNumberOfExistingUsers) { return CODE_NONSYSTEM_USER_EXISTS; } @@ -13477,7 +13576,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); // Only adb or system apps with the right permission can mark a profile owner on // organization-owned device. - if (!(isAdb(caller) || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED))) { + if (!(isAdb(caller) || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED) + || hasCallingPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS))) { throw new SecurityException( "Only the system can mark a profile owner of organization-owned device."); } @@ -13796,8 +13896,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public long forceSecurityLogs() { - Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), - "Non-shell user attempted to call forceSecurityLogs"); + Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()) + || hasCallingOrSelfPermission(permission.FORCE_DEVICE_POLICY_MANAGER_LOGS), + "Caller must be shell or hold FORCE_DEVICE_POLICY_MANAGER_LOGS to call " + + "forceSecurityLogs"); if (!mInjector.securityLogGetLoggingEnabledProperty()) { throw new IllegalStateException("logging is not available"); } @@ -14317,8 +14419,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public long forceNetworkLogs() { - Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()), - "Non-shell user attempted to call forceNetworkLogs"); + Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()) + || hasCallingOrSelfPermission(permission.FORCE_DEVICE_POLICY_MANAGER_LOGS), + "Caller must be shell or hold FORCE_DEVICE_POLICY_MANAGER_LOGS to call " + + "forceNetworkLogs"); synchronized (getLockObject()) { if (!isNetworkLoggingEnabledInternalLocked()) { throw new IllegalStateException("logging is not available"); @@ -16721,6 +16825,37 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public void setDeviceOwnerType(@NonNull ComponentName admin, + @DeviceOwnerType int deviceOwnerType) { + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( + permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); + verifyDeviceOwnerTypePreconditions(admin); + + final String packageName = admin.getPackageName(); + Preconditions.checkState(!mOwners.isDeviceOwnerTypeSetForDeviceOwner(packageName), + "The device owner type has already been set for " + packageName); + + synchronized (getLockObject()) { + mOwners.setDeviceOwnerType(packageName, deviceOwnerType); + } + } + + @Override + @DeviceOwnerType + public int getDeviceOwnerType(@NonNull ComponentName admin) { + verifyDeviceOwnerTypePreconditions(admin); + synchronized (getLockObject()) { + return mOwners.getDeviceOwnerType(admin.getPackageName()); + } + } + + private void verifyDeviceOwnerTypePreconditions(@NonNull ComponentName admin) { + Preconditions.checkState(mOwners.hasDeviceOwner(), "there is no device owner"); + Preconditions.checkState(mOwners.getDeviceOwnerComponent().equals(admin), + "admin is not the device owner"); + } + + @Override public void setUsbDataSignalingEnabled(String packageName, boolean enabled) { Objects.requireNonNull(packageName, "Admin package name must be provided"); final CallerIdentity caller = getCallerIdentity(packageName); @@ -16784,7 +16919,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked( UserHandle.USER_SYSTEM); - return admin != null && admin.mUsbDataSignalingEnabled; + return admin == null || admin.mUsbDataSignalingEnabled; } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java index 7de1bd50a9eb..257fc640f93c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java @@ -23,6 +23,8 @@ import android.app.admin.DevicePolicyManager.DevicePolicyOperation; import android.app.admin.DevicePolicyManager.OperationSafetyReason; import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.DevicePolicySafetyChecker; +import android.os.Handler; +import android.os.Looper; import android.util.Slog; import com.android.internal.os.IResultReceiver; @@ -42,10 +44,15 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { private static final String TAG = OneTimeSafetyChecker.class.getSimpleName(); + private static final long SELF_DESTRUCT_TIMEOUT_MS = 10_000; + private final DevicePolicyManagerService mService; private final DevicePolicySafetyChecker mRealSafetyChecker; private final @DevicePolicyOperation int mOperation; private final @OperationSafetyReason int mReason; + private final Handler mHandler = new Handler(Looper.getMainLooper()); + + private boolean mDone; OneTimeSafetyChecker(DevicePolicyManagerService service, @DevicePolicyOperation int operation, @OperationSafetyReason int reason) { @@ -53,7 +60,11 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { mOperation = operation; mReason = reason; mRealSafetyChecker = service.getDevicePolicySafetyChecker(); - Slog.i(TAG, "Saving real DevicePolicySafetyChecker as " + mRealSafetyChecker); + Slog.i(TAG, "OneTimeSafetyChecker constructor: operation= " + operationToString(operation) + + ", reason=" + operationSafetyReasonToString(reason) + + ", realChecker=" + mRealSafetyChecker + + ", maxDuration=" + SELF_DESTRUCT_TIMEOUT_MS + "ms"); + mHandler.postDelayed(() -> selfDestruct(), SELF_DESTRUCT_TIMEOUT_MS); } @Override @@ -72,15 +83,15 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { DevicePolicyManagerInternal dpmi = LocalServices .getService(DevicePolicyManagerInternal.class); - Slog.i(TAG, "notifying " + reasonName + " is active"); - dpmi.notifyUnsafeOperationStateChanged(this, reason, true); + Slog.i(TAG, "notifying " + reasonName + " is UNSAFE"); + dpmi.notifyUnsafeOperationStateChanged(this, reason, /* isSafe= */ false); - Slog.i(TAG, "notifying " + reasonName + " is inactive"); - dpmi.notifyUnsafeOperationStateChanged(this, reason, false); + Slog.i(TAG, "notifying " + reasonName + " is SAFE"); + dpmi.notifyUnsafeOperationStateChanged(this, reason, /* isSafe= */ true); - Slog.i(TAG, "returning " + reasonName - + " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker); - mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker); + Slog.i(TAG, "returning " + reasonName); + + disableSelf(); return reason; } @@ -89,6 +100,7 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { boolean safe = mReason != reason; Slog.i(TAG, "isSafeOperation(" + operationSafetyReasonToString(reason) + "): " + safe); + disableSelf(); return safe; } @@ -96,4 +108,29 @@ final class OneTimeSafetyChecker implements DevicePolicySafetyChecker { public void onFactoryReset(IResultReceiver callback) { throw new UnsupportedOperationException(); } + + private void disableSelf() { + if (mDone) { + Slog.w(TAG, "disableSelf(): already disabled"); + return; + } + Slog.i(TAG, "restoring DevicePolicySafetyChecker to " + mRealSafetyChecker); + mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker); + mDone = true; + } + + private void selfDestruct() { + if (mDone) return; + + // Usually happens when a CTS failed before calling the DPM method that would clear it + Slog.e(TAG, "Self destructing " + this + ", as it was not automatically disabled"); + disableSelf(); + } + + @Override + public String toString() { + return "OneTimeSafetyChecker[id=" + System.identityHashCode(this) + + ", reason=" + operationSafetyReasonToString(mReason) + + ", operation=" + operationToString(mOperation) + ']'; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java index 261a83c4f67a..280f12f6a60e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java @@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PRO import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER; import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.server.devicepolicy.DevicePolicyManagerService.dumpResources; import android.annotation.NonNull; import android.annotation.UserIdInt; @@ -32,6 +33,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.view.inputmethod.InputMethodInfo; import com.android.internal.R; @@ -224,4 +226,39 @@ public class OverlayPackagesProvider { } return new ArraySet<>(Arrays.asList(mContext.getResources().getStringArray(resId))); } + + void dump(IndentingPrintWriter pw) { + pw.println("OverlayPackagesProvider"); + pw.increaseIndent(); + + dumpResources(pw, mContext, "required_apps_managed_device", + R.array.required_apps_managed_device); + dumpResources(pw, mContext, "required_apps_managed_user", + R.array.required_apps_managed_user); + dumpResources(pw, mContext, "required_apps_managed_profile", + R.array.required_apps_managed_profile); + + dumpResources(pw, mContext, "disallowed_apps_managed_device", + R.array.disallowed_apps_managed_device); + dumpResources(pw, mContext, "disallowed_apps_managed_user", + R.array.disallowed_apps_managed_user); + dumpResources(pw, mContext, "disallowed_apps_managed_device", + R.array.disallowed_apps_managed_device); + + dumpResources(pw, mContext, "vendor_required_apps_managed_device", + R.array.vendor_required_apps_managed_device); + dumpResources(pw, mContext, "vendor_required_apps_managed_user", + R.array.vendor_required_apps_managed_user); + dumpResources(pw, mContext, "vendor_required_apps_managed_profile", + R.array.vendor_required_apps_managed_profile); + + dumpResources(pw, mContext, "vendor_disallowed_apps_managed_user", + R.array.vendor_disallowed_apps_managed_user); + dumpResources(pw, mContext, "vendor_disallowed_apps_managed_device", + R.array.vendor_disallowed_apps_managed_device); + dumpResources(pw, mContext, "vendor_disallowed_apps_managed_profile", + R.array.vendor_disallowed_apps_managed_profile); + + pw.decreaseIndent(); + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 1e70d59a5fd5..7fdd6eeef642 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -16,10 +16,13 @@ package com.android.server.devicepolicy; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; + import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.app.AppOpsManagerInternal; +import android.app.admin.DevicePolicyManager.DeviceOwnerType; import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; import android.content.ComponentName; @@ -93,6 +96,7 @@ class Owners { private static final String TAG_PROFILE_OWNER = "profile-owner"; // Holds "context" for device-owner, this must not be show up before device-owner. private static final String TAG_DEVICE_OWNER_CONTEXT = "device-owner-context"; + private static final String TAG_DEVICE_OWNER_TYPE = "device-owner-type"; private static final String ATTR_NAME = "name"; private static final String ATTR_PACKAGE = "package"; @@ -109,6 +113,7 @@ class Owners { // New attribute for profile owner of organization-owned device. private static final String ATTR_PROFILE_OWNER_OF_ORG_OWNED_DEVICE = "isPoOrganizationOwnedDevice"; + private static final String ATTR_DEVICE_OWNER_TYPE_VALUE = "value"; private final UserManager mUserManager; private final UserManagerInternal mUserManagerInternal; @@ -121,6 +126,9 @@ class Owners { // Internal state for the device owner package. private OwnerInfo mDeviceOwner; + // Device owner type for a managed device. + private final ArrayMap<String, Integer> mDeviceOwnerTypes = new ArrayMap<>(); + private int mDeviceOwnerUserId = UserHandle.USER_NULL; // Internal state for the profile owner packages. @@ -334,6 +342,7 @@ class Owners { void clearDeviceOwner() { synchronized (mLock) { + mDeviceOwnerTypes.remove(mDeviceOwner.packageName); mDeviceOwner = null; mDeviceOwnerUserId = UserHandle.USER_NULL; @@ -384,12 +393,16 @@ class Owners { void transferDeviceOwnership(ComponentName target) { synchronized (mLock) { + Integer previousDeviceOwnerType = mDeviceOwnerTypes.remove(mDeviceOwner.packageName); // We don't set a name because it's not used anyway. // See DevicePolicyManagerService#getDeviceOwnerName mDeviceOwner = new OwnerInfo(null, target, mDeviceOwner.userRestrictionsMigrated, mDeviceOwner.remoteBugreportUri, mDeviceOwner.remoteBugreportHash, /* isOrganizationOwnedDevice =*/ mDeviceOwner.isOrganizationOwnedDevice); + if (previousDeviceOwnerType != null) { + mDeviceOwnerTypes.put(mDeviceOwner.packageName, previousDeviceOwnerType); + } pushToPackageManagerLocked(); pushToActivityTaskManagerLocked(); pushToActivityManagerLocked(); @@ -596,6 +609,37 @@ class Owners { } } + void setDeviceOwnerType(String packageName, @DeviceOwnerType int deviceOwnerType) { + synchronized (mLock) { + if (!hasDeviceOwner()) { + Slog.e(TAG, "Attempting to set a device owner type when there is no device owner"); + return; + } else if (isDeviceOwnerTypeSetForDeviceOwner(packageName)) { + Slog.e(TAG, "Device owner type for " + packageName + " has already been set"); + return; + } + + mDeviceOwnerTypes.put(packageName, deviceOwnerType); + writeDeviceOwner(); + } + } + + @DeviceOwnerType + int getDeviceOwnerType(String packageName) { + synchronized (mLock) { + if (isDeviceOwnerTypeSetForDeviceOwner(packageName)) { + return mDeviceOwnerTypes.get(packageName); + } + return DEVICE_OWNER_TYPE_DEFAULT; + } + } + + boolean isDeviceOwnerTypeSetForDeviceOwner(String packageName) { + synchronized (mLock) { + return !mDeviceOwnerTypes.isEmpty() && mDeviceOwnerTypes.containsKey(packageName); + } + } + private boolean readLegacyOwnerFileLocked(File file) { if (!file.exists()) { // Already migrated or the device has no owners. @@ -880,6 +924,16 @@ class Owners { out.startTag(null, TAG_DEVICE_OWNER_CONTEXT); out.attributeInt(null, ATTR_USERID, mDeviceOwnerUserId); out.endTag(null, TAG_DEVICE_OWNER_CONTEXT); + + } + + if (!mDeviceOwnerTypes.isEmpty()) { + for (ArrayMap.Entry<String, Integer> entry : mDeviceOwnerTypes.entrySet()) { + out.startTag(null, TAG_DEVICE_OWNER_TYPE); + out.attribute(null, ATTR_PACKAGE, entry.getKey()); + out.attributeInt(null, ATTR_DEVICE_OWNER_TYPE_VALUE, entry.getValue()); + out.endTag(null, TAG_DEVICE_OWNER_TYPE); + } } if (mSystemUpdatePolicy != null) { @@ -942,6 +996,12 @@ class Owners { } } break; + case TAG_DEVICE_OWNER_TYPE: + String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); + int deviceOwnerType = parser.getAttributeInt(null, ATTR_DEVICE_OWNER_TYPE_VALUE, + DEVICE_OWNER_TYPE_DEFAULT); + mDeviceOwnerTypes.put(packageName, deviceOwnerType); + break; default: Slog.e(TAG, "Unexpected tag: " + tag); return false; diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp index 7534c7c40a3d..5ffbd771764d 100644 --- a/services/incremental/Android.bp +++ b/services/incremental/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + cc_defaults { name: "service.incremental-proto-defaults", @@ -124,5 +133,8 @@ cc_test { ], static_libs: [ "libgmock", - ] + ], + test_options: { + unit_test: true, + }, } diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 886c1e5b98ea..ce6e6ab1e29c 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -66,6 +66,8 @@ struct Constants { static constexpr auto blockSize = 4096; static constexpr auto systemPackage = "android"sv; + static constexpr auto userStatusDelay = 100ms; + static constexpr auto progressUpdateInterval = 1000ms; static constexpr auto perUidTimeoutOffset = progressUpdateInterval * 2; static constexpr auto minPerUidTimeout = progressUpdateInterval * 3; @@ -1615,7 +1617,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ // Need a shared pointer: will be passing it into all unpacking jobs. std::shared_ptr<ZipArchive> zipFile(zipFileHandle, [](ZipArchiveHandle h) { CloseArchive(h); }); void* cookie = nullptr; - const auto libFilePrefix = path::join(constants().libDir, abi) + "/"; + const auto libFilePrefix = path::join(constants().libDir, abi) += "/"; if (StartIteration(zipFile.get(), &cookie, libFilePrefix, constants().libSuffix)) { LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath; return false; @@ -1625,6 +1627,17 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ auto openZipTs = Clock::now(); + auto mapFiles = (mIncFs->features() & incfs::Features::v2); + incfs::FileId sourceId; + if (mapFiles) { + sourceId = mIncFs->getFileId(ifs->control, apkFullPath); + if (!incfs::isValidFileId(sourceId)) { + LOG(WARNING) << "Error getting IncFS file ID for apk path '" << apkFullPath + << "', mapping disabled"; + mapFiles = false; + } + } + std::vector<Job> jobQueue; ZipEntry entry; std::string_view fileName; @@ -1633,13 +1646,16 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ continue; } + const auto entryUncompressed = entry.method == kCompressStored; + const auto entryPageAligned = (entry.offset & (constants().blockSize - 1)) == 0; + if (!extractNativeLibs) { // ensure the file is properly aligned and unpacked - if (entry.method != kCompressStored) { + if (!entryUncompressed) { LOG(WARNING) << "Library " << fileName << " must be uncompressed to mmap it"; return false; } - if ((entry.offset & (constants().blockSize - 1)) != 0) { + if (!entryPageAligned) { LOG(WARNING) << "Library " << fileName << " must be page-aligned to mmap it, offset = 0x" << std::hex << entry.offset; @@ -1663,6 +1679,28 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ continue; } + if (mapFiles && entryUncompressed && entryPageAligned && entry.uncompressed_length > 0) { + incfs::NewMappedFileParams mappedFileParams = { + .sourceId = sourceId, + .sourceOffset = entry.offset, + .size = entry.uncompressed_length, + }; + + if (auto res = mIncFs->makeMappedFile(ifs->control, targetLibPathAbsolute, 0755, + mappedFileParams); + res == 0) { + if (perfLoggingEnabled()) { + auto doneTs = Clock::now(); + LOG(INFO) << "incfs: Mapped " << libName << ": " + << elapsedMcs(startFileTs, doneTs) << "mcs"; + } + continue; + } else { + LOG(WARNING) << "Failed to map file for: '" << targetLibPath << "' errno: " << res + << "; falling back to full extraction"; + } + } + // Create new lib file without signature info incfs::NewFileParams libFileParams = { .size = entry.uncompressed_length, @@ -1671,7 +1709,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ .metadata = {targetLibPath.c_str(), (IncFsSize)targetLibPath.size()}, }; incfs::FileId libFileId = idFromMetadata(targetLibPath); - if (auto res = mIncFs->makeFile(ifs->control, targetLibPathAbsolute, 0777, libFileId, + if (auto res = mIncFs->makeFile(ifs->control, targetLibPathAbsolute, 0755, libFileId, libFileParams)) { LOG(ERROR) << "Failed to make file for: " << targetLibPath << " errno: " << res; // If one lib file fails to be created, abort others as well @@ -1898,25 +1936,33 @@ IncrementalService::LoadingProgress IncrementalService::getLoadingProgress( } IncrementalService::LoadingProgress IncrementalService::getLoadingProgressFromPath( - const IncFsMount& ifs, std::string_view storagePath, bool stopOnFirstIncomplete) const { - ssize_t totalBlocks = 0, filledBlocks = 0; - const auto filePaths = mFs->listFilesRecursive(storagePath); - for (const auto& filePath : filePaths) { + const IncFsMount& ifs, std::string_view storagePath, + const bool stopOnFirstIncomplete) const { + ssize_t totalBlocks = 0, filledBlocks = 0, error = 0; + mFs->listFilesRecursive(storagePath, [&, this](auto filePath) { const auto [filledBlocksCount, totalBlocksCount] = mIncFs->countFilledBlocks(ifs.control, filePath); + if (filledBlocksCount == -EOPNOTSUPP || filledBlocksCount == -ENOTSUP || + filledBlocksCount == -ENOENT) { + // a kind of a file that's not really being loaded, e.g. a mapped range + // an older IncFS used to return ENOENT in this case, so handle it the same way + return true; + } if (filledBlocksCount < 0) { LOG(ERROR) << "getLoadingProgress failed to get filled blocks count for: " << filePath << " errno: " << filledBlocksCount; - return {filledBlocksCount, filledBlocksCount}; + error = filledBlocksCount; + return false; } totalBlocks += totalBlocksCount; filledBlocks += filledBlocksCount; if (stopOnFirstIncomplete && filledBlocks < totalBlocks) { - break; + return false; } - } + return true; + }); - return {filledBlocks, totalBlocks}; + return error ? LoadingProgress{error, error} : LoadingProgress{filledBlocks, totalBlocks}; } bool IncrementalService::updateLoadingProgress( @@ -2306,13 +2352,24 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId; return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch."); } + if (newStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) { + // User-provided status, let's postpone the handling to avoid possible deadlocks. + mService.addTimedJob(*mService.mTimedQueue, id(), Constants::userStatusDelay, + [this, newStatus]() { setCurrentStatus(newStatus); }); + return binder::Status::ok(); + } + setCurrentStatus(newStatus); + return binder::Status::ok(); +} + +void IncrementalService::DataLoaderStub::setCurrentStatus(int newStatus) { int targetStatus, oldStatus; DataLoaderStatusListener listener; { std::unique_lock lock(mMutex); if (mCurrentStatus == newStatus) { - return binder::Status::ok(); + return; } oldStatus = mCurrentStatus; @@ -2332,14 +2389,12 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount << newStatus << " (target " << targetStatus << ")"; if (listener) { - listener->onStatusChanged(mountId, newStatus); + listener->onStatusChanged(id(), newStatus); } fsmStep(); mStatusCondition.notify_all(); - - return binder::Status::ok(); } binder::Status IncrementalService::DataLoaderStub::reportStreamHealth(MountId mountId, diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index 459ed3293510..d8f2c91a971c 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -234,6 +234,8 @@ private: binder::Status onStatusChanged(MountId mount, int newStatus) final; binder::Status reportStreamHealth(MountId mount, int newStatus) final; + void setCurrentStatus(int newStatus); + sp<content::pm::IDataLoader> getDataLoader(); bool bind(); diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp index 659d6503e11c..d61328942e5c 100644 --- a/services/incremental/ServiceWrappers.cpp +++ b/services/incremental/ServiceWrappers.cpp @@ -134,10 +134,11 @@ private: } mLooper; }; -class RealIncFs : public IncFsWrapper { +class RealIncFs final : public IncFsWrapper { public: RealIncFs() = default; ~RealIncFs() final = default; + Features features() const final { return incfs::features(); } void listExistingMounts(const ExistingMountCallback& cb) const final { for (auto mount : incfs::defaultMountRegistry().copyMounts()) { auto binds = mount.binds(); // span() doesn't like rvalue containers, needs to save it. @@ -153,6 +154,10 @@ public: incfs::NewFileParams params) const final { return incfs::makeFile(control, path, mode, id, params); } + ErrorCode makeMappedFile(const Control& control, std::string_view path, int mode, + incfs::NewMappedFileParams params) const final { + return incfs::makeMappedFile(control, path, mode, params); + } ErrorCode makeDir(const Control& control, std::string_view path, int mode) const final { return incfs::makeDir(control, path, mode); } @@ -282,11 +287,10 @@ private: auto it = mJobs.begin(); // Always acquire begin(). We can't use it after unlock as mTimedJobs can change. for (; it != mJobs.end() && it->when <= now; it = mJobs.begin()) { - auto job = std::move(it->what); - mJobs.erase(it); + auto jobNode = mJobs.extract(it); lock.unlock(); - job(); + jobNode.value().what(); lock.lock(); } nextJobTs = it != mJobs.end() ? it->when : kInfinityTs; @@ -308,20 +312,20 @@ private: std::thread mThread; }; -class RealFsWrapper : public FsWrapper { +class RealFsWrapper final : public FsWrapper { public: RealFsWrapper() = default; ~RealFsWrapper() = default; - std::vector<std::string> listFilesRecursive(std::string_view directoryPath) const final { - std::vector<std::string> files; + void listFilesRecursive(std::string_view directoryPath, FileCallback onFile) const final { for (const auto& entry : std::filesystem::recursive_directory_iterator(directoryPath)) { if (!entry.is_regular_file()) { continue; } - files.push_back(entry.path().c_str()); + if (!onFile(entry.path().native())) { + break; + } } - return files; } }; diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h index d60035a9274d..245bb3105be5 100644 --- a/services/incremental/ServiceWrappers.h +++ b/services/incremental/ServiceWrappers.h @@ -16,6 +16,7 @@ #pragma once +#include <android-base/function_ref.h> #include <android-base/unique_fd.h> #include <android/content/pm/DataLoaderParamsParcel.h> #include <android/content/pm/FileSystemControlParcel.h> @@ -77,18 +78,22 @@ public: using ErrorCode = incfs::ErrorCode; using UniqueFd = incfs::UniqueFd; using WaitResult = incfs::WaitResult; + using Features = incfs::Features; - using ExistingMountCallback = - std::function<void(std::string_view root, std::string_view backingDir, - std::span<std::pair<std::string_view, std::string_view>> binds)>; + using ExistingMountCallback = android::base::function_ref< + void(std::string_view root, std::string_view backingDir, + std::span<std::pair<std::string_view, std::string_view>> binds)>; virtual ~IncFsWrapper() = default; + virtual Features features() const = 0; virtual void listExistingMounts(const ExistingMountCallback& cb) const = 0; virtual Control openMount(std::string_view path) const = 0; virtual Control createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs, IncFsFd blocksWritten) const = 0; virtual ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId id, incfs::NewFileParams params) const = 0; + virtual ErrorCode makeMappedFile(const Control& control, std::string_view path, int mode, + incfs::NewMappedFileParams params) const = 0; virtual ErrorCode makeDir(const Control& control, std::string_view path, int mode) const = 0; virtual ErrorCode makeDirs(const Control& control, std::string_view path, int mode) const = 0; virtual incfs::RawMetadata getMetadata(const Control& control, FileId fileid) const = 0; @@ -148,7 +153,9 @@ public: class FsWrapper { public: virtual ~FsWrapper() = default; - virtual std::vector<std::string> listFilesRecursive(std::string_view directoryPath) const = 0; + + using FileCallback = android::base::function_ref<bool(std::string_view)>; + virtual void listFilesRecursive(std::string_view directoryPath, FileCallback onFile) const = 0; }; class ServiceManagerWrapper { diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING new file mode 100644 index 000000000000..d93525600a2d --- /dev/null +++ b/services/incremental/TEST_MAPPING @@ -0,0 +1,32 @@ +{ + "presubmit": [ + { + "name": "CtsContentTestCases", + "options": [ + { + "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest" + }, + { + "include-filter": "android.content.pm.cts.PackageManagerShellCommandIncrementalTest" + }, + { + "include-filter": "android.content.pm.cts.ChecksumsTest" + } + ] + }, + { + "name": "CtsPackageManagerStatsHostTestCases", + "options": [ + { + "include-filter": "com.android.cts.packagemanager.stats.host.PackageInstallerV2StatsTests" + } + ] + }, + { + "name": "CtsIncrementalInstallHostTestCases" + }, + { + "name": "CtsInstalledLoadingProgressHostTests" + } + ] +} diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index ab491efe8583..b00a84fcd003 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -322,6 +322,7 @@ private: class MockIncFs : public IncFsWrapper { public: + MOCK_CONST_METHOD0(features, Features()); MOCK_CONST_METHOD1(listExistingMounts, void(const ExistingMountCallback& cb)); MOCK_CONST_METHOD1(openMount, Control(std::string_view path)); MOCK_CONST_METHOD4(createControl, @@ -330,6 +331,9 @@ public: MOCK_CONST_METHOD5(makeFile, ErrorCode(const Control& control, std::string_view path, int mode, FileId id, NewFileParams params)); + MOCK_CONST_METHOD4(makeMappedFile, + ErrorCode(const Control& control, std::string_view path, int mode, + NewMappedFileParams params)); MOCK_CONST_METHOD3(makeDir, ErrorCode(const Control& control, std::string_view path, int mode)); MOCK_CONST_METHOD3(makeDirs, ErrorCode(const Control& control, std::string_view path, int mode)); @@ -539,16 +543,16 @@ public: class MockFsWrapper : public FsWrapper { public: - MOCK_CONST_METHOD1(listFilesRecursive, std::vector<std::string>(std::string_view)); - void hasNoFile() { - ON_CALL(*this, listFilesRecursive(_)).WillByDefault(Return(std::vector<std::string>())); - } + MOCK_CONST_METHOD2(listFilesRecursive, void(std::string_view, FileCallback)); + void hasNoFile() { ON_CALL(*this, listFilesRecursive(_, _)).WillByDefault(Return()); } void hasFiles() { - ON_CALL(*this, listFilesRecursive(_)) + ON_CALL(*this, listFilesRecursive(_, _)) .WillByDefault(Invoke(this, &MockFsWrapper::fakeFiles)); } - std::vector<std::string> fakeFiles(std::string_view directoryPath) { - return {"base.apk", "split.apk", "lib/a.so"}; + void fakeFiles(std::string_view directoryPath, FileCallback onFile) { + for (auto file : {"base.apk", "split.apk", "lib/a.so"}) { + if (!onFile(file)) break; + } } }; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index dd2dd8150165..a3d335340e9f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -104,7 +104,6 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.ILockSettings; import com.android.server.am.ActivityManagerService; import com.android.server.appbinding.AppBindingService; -import com.android.server.apphibernation.AppHibernationService; import com.android.server.attention.AttentionManagerService; import com.android.server.audio.AudioService; import com.android.server.biometrics.AuthService; @@ -163,6 +162,7 @@ import com.android.server.pm.ShortcutService; import com.android.server.pm.UserManagerService; import com.android.server.pm.dex.SystemServerDexLoadReporter; import com.android.server.pm.verify.domain.DomainVerificationService; +import com.android.server.policy.AppOpsPolicy; import com.android.server.policy.PermissionPolicyService; import com.android.server.policy.PhoneWindowManager; import com.android.server.policy.role.RoleServicePlatformHelperImpl; @@ -1718,14 +1718,9 @@ public final class SystemServer implements Dumpable { startTextToSpeechManagerService(context, t); // System Speech Recognition Service - if (deviceHasConfigString(context, - R.string.config_defaultOnDeviceSpeechRecognitionService)) { - t.traceBegin("StartSpeechRecognitionManagerService"); - mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS); - t.traceEnd(); - } else { - Slog.d(TAG, "System speech recognition is not defined by OEM"); - } + t.traceBegin("StartSpeechRecognitionManagerService"); + mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS); + t.traceEnd(); // App prediction manager service if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) { @@ -1785,7 +1780,7 @@ public final class SystemServer implements Dumpable { t.traceBegin("StartIpSecService"); try { - ipSecService = IpSecService.create(context, networkManagement); + ipSecService = IpSecService.create(context); ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService); } catch (Throwable e) { reportWtf("starting IpSec Service", e); @@ -2150,11 +2145,9 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS); t.traceEnd(); - if (AppHibernationService.isAppHibernationEnabled()) { - t.traceBegin("StartAppHibernationService"); - mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS); - t.traceEnd(); - } + t.traceBegin("StartAppHibernationService"); + mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS); + t.traceEnd(); if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) { t.traceBegin("StartGestureLauncher"); @@ -2664,6 +2657,14 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); + t.traceBegin("RegisterAppOpsPolicy"); + try { + mActivityManagerService.setAppOpsPolicy(new AppOpsPolicy()); + } catch (Throwable e) { + reportWtf("registering app ops policy", e); + } + t.traceEnd(); + // No dependency on Webview preparation in system server. But this should // be completed before allowing 3rd party final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation"; diff --git a/services/midi/Android.bp b/services/midi/Android.bp index 013f23d67cd8..5adcfbaf432e 100644 --- a/services/midi/Android.bp +++ b/services/midi/Android.bp @@ -1,3 +1,12 @@ +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: "services.midi-sources", srcs: ["java/**/*.java"], diff --git a/services/musicrecognition/Android.bp b/services/musicrecognition/Android.bp index fea9efa9dde5..8298dec29884 100644 --- a/services/musicrecognition/Android.bp +++ b/services/musicrecognition/Android.bp @@ -1,3 +1,12 @@ +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: "services.musicsearch-sources", srcs: ["java/**/*.java"], @@ -10,4 +19,4 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [":services.musicsearch-sources"], libs: ["services.core", "app-compat-annotations"], -}
\ No newline at end of file +} diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java index 0cb729d924b4..87b2c84a30f7 100644 --- a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java +++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java @@ -16,6 +16,7 @@ package com.android.server.musicrecognition; +import static android.Manifest.permission.RECORD_AUDIO; import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_AUDIO_UNAVAILABLE; import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_KILLED; import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_UNAVAILABLE; @@ -25,6 +26,7 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; +import android.app.AppOpsManager; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; @@ -45,6 +47,7 @@ import com.android.server.infra.AbstractPerUserSystemService; import java.io.IOException; import java.io.OutputStream; +import java.util.Objects; /** * Handles per-user requests received by @@ -64,11 +67,20 @@ public final class MusicRecognitionManagerPerUserService extends @Nullable @GuardedBy("mLock") private RemoteMusicRecognitionService mRemoteService; + private final AppOpsManager mAppOpsManager; + + private String mAttributionTag; + private String mAttributionMessage; + private ServiceInfo mServiceInfo; MusicRecognitionManagerPerUserService( @NonNull MusicRecognitionManagerService primary, @NonNull Object lock, int userId) { super(primary, lock, userId); + mAppOpsManager = getContext().getSystemService(AppOpsManager.class); + mAttributionMessage = String.format("MusicRecognitionManager.invokedByUid.%s", userId); + mAttributionTag = null; + mServiceInfo = null; } @NonNull @@ -114,6 +126,13 @@ public final class MusicRecognitionManagerPerUserService extends new MusicRecognitionServiceCallback(clientCallback), mMaster.isBindInstantServiceAllowed(), mMaster.verbose); + try { + mServiceInfo = + getContext().getPackageManager().getServiceInfo( + mRemoteService.getComponentName(), 0); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Service was not found.", e); + } } return mRemoteService; @@ -127,12 +146,8 @@ public final class MusicRecognitionManagerPerUserService extends public void beginRecognitionLocked( @NonNull RecognitionRequest recognitionRequest, @NonNull IBinder callback) { - int maxAudioLengthSeconds = Math.min(recognitionRequest.getMaxAudioLengthSeconds(), - MAX_STREAMING_SECONDS); IMusicRecognitionManagerCallback clientCallback = IMusicRecognitionManagerCallback.Stub.asInterface(callback); - AudioRecord audioRecord = createAudioRecord(recognitionRequest, maxAudioLengthSeconds); - mRemoteService = ensureRemoteServiceLocked(clientCallback); if (mRemoteService == null) { try { @@ -158,52 +173,92 @@ public final class MusicRecognitionManagerPerUserService extends ParcelFileDescriptor clientRead = clientPipe.first; mMaster.mExecutorService.execute(() -> { - try (OutputStream fos = - new ParcelFileDescriptor.AutoCloseOutputStream(audioSink)) { - int halfSecondBufferSize = - audioRecord.getBufferSizeInFrames() / maxAudioLengthSeconds; - byte[] byteBuffer = new byte[halfSecondBufferSize]; - int bytesRead = 0; - int totalBytesRead = 0; - int ignoreBytes = - recognitionRequest.getIgnoreBeginningFrames() * BYTES_PER_SAMPLE; - audioRecord.startRecording(); - while (bytesRead >= 0 && totalBytesRead - < audioRecord.getBufferSizeInFrames() * BYTES_PER_SAMPLE - && mRemoteService != null) { - bytesRead = audioRecord.read(byteBuffer, 0, byteBuffer.length); - if (bytesRead > 0) { - totalBytesRead += bytesRead; - // If we are ignoring the first x bytes, update that counter. - if (ignoreBytes > 0) { - ignoreBytes -= bytesRead; - // If we've dipped negative, we've skipped through all ignored bytes - // and then some. Write out the bytes we shouldn't have skipped. - if (ignoreBytes < 0) { - fos.write(byteBuffer, bytesRead + ignoreBytes, -ignoreBytes); - } - } else { - fos.write(byteBuffer); - } - } - } - Slog.i(TAG, String.format("Streamed %s bytes from audio record", totalBytesRead)); - } catch (IOException e) { - Slog.e(TAG, "Audio streaming stopped.", e); - } finally { - audioRecord.release(); - try { - clientCallback.onAudioStreamClosed(); - } catch (RemoteException ignored) { - // Ignored. - } - } + streamAudio(recognitionRequest, clientCallback, audioSink); }); // Send the pipe down to the lookup service while we write to it asynchronously. mRemoteService.writeAudioToPipe(clientRead, recognitionRequest.getAudioFormat()); } /** + * Streams audio based on given request to the given audioSink. Notifies callback of errors. + * + * @param recognitionRequest the recognition request specifying audio parameters. + * @param clientCallback the callback to notify on errors. + * @param audioSink the sink to which to stream audio to. + */ + private void streamAudio(@NonNull RecognitionRequest recognitionRequest, + IMusicRecognitionManagerCallback clientCallback, ParcelFileDescriptor audioSink) { + try { + startRecordAudioOp(); + } catch (SecurityException e) { + // A security exception can occur if the MusicRecognitionService (receiving the audio) + // does not (or does no longer) hold the necessary permissions to record audio. + Slog.e(TAG, "RECORD_AUDIO op not permitted on behalf of " + + mServiceInfo.getComponentName(), e); + try { + clientCallback.onRecognitionFailed( + RECOGNITION_FAILED_AUDIO_UNAVAILABLE); + } catch (RemoteException ignored) { + // Ignored. + } + return; + } + + int maxAudioLengthSeconds = Math.min(recognitionRequest.getMaxAudioLengthSeconds(), + MAX_STREAMING_SECONDS); + AudioRecord audioRecord = createAudioRecord(recognitionRequest, maxAudioLengthSeconds); + try (OutputStream fos = + new ParcelFileDescriptor.AutoCloseOutputStream(audioSink)) { + streamAudio(recognitionRequest, maxAudioLengthSeconds, audioRecord, fos); + } catch (IOException e) { + Slog.e(TAG, "Audio streaming stopped.", e); + } finally { + audioRecord.release(); + finishRecordAudioOp(); + try { + clientCallback.onAudioStreamClosed(); + } catch (RemoteException ignored) { + // Ignored. + } + } + } + + /** Performs the actual streaming from audioRecord into outputStream. **/ + private void streamAudio(@NonNull RecognitionRequest recognitionRequest, + int maxAudioLengthSeconds, AudioRecord audioRecord, OutputStream outputStream) + throws IOException { + int halfSecondBufferSize = + audioRecord.getBufferSizeInFrames() / maxAudioLengthSeconds; + byte[] byteBuffer = new byte[halfSecondBufferSize]; + int bytesRead = 0; + int totalBytesRead = 0; + int ignoreBytes = + recognitionRequest.getIgnoreBeginningFrames() * BYTES_PER_SAMPLE; + audioRecord.startRecording(); + while (bytesRead >= 0 && totalBytesRead + < audioRecord.getBufferSizeInFrames() * BYTES_PER_SAMPLE + && mRemoteService != null) { + bytesRead = audioRecord.read(byteBuffer, 0, byteBuffer.length); + if (bytesRead > 0) { + totalBytesRead += bytesRead; + // If we are ignoring the first x bytes, update that counter. + if (ignoreBytes > 0) { + ignoreBytes -= bytesRead; + // If we've dipped negative, we've skipped through all ignored bytes + // and then some. Write out the bytes we shouldn't have skipped. + if (ignoreBytes < 0) { + outputStream.write(byteBuffer, bytesRead + ignoreBytes, -ignoreBytes); + } + } else { + outputStream.write(byteBuffer); + } + } + } + Slog.i(TAG, + String.format("Streamed %s bytes from audio record", totalBytesRead)); + } + + /** * Callback invoked by {@link android.service.musicrecognition.MusicRecognitionService} to pass * back the music search result. */ @@ -264,6 +319,29 @@ public final class MusicRecognitionManagerPerUserService extends } } + /** + * Tracks that the RECORD_AUDIO operation started (attributes it to the service receiving the + * audio). + */ + private void startRecordAudioOp() { + mAppOpsManager.startProxyOp( + Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)), + mServiceInfo.applicationInfo.uid, + mServiceInfo.packageName, + mAttributionTag, + mAttributionMessage); + } + + + /** Tracks that the RECORD_AUDIO operation finished. */ + private void finishRecordAudioOp() { + mAppOpsManager.finishProxyOp( + Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)), + mServiceInfo.applicationInfo.uid, + mServiceInfo.packageName, + mAttributionTag); + } + /** Establishes an audio stream from the DSP audio source. */ private static AudioRecord createAudioRecord( @NonNull RecognitionRequest recognitionRequest, diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java index 38f43138aee0..e145d3301292 100644 --- a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java +++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java @@ -45,7 +45,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** - * Service which allows a DSP audio event to be securely streamed to a designated {@link + * Service which allows audio to be securely streamed to a designated {@link * MusicRecognitionService}. */ public class MusicRecognitionManagerService extends diff --git a/services/net/Android.bp b/services/net/Android.bp index 7036ccfbd951..b01e42516358 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -1,3 +1,12 @@ +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: "services.net-sources", srcs: ["java/**/*.java"], @@ -43,6 +52,7 @@ java_library { libs: [ "unsupportedappusage", "framework-wifi-util-lib", + "framework-connectivity" ], static_libs: [ // All the classes in netd_aidl_interface must be jarjar so they do not conflict with the diff --git a/services/people/Android.bp b/services/people/Android.bp index 9bdf488d6631..c0188eca1970 100644 --- a/services/people/Android.bp +++ b/services/people/Android.bp @@ -1,3 +1,12 @@ +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_static { name: "services.people", defaults: ["platform_service_defaults"], diff --git a/services/print/Android.bp b/services/print/Android.bp index be5f082eb2b3..5b4349a92692 100644 --- a/services/print/Android.bp +++ b/services/print/Android.bp @@ -1,3 +1,12 @@ +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: "services.print-sources", srcs: ["java/**/*.java"], diff --git a/services/profcollect/Android.bp b/services/profcollect/Android.bp index 7f5f623879f7..2040bb6e544f 100644 --- a/services/profcollect/Android.bp +++ b/services/profcollect/Android.bp @@ -12,6 +12,15 @@ // 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: "services.profcollect-javasources", srcs: ["src/**/*.java"], diff --git a/services/restrictions/Android.bp b/services/restrictions/Android.bp index 60d161de1b2b..62316f14a099 100644 --- a/services/restrictions/Android.bp +++ b/services/restrictions/Android.bp @@ -1,3 +1,12 @@ +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: "services.restrictions-sources", srcs: ["java/**/*.java"], diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp index 1ae2aec90ba3..52eae21f9e66 100644 --- a/services/robotests/Android.bp +++ b/services/robotests/Android.bp @@ -16,6 +16,15 @@ // FrameworksServicesLib app just for Robolectric test target # //################################################################## +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"], +} + android_app { name: "FrameworksServicesLib", platform_apis: true, diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp index 32587accf160..506e1561e8d7 100644 --- a/services/robotests/backup/Android.bp +++ b/services/robotests/backup/Android.bp @@ -15,6 +15,15 @@ //################################################################## // BackupFrameworksServicesLib app just for Robolectric test target # //################################################################## +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"], +} + android_app { name: "BackupFrameworksServicesLib", platform_apis: true, diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java index cbebe6984794..2219d477630e 100644 --- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java +++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java @@ -38,7 +38,6 @@ import static org.testng.Assert.expectThrows; import android.annotation.UserIdInt; import android.app.Application; -import android.app.backup.BackupManager.OperationType; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IBackupObserver; import android.app.backup.IFullBackupRestoreObserver; @@ -874,8 +873,7 @@ public class BackupManagerServiceRoboTest { SecurityException.class, () -> backupManagerService.requestBackup( - mUserTwoId, packages, observer, monitor, 0, - OperationType.BACKUP)); + mUserTwoId, packages, observer, monitor, 0)); } /** @@ -893,11 +891,9 @@ public class BackupManagerServiceRoboTest { IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true); - backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0, - OperationType.BACKUP); + backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0); - verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0, - OperationType.BACKUP); + verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0); } /** Test that the backup service routes methods correctly to the user that requests it. */ @@ -910,11 +906,9 @@ public class BackupManagerServiceRoboTest { IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0, - OperationType.BACKUP); + backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0); - verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0, - OperationType.BACKUP); + verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0); } /** Test that the backup service routes methods correctly to the user that requests it. */ @@ -927,11 +921,9 @@ public class BackupManagerServiceRoboTest { IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0, - OperationType.BACKUP); + backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0); - verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0, - OperationType.BACKUP); + verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0); } /** @@ -1092,11 +1084,9 @@ public class BackupManagerServiceRoboTest { registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT, - OperationType.BACKUP); + backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT); - verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT, - OperationType.BACKUP); + verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); } /** Test that the backup service does not route methods for non-registered users. */ @@ -1106,11 +1096,9 @@ public class BackupManagerServiceRoboTest { registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT, - OperationType.BACKUP); + backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT); - verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT, - OperationType.BACKUP); + verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); } /** Test that the backup service routes methods correctly to the user that requests it. */ diff --git a/services/searchui/Android.bp b/services/searchui/Android.bp index cc632940e4a6..3081a5111ab7 100644 --- a/services/searchui/Android.bp +++ b/services/searchui/Android.bp @@ -1,3 +1,12 @@ +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: "services.searchui-sources", srcs: ["java/**/*.java"], diff --git a/services/smartspace/Android.bp b/services/smartspace/Android.bp index fcf780d4d927..640a88dbaa24 100644 --- a/services/smartspace/Android.bp +++ b/services/smartspace/Android.bp @@ -1,3 +1,12 @@ +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: "services.smartspace-sources", srcs: ["java/**/*.java"], diff --git a/services/startop/Android.bp b/services/startop/Android.bp index 157408f24c5a..c56c463d0168 100644 --- a/services/startop/Android.bp +++ b/services/startop/Android.bp @@ -14,6 +14,17 @@ * 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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], +} + java_library_static { name: "services.startop", defaults: ["platform_service_defaults"], diff --git a/services/systemcaptions/Android.bp b/services/systemcaptions/Android.bp index 54a5a794d086..4cb58cbb2284 100644 --- a/services/systemcaptions/Android.bp +++ b/services/systemcaptions/Android.bp @@ -1,3 +1,12 @@ +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: "services.systemcaptions-sources", srcs: ["java/**/*.java"], diff --git a/services/tests/PackageManagerComponentOverrideTests/Android.bp b/services/tests/PackageManagerComponentOverrideTests/Android.bp index a2668a184fe0..19fdf6056453 100644 --- a/services/tests/PackageManagerComponentOverrideTests/Android.bp +++ b/services/tests/PackageManagerComponentOverrideTests/Android.bp @@ -17,6 +17,15 @@ // NOTE: This test is separate from service tests since it relies on same vs different calling UID, // and this is more representative of a real caller. It also uses Mockito extended, and this // prevents converting the entire services test module. +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"], +} + android_test { name: "PackageManagerComponentOverrideTests", srcs: [ diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp index a941331fcec9..06313da58da1 100644 --- a/services/tests/PackageManagerServiceTests/host/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/Android.bp @@ -12,6 +12,15 @@ // 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_test_host { name: "PackageManagerServiceHostTests", srcs: ["src/**/*.kt"], diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp index b7a0624e02b8..e70a734cf271 100644 --- a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp @@ -12,6 +12,15 @@ // 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: "PackageManagerServiceHostTestsIntentVerifyUtils", srcs: ["src/**/*.kt"], diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp index af0ac77eaadd..7e4f0e72b62d 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test_helper_app { name: "PackageManagerServiceDeviceSideTests", sdk_version: "test_current", diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp index 4a3076e4736a..1cc7ccc01283 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "PackageManagerTestAppStub", manifest: "AndroidManifestVersion1.xml", diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp index e82f57d20fcc..4f3f2eb1b58e 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "PackageManagerTestIntentVerifier", srcs: [ "src/**/*.kt" ], diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp index 7161fdd33516..9f9ed24c63bc 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "PackageManagerTestIntentVerifierTarget1", manifest: "AndroidManifest1.xml", diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp index 58f17f253a24..ebad5afac625 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "PackageManagerTestAppDeclaresStaticLibrary", manifest: "AndroidManifestDeclaresStaticLibrary.xml", diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp index 4aa8abc84392..334e53a3aec7 100644 --- a/services/tests/PackageManagerServiceTests/unit/Android.bp +++ b/services/tests/PackageManagerServiceTests/unit/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "PackageManagerServiceUnitTests", srcs: ["src/**/*.kt"], diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt index deb314764404..9447f390ada0 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt @@ -16,8 +16,9 @@ package com.android.server.pm.test.verify.domain -import android.content.pm.verify.domain.DomainVerificationRequest +import android.content.pm.verify.domain.DomainSet import android.content.pm.verify.domain.DomainVerificationInfo +import android.content.pm.verify.domain.DomainVerificationRequest import android.content.pm.verify.domain.DomainVerificationUserSelection import android.os.Parcel import android.os.Parcelable @@ -27,6 +28,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized import java.util.UUID +import kotlin.random.Random @RunWith(Parameterized::class) class DomainVerificationCoreApiTest { @@ -40,18 +42,25 @@ class DomainVerificationCoreApiTest { assertThat(value).containsExactlyEntriesIn(other) } + private val massiveSet by lazy { + val fragmentOf21 = ".com.example.test.app" + val list = mutableListOf("prefix$fragmentOf21") + var totalSize = 0 + // Slightly overshoot a size of 1MB + while (totalSize < (1024 * 512)) { + val nextValue = "${list.last()}$fragmentOf21" + totalSize += nextValue.length + list += nextValue + } + list.toSet() + } + @JvmStatic - @Parameterized.Parameters + @Parameterized.Parameters(name = "{0}") fun parameters() = arrayOf( Parameter( - initial = { - DomainVerificationRequest( - setOf( - "com.test.pkg.one", - "com.test.pkg.two" - ) - ) - }, + testName = "DomainVerificationRequest", + initial = { DomainVerificationRequest(massiveSet) }, unparcel = { DomainVerificationRequest.CREATOR.createFromParcel(it) }, assertion = { first, second -> assertAll<DomainVerificationRequest, Set<String>>(first, second, @@ -61,15 +70,12 @@ class DomainVerificationCoreApiTest { } ), Parameter( + testName = "DomainVerificationInfo", initial = { DomainVerificationInfo( UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"), "com.test.pkg", - mapOf( - "example.com" to 0, - "example.org" to 1, - "example.new" to 1000 - ) + massiveSet.withIndex().associate { it.value to it.index } ) }, unparcel = { DomainVerificationInfo.CREATOR.createFromParcel(it) }, @@ -86,17 +92,15 @@ class DomainVerificationCoreApiTest { } ), Parameter( + testName = "DomainVerificationUserSelection", initial = { DomainVerificationUserSelection( UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"), "com.test.pkg", UserHandle.of(10), true, - mapOf( - "example.com" to true, - "example.org" to false, - "example.new" to true - ) + massiveSet.withIndex() + .associate { it.value to (it.index % 3) } ) }, unparcel = { DomainVerificationUserSelection.CREATOR.createFromParcel(it) }, @@ -114,21 +118,35 @@ class DomainVerificationCoreApiTest { first, second, { it.isLinkHandlingAllowed }, { it.component4() }, IS_EQUAL_TO ) - assertAll<DomainVerificationUserSelection, Map<String, Boolean>>( - first, second, { it.hostToUserSelectionMap }, + assertAll<DomainVerificationUserSelection, Map<String, Int>>( + first, second, { it.hostToStateMap }, { it.component5() }, IS_MAP_EQUAL_TO ) } + ), + Parameter( + testName = "DomainSet", + initial = { DomainSet(massiveSet) }, + unparcel = { DomainSet.CREATOR.createFromParcel(it) }, + assertion = { first, second -> + assertAll<DomainSet, Set<String>>( + first, second, + { it.domains }, assertion = IS_EQUAL_TO + ) + } ) ) class Parameter<T : Parcelable>( + val testName: String, val initial: () -> T, val unparcel: (Parcel) -> T, private val assertion: (first: T, second: T) -> Unit ) { @Suppress("UNCHECKED_CAST") fun assert(first: Any, second: Any) = assertion(first as T, second as T) + + override fun toString() = testName } private fun <T> assertAll(vararg values: T, block: (value: T, other: T) -> Unit) { @@ -141,11 +159,17 @@ class DomainVerificationCoreApiTest { first: T, second: T, fieldValue: (T) -> V, - componentValue: (T) -> V, + componentValue: ((T) -> V)? = null, assertion: (value: V, other: V) -> Unit ) { - val values = arrayOf<Any>(fieldValue(first), fieldValue(second), - componentValue(first), componentValue(second)) + val values = mutableListOf<Any>(fieldValue(first), fieldValue(second)) + .apply { + componentValue?.let { + add(it(first)) + add(it(second)) + } + } + .toTypedArray() values.indices.drop(1).forEach { @Suppress("UNCHECKED_CAST") assertion(values[0] as V, values[it] as V) diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt index 2d23fb4990bf..89394837655a 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt @@ -244,6 +244,14 @@ class DomainVerificationEnforcerTest { service(Type.LEGACY_QUERENT, "getLegacyUserState") { getLegacyState(it.targetPackageName, it.userId) }, + service(Type.OWNER_QUERENT, "getOwnersForDomain") { + // Re-use package name, since the result itself isn't relevant + getOwnersForDomain(it.targetPackageName) + }, + service(Type.OWNER_QUERENT_USER, "getOwnersForDomainUserId") { + // Re-use package name, since the result itself isn't relevant + getOwnersForDomain(it.targetPackageName, it.userId) + }, ) } @@ -327,6 +335,7 @@ class DomainVerificationEnforcerTest { domainSetId ) ) { + whenever(getName()) { packageName } whenever(getPkg()) { mockPkg(packageName) } whenever(this.domainSetId) { domainSetId } whenever(userState) { @@ -357,6 +366,8 @@ class DomainVerificationEnforcerTest { Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true) Type.LEGACY_QUERENT -> legacyQuerent() Type.LEGACY_SELECTOR -> legacyUserSelector() + Type.OWNER_QUERENT -> ownerQuerent(verifyCrossUser = false) + Type.OWNER_QUERENT_USER -> ownerQuerent(verifyCrossUser = true) }.run { /*exhaust*/ } } @@ -628,6 +639,80 @@ class DomainVerificationEnforcerTest { runTestCases(callingUserId, notCallingUserId, throws = false) } + private fun ownerQuerent(verifyCrossUser: Boolean) { + val allowQueryAll = AtomicBoolean(false) + val allowUserSelection = AtomicBoolean(false) + val allowInteractAcrossUsers = AtomicBoolean(false) + val context: Context = mockThrowOnUnmocked { + initPermission( + allowQueryAll, + android.Manifest.permission.QUERY_ALL_PACKAGES + ) + initPermission( + allowUserSelection, + android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION + ) + initPermission( + allowInteractAcrossUsers, + android.Manifest.permission.INTERACT_ACROSS_USERS + ) + } + val target = params.construct(context) + + fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) { + // Owner querent makes no distinction by UID + val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID + if (throws) { + allUids.forEach { + assertFails { + runMethod(target, it, visible = true, callingUserId, targetUserId) + } + } + } else { + allUids.forEach { + runMethod(target, it, visible = true, callingUserId, targetUserId) + } + } + } + + val callingUserId = 0 + val notCallingUserId = 1 + + runTestCases(callingUserId, callingUserId, throws = true) + if (verifyCrossUser) { + runTestCases(callingUserId, notCallingUserId, throws = true) + } + + allowQueryAll.set(true) + + runTestCases(callingUserId, callingUserId, throws = true) + if (verifyCrossUser) { + runTestCases(callingUserId, notCallingUserId, throws = true) + } + + allowUserSelection.set(true) + + runTestCases(callingUserId, callingUserId, throws = false) + if (verifyCrossUser) { + runTestCases(callingUserId, notCallingUserId, throws = true) + } + + allowQueryAll.set(false) + + runTestCases(callingUserId, callingUserId, throws = true) + if (verifyCrossUser) { + runTestCases(callingUserId, notCallingUserId, throws = true) + } + + allowQueryAll.set(true) + allowInteractAcrossUsers.set(true) + + runTestCases(callingUserId, callingUserId, throws = false) + if (verifyCrossUser) { + runTestCases(callingUserId, notCallingUserId, throws = false) + } + } + private fun Context.initPermission(boolean: AtomicBoolean, permission: String) { whenever(enforcePermission(eq(permission), anyInt(), anyInt(), anyString())) { if (!boolean.get()) { @@ -694,6 +779,12 @@ class DomainVerificationEnforcerTest { LEGACY_QUERENT, // Holding the legacy preferred apps permission - LEGACY_SELECTOR + LEGACY_SELECTOR, + + // Holding user setting permission, but not targeting a package + OWNER_QUERENT, + + // Holding user setting permission, but not targeting a package, but targeting cross user + OWNER_QUERENT_USER, } } diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerUserSelectionOverrideTest.kt new file mode 100644 index 000000000000..48056a2b54d1 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerUserSelectionOverrideTest.kt @@ -0,0 +1,174 @@ +/* + * 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.test.verify.domain + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.content.pm.parsing.component.ParsedActivity +import android.content.pm.parsing.component.ParsedIntentInfo +import android.content.pm.verify.domain.DomainVerificationManager +import android.content.pm.verify.domain.DomainVerificationUserSelection +import android.os.Build +import android.os.PatternMatcher +import android.os.Process +import android.util.ArraySet +import androidx.test.InstrumentationRegistry +import com.android.server.pm.PackageSetting +import com.android.server.pm.parsing.pkg.AndroidPackage +import com.android.server.pm.verify.domain.DomainVerificationService +import com.android.server.testutils.mockThrowOnUnmocked +import com.android.server.testutils.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyLong +import org.mockito.ArgumentMatchers.anyString +import java.util.UUID + +class DomainVerificationManagerUserSelectionOverrideTest { + + companion object { + private const val PKG_ONE = "com.test.one" + private const val PKG_TWO = "com.test.two" + private val UUID_ONE = UUID.fromString("1b041c96-8d37-4932-a858-561bfac5947c") + private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c") + + private val DOMAIN_ONE = + DomainVerificationManagerUserSelectionOverrideTest::class.java.packageName + + private const val STATE_NONE = DomainVerificationUserSelection.DOMAIN_STATE_NONE + private const val STATE_SELECTED = DomainVerificationUserSelection.DOMAIN_STATE_SELECTED + private const val STATE_VERIFIED = DomainVerificationUserSelection.DOMAIN_STATE_VERIFIED + } + + private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE) + private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO) + + fun makeManager(): DomainVerificationManager = + DomainVerificationService(mockThrowOnUnmocked { + // Assume the test has every permission necessary + whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString())) + whenever(checkPermission(anyString(), anyInt(), anyInt())) { + PackageManager.PERMISSION_GRANTED + } + }, mockThrowOnUnmocked { + whenever(linkedApps) { ArraySet<String>() } + }, mockThrowOnUnmocked { + whenever(isChangeEnabled(anyLong(), any())) { true } + }).apply { + setConnection(mockThrowOnUnmocked { + whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false } + whenever(scheduleWriteSettings()) + + // Need to provide an internal UID so some permission checks are ignored + whenever(callingUid) { Process.ROOT_UID } + whenever(callingUserId) { 0 } + whenever(getPackageSettingLocked(PKG_ONE)) { pkg1 } + whenever(getPackageSettingLocked(PKG_TWO)) { pkg2 } + whenever(getPackageLocked(PKG_ONE)) { pkg1.getPkg() } + whenever(getPackageLocked(PKG_TWO)) { pkg2.getPkg() } + }) + addPackage(pkg1) + addPackage(pkg2) + + // Starting state for all tests is to have domain 1 enabled for the first package + setDomainVerificationUserSelection(UUID_ONE, setOf(DOMAIN_ONE), true) + + assertThat(stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_SELECTED) + } + + fun mockPkgSetting(pkgName: String, domainSetId: UUID) = mockThrowOnUnmocked<PackageSetting> { + val pkg = mockThrowOnUnmocked<AndroidPackage> { + whenever(packageName) { pkgName } + whenever(targetSdkVersion) { Build.VERSION_CODES.S } + + val activityList = listOf( + ParsedActivity().apply { + addIntent( + ParsedIntentInfo().apply { + autoVerify = true + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_BROWSABLE) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("http") + addDataScheme("https") + addDataPath("/sub", PatternMatcher.PATTERN_LITERAL) + addDataAuthority(DOMAIN_ONE, null) + } + ) + addIntent( + ParsedIntentInfo().apply { + autoVerify = true + addAction(Intent.ACTION_VIEW) + addCategory(Intent.CATEGORY_BROWSABLE) + addCategory(Intent.CATEGORY_DEFAULT) + addDataScheme("http") + addDataPath("/sub2", PatternMatcher.PATTERN_LITERAL) + addDataAuthority("example2.com", null) + } + ) + }, + ) + + whenever(activities) { activityList } + } + + whenever(getPkg()) { pkg } + whenever(getName()) { pkgName } + whenever(this.domainSetId) { domainSetId } + whenever(getInstantApp(anyInt())) { false } + whenever(firstInstallTime) { 0L } + } + + @Test + fun anotherPackageTakeoverSuccess() { + val manager = makeManager() + + // Attempt override by package 2 + manager.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true) + + // 1 loses approval + assertThat(manager.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_NONE) + + // 2 gains approval + assertThat(manager.stateFor(PKG_TWO, DOMAIN_ONE)).isEqualTo(STATE_SELECTED) + + // 2 is the only owner + assertThat(manager.getOwnersForDomain(DOMAIN_ONE).map { it.packageName }) + .containsExactly(PKG_TWO) + } + + @Test(expected = IllegalArgumentException::class) + fun anotherPackageTakeoverFailure() { + val manager = makeManager() + + // Verify 1 to give it a higher approval level + manager.setDomainVerificationStatus(UUID_ONE, setOf(DOMAIN_ONE), + DomainVerificationManager.STATE_SUCCESS) + assertThat(manager.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_VERIFIED) + assertThat(manager.getOwnersForDomain(DOMAIN_ONE).map { it.packageName }) + .containsExactly(PKG_ONE) + + // Attempt override by package 2 + manager.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true) + } + + private fun DomainVerificationManager.stateFor(pkgName: String, host: String) = + getDomainVerificationUserSelection(pkgName)!!.hostToStateMap[host] +} diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt index a76d8cee582c..439048ce51bb 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt @@ -34,7 +34,7 @@ operator fun DomainVerificationUserSelection.component1() = identifier operator fun DomainVerificationUserSelection.component2() = packageName operator fun DomainVerificationUserSelection.component3() = user operator fun DomainVerificationUserSelection.component4() = isLinkHandlingAllowed -operator fun DomainVerificationUserSelection.component5() = hostToUserSelectionMap +operator fun DomainVerificationUserSelection.component5() = hostToStateMap operator fun DomainVerificationPersistence.ReadResult.component1() = active operator fun DomainVerificationPersistence.ReadResult.component2() = restored diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt index 48518f4693dd..010eacf3f51f 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt @@ -237,6 +237,7 @@ class DomainVerificationSettingsMutationTest { TEST_UUID ) ) { + whenever(getName()) { TEST_PKG } whenever(getPkg()) { mockPkg() } whenever(domainSetId) { TEST_UUID } whenever(userState) { diff --git a/services/tests/inprocesstests/Android.bp b/services/tests/inprocesstests/Android.bp index 6dd059fc919d..7c237ac6befb 100644 --- a/services/tests/inprocesstests/Android.bp +++ b/services/tests/inprocesstests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "FrameworksInProcessTests", srcs: ["src/**/*.java"], diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index e7d56a070182..64b9a63991b4 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -18,6 +18,15 @@ java_defaults { ], } +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"], +} + android_test { name: "FrameworksMockingServicesTests", defaults: ["FrameworkMockingServicesTests-jni-defaults"], diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp index 928065a7ebd9..a32bf2c3b260 100644 --- a/services/tests/mockingservicestests/jni/Android.bp +++ b/services/tests/mockingservicestests/jni/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_library_shared { name: "libactivitymanagermockingservicestestjni", 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 1254df95a1c0..91098813380e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.alarm; import static android.app.AlarmManager.ELAPSED_REALTIME; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE; +import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT; import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; import static android.app.AlarmManager.FLAG_IDLE_UNTIL; import static android.app.AlarmManager.FLAG_STANDALONE; @@ -25,11 +26,14 @@ import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE; import static android.app.AlarmManager.RTC; import static android.app.AlarmManager.RTC_WAKEUP; import static android.app.AlarmManager.WINDOW_EXACT; +import static android.app.AlarmManager.WINDOW_HEURISTIC; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; +import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; @@ -44,7 +48,7 @@ import static com.android.server.alarm.AlarmManagerService.ACTIVE_INDEX; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.CHARGING_STATUS_CHANGED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED; -import static com.android.server.alarm.AlarmManagerService.Constants.ALLOW_WHILE_IDLE_WINDOW; +import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_QUOTA; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_LAZY_BATCHING; @@ -61,6 +65,7 @@ import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE; import static com.android.server.alarm.Constants.TEST_CALLING_UID; 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; @@ -75,20 +80,29 @@ import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; +import android.Manifest; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.app.AlarmManager; +import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.IAlarmCompleteListener; import android.app.IAlarmListener; +import android.app.IAlarmManager; import android.app.PendingIntent; +import android.app.compat.CompatChanges; import android.app.usage.UsageStatsManagerInternal; import android.content.Context; import android.content.Intent; +import android.content.PermissionChecker; import android.os.BatteryManager; +import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.PowerManager; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -102,9 +116,12 @@ import androidx.test.runner.AndroidJUnit4; import com.android.dx.mockito.inline.extended.MockedVoidMethod; import com.android.internal.annotations.GuardedBy; +import com.android.internal.app.IAppOpsCallback; +import com.android.internal.app.IAppOpsService; import com.android.server.AlarmManagerInternal; import com.android.server.AppStateTracker; import com.android.server.AppStateTrackerImpl; +import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.usage.AppStandbyInternal; @@ -125,6 +142,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.LongConsumer; @Presubmit @RunWith(AndroidJUnit4.class) @@ -134,14 +152,21 @@ public class AlarmManagerServiceTest { private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID); private long mAppStandbyWindow; + private long mAllowWhileIdleWindow; private AlarmManagerService mService; private AppStandbyInternal.AppIdleStateChangeListener mAppStandbyListener; private AlarmManagerService.ChargingReceiver mChargingReceiver; + private IAppOpsCallback mIAppOpsCallback; + private IAlarmManager mBinder; @Mock private Context mMockContext; @Mock private IActivityManager mIActivityManager; @Mock + private IAppOpsService mIAppOpsService; + @Mock + private DeviceIdleInternal mDeviceIdleInternal; + @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal; @Mock private AppStandbyInternal mAppStandbyInternal; @@ -280,6 +305,11 @@ public class AlarmManagerServiceTest { // "IllegalStateException: Querying activity state off main thread is not allowed." // when AlarmManager calls DeviceConfig.addOnPropertiesChangedListener(). } + + @Override + IAppOpsService getAppOpsService() { + return mIAppOpsService; + } } @Before @@ -287,15 +317,20 @@ public class AlarmManagerServiceTest { mMockingSession = mockitoSession() .initMocks(this) .spyStatic(ActivityManager.class) + .mockStatic(CompatChanges.class) .spyStatic(DeviceConfig.class) .mockStatic(LocalServices.class) .spyStatic(Looper.class) + .mockStatic(PermissionChecker.class) .mockStatic(Settings.Global.class) .mockStatic(ServiceManager.class) .spyStatic(UserHandle.class) .strictness(Strictness.WARN) .startMocking(); + doReturn(mIActivityManager).when(ActivityManager::getService); + doReturn(mDeviceIdleInternal).when( + () -> LocalServices.getService(DeviceIdleInternal.class)); doReturn(mActivityManagerInternal).when( () -> LocalServices.getService(ActivityManagerInternal.class)); doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class)); @@ -330,6 +365,9 @@ public class AlarmManagerServiceTest { () -> DeviceConfig.getProperties( eq(DeviceConfig.NAMESPACE_ALARM_MANAGER), ArgumentMatchers.<String>any())); + when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn( + mock(AppOpsManager.class)); + mInjector = new Injector(mMockContext); mService = new AlarmManagerService(mMockContext, mInjector); spyOn(mService); @@ -346,6 +384,7 @@ public class AlarmManagerServiceTest { // Other boot phases don't matter mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW; + mAllowWhileIdleWindow = mService.mConstants.ALLOW_WHILE_IDLE_WINDOW; ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> captor = ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class); verify(mAppStandbyInternal).addListener(captor.capture()); @@ -358,6 +397,20 @@ public class AlarmManagerServiceTest { && filter.hasAction(BatteryManager.ACTION_DISCHARGING))); mChargingReceiver = chargingReceiverCaptor.getValue(); + ArgumentCaptor<IBinder> binderCaptor = ArgumentCaptor.forClass(IBinder.class); + verify(() -> ServiceManager.addService(eq(Context.ALARM_SERVICE), binderCaptor.capture(), + anyBoolean(), anyInt())); + mBinder = IAlarmManager.Stub.asInterface(binderCaptor.getValue()); + + ArgumentCaptor<IAppOpsCallback> appOpsCallbackCaptor = ArgumentCaptor.forClass( + IAppOpsCallback.class); + try { + verify(mIAppOpsService).startWatchingMode(eq(AppOpsManager.OP_SCHEDULE_EXACT_ALARM), + isNull(), appOpsCallbackCaptor.capture()); + } catch (RemoteException e) { + // Not expected on a mock. + } + mIAppOpsCallback = appOpsCallbackCaptor.getValue(); setTestableQuotas(); } @@ -389,13 +442,18 @@ public class AlarmManagerServiceTest { private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval, int flags, int callingUid) { + setTestAlarm(type, triggerTime, operation, interval, flags, callingUid, null); + } + + private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval, + int flags, int callingUid, Bundle idleOptions) { mService.setImpl(type, triggerTime, WINDOW_EXACT, interval, operation, null, "test", flags, - null, null, callingUid, TEST_CALLING_PACKAGE); + null, null, callingUid, TEST_CALLING_PACKAGE, idleOptions); } private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) { mService.setImpl(type, triggerTime, WINDOW_EXACT, 0, null, listener, "test", - FLAG_STANDALONE, null, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE); + FLAG_STANDALONE, null, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE, null); } @@ -506,14 +564,27 @@ public class AlarmManagerServiceTest { setDeviceConfigLong(KEY_MIN_INTERVAL, 10); setDeviceConfigLong(KEY_MAX_INTERVAL, 15); setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, 20); + setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, 25); setDeviceConfigLong(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, 30); setDeviceConfigLong(KEY_LISTENER_TIMEOUT, 35); assertEquals(5, mService.mConstants.MIN_FUTURITY); assertEquals(10, mService.mConstants.MIN_INTERVAL); assertEquals(15, mService.mConstants.MAX_INTERVAL); assertEquals(20, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA); + assertEquals(25, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA); assertEquals(30, mService.mConstants.ALLOW_WHILE_IDLE_WHITELIST_DURATION); assertEquals(35, mService.mConstants.LISTENER_TIMEOUT); + + // Test safeguards. + setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, -3); + assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA); + setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_QUOTA, 0); + assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_QUOTA); + + setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, -8); + assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA); + setDeviceConfigInt(KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA, 0); + assertEquals(1, mService.mConstants.ALLOW_WHILE_IDLE_COMPAT_QUOTA); } @Test @@ -559,56 +630,45 @@ public class AlarmManagerServiceTest { assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed()); } - private void testQuotasDeferralOnSet(int standbyBucket) throws Exception { - final int quota = mService.getQuotaForBucketLocked(standbyBucket); - when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), - anyLong())).thenReturn(standbyBucket); + private void testQuotasDeferralOnSet(LongConsumer alarmSetter, int quota, long window) + throws Exception { final long firstTrigger = mNowElapsedTest + 10; for (int i = 0; i < quota; i++) { - setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i, - getNewMockPendingIntent()); + alarmSetter.accept(firstTrigger + i); mNowElapsedTest = mTestTimer.getElapsed(); mTestTimer.expire(); } // This one should get deferred on set - setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota, - getNewMockPendingIntent()); - final long expectedNextTrigger = firstTrigger + mAppStandbyWindow; + alarmSetter.accept(firstTrigger + quota); + final long expectedNextTrigger = firstTrigger + window; assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed()); } - private void testQuotasDeferralOnExpiration(int standbyBucket) throws Exception { - final int quota = mService.getQuotaForBucketLocked(standbyBucket); - when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), - anyLong())).thenReturn(standbyBucket); + private void testQuotasDeferralOnExpiration(LongConsumer alarmSetter, int quota, long window) + throws Exception { final long firstTrigger = mNowElapsedTest + 10; for (int i = 0; i < quota; i++) { - setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i, - getNewMockPendingIntent()); + alarmSetter.accept(firstTrigger + i); } - // This one should get deferred after the latest alarm expires - setTestAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota, - getNewMockPendingIntent()); + // This one should get deferred after the latest alarm expires. + alarmSetter.accept(firstTrigger + quota); for (int i = 0; i < quota; i++) { mNowElapsedTest = mTestTimer.getElapsed(); mTestTimer.expire(); } - final long expectedNextTrigger = firstTrigger + mAppStandbyWindow; + final long expectedNextTrigger = firstTrigger + window; assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed()); } - private void testQuotasNoDeferral(int standbyBucket) throws Exception { - final int quota = mService.getQuotaForBucketLocked(standbyBucket); - when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), - anyLong())).thenReturn(standbyBucket); + private void testQuotasNoDeferral(LongConsumer alarmSetter, int quota, long window) + throws Exception { final long firstTrigger = mNowElapsedTest + 10; for (int i = 0; i < quota; i++) { - setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10 + i, - getNewMockPendingIntent()); + alarmSetter.accept(firstTrigger + i); } // This delivery time maintains the quota invariant. Should not be deferred. - final long expectedNextTrigger = firstTrigger + mAppStandbyWindow + 5; - setTestAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger, getNewMockPendingIntent()); + final long expectedNextTrigger = firstTrigger + window + 5; + alarmSetter.accept(expectedNextTrigger); for (int i = 0; i < quota; i++) { mNowElapsedTest = mTestTimer.getElapsed(); mTestTimer.expire(); @@ -616,64 +676,88 @@ public class AlarmManagerServiceTest { assertEquals("Incorrect next alarm trigger", expectedNextTrigger, mTestTimer.getElapsed()); } + private void testStandbyQuotasDeferralOnSet(int standbyBucket) throws Exception { + final int quota = mService.getQuotaForBucketLocked(standbyBucket); + when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), + anyLong())).thenReturn(standbyBucket); + testQuotasDeferralOnSet(trigger -> setTestAlarm(ELAPSED_REALTIME_WAKEUP, trigger, + getNewMockPendingIntent()), quota, mAppStandbyWindow); + } + + private void testStandbyQuotasDeferralOnExpiration(int standbyBucket) throws Exception { + final int quota = mService.getQuotaForBucketLocked(standbyBucket); + when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), + anyLong())).thenReturn(standbyBucket); + testQuotasDeferralOnExpiration(trigger -> setTestAlarm(ELAPSED_REALTIME_WAKEUP, trigger, + getNewMockPendingIntent()), quota, mAppStandbyWindow); + } + + private void testStandbyQuotasNoDeferral(int standbyBucket) throws Exception { + final int quota = mService.getQuotaForBucketLocked(standbyBucket); + when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), + anyLong())).thenReturn(standbyBucket); + testQuotasNoDeferral(trigger -> setTestAlarm(ELAPSED_REALTIME_WAKEUP, trigger, + getNewMockPendingIntent()), quota, mAppStandbyWindow); + } + @Test public void testActiveQuota_deferredOnSet() throws Exception { - testQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE); + testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_ACTIVE); } @Test public void testActiveQuota_deferredOnExpiration() throws Exception { - testQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE); + testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_ACTIVE); } @Test public void testActiveQuota_notDeferred() throws Exception { - testQuotasNoDeferral(STANDBY_BUCKET_ACTIVE); + testStandbyQuotasNoDeferral(STANDBY_BUCKET_ACTIVE); } @Test public void testWorkingQuota_deferredOnSet() throws Exception { - testQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET); + testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_WORKING_SET); } @Test public void testWorkingQuota_deferredOnExpiration() throws Exception { - testQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET); + testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_WORKING_SET); } @Test public void testWorkingQuota_notDeferred() throws Exception { - testQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET); + testStandbyQuotasNoDeferral(STANDBY_BUCKET_WORKING_SET); } @Test public void testFrequentQuota_deferredOnSet() throws Exception { - testQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT); + testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_FREQUENT); } @Test public void testFrequentQuota_deferredOnExpiration() throws Exception { - testQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT); + testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_FREQUENT); } @Test public void testFrequentQuota_notDeferred() throws Exception { - testQuotasNoDeferral(STANDBY_BUCKET_FREQUENT); + testStandbyQuotasNoDeferral(STANDBY_BUCKET_FREQUENT); } @Test public void testRareQuota_deferredOnSet() throws Exception { - testQuotasDeferralOnSet(STANDBY_BUCKET_RARE); + testStandbyQuotasDeferralOnSet(STANDBY_BUCKET_RARE); } @Test public void testRareQuota_deferredOnExpiration() throws Exception { - testQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE); + testStandbyQuotasDeferralOnExpiration(STANDBY_BUCKET_RARE); } @Test public void testRareQuota_notDeferred() throws Exception { - testQuotasNoDeferral(STANDBY_BUCKET_RARE); + testStandbyQuotasNoDeferral(STANDBY_BUCKET_RARE); } @Test @@ -731,7 +815,7 @@ public class AlarmManagerServiceTest { } @Test - public void testQuotaDowngrade() throws Exception { + public void testStandbyQuotaDowngrade() throws Exception { final int workingQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_WORKING_SET); when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET); @@ -758,7 +842,7 @@ public class AlarmManagerServiceTest { } @Test - public void testQuotaUpgrade() throws Exception { + public void testStandbyQuotaUpgrade() throws Exception { final int frequentQuota = mService.getQuotaForBucketLocked(STANDBY_BUCKET_FREQUENT); when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT); @@ -1308,7 +1392,7 @@ public class AlarmManagerServiceTest { public void allowWhileIdleAlarmsWhileDeviceIdle() throws Exception { doReturn(0).when(mService).fuzzForDuration(anyLong()); - setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + ALLOW_WHILE_IDLE_WINDOW + 1000, + setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + mAllowWhileIdleWindow + 1000, getNewMockPendingIntent()); assertNotNull(mService.mPendingIdleUntil); @@ -1323,7 +1407,7 @@ public class AlarmManagerServiceTest { // This one should get deferred on set. setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota, getNewMockPendingIntent(), false); - final long expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW; + final long expectedNextTrigger = firstTrigger + mAllowWhileIdleWindow; assertEquals("Incorrect trigger when no quota left", expectedNextTrigger, mTestTimer.getElapsed()); @@ -1449,61 +1533,24 @@ public class AlarmManagerServiceTest { when(mAppStateTracker.areAlarmsRestrictedByBatterySaver(TEST_CALLING_UID, TEST_CALLING_PACKAGE)).thenReturn(true); when(mAppStateTracker.isForceAllAppsStandbyEnabled()).thenReturn(true); - final int quota = mService.mConstants.ALLOW_WHILE_IDLE_QUOTA; - long firstTrigger = mNowElapsedTest + 10; - for (int i = 0; i < quota; i++) { - setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i, - getNewMockPendingIntent(), false); - mNowElapsedTest = mTestTimer.getElapsed(); - mTestTimer.expire(); - } - // This one should get deferred on set. - setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota, - getNewMockPendingIntent(), false); - long expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW; - assertEquals("Incorrect trigger when no quota available", expectedNextTrigger, - mTestTimer.getElapsed()); + + testQuotasDeferralOnSet(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger, + getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow); // Refresh the state mService.removeLocked(TEST_CALLING_UID); mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER); - firstTrigger = mNowElapsedTest + 10; - for (int i = 0; i < quota; i++) { - setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i, - getNewMockPendingIntent(), false); - } - // This one should get deferred after the latest alarm expires. - setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + quota, - getNewMockPendingIntent(), false); - for (int i = 0; i < quota; i++) { - mNowElapsedTest = mTestTimer.getElapsed(); - mTestTimer.expire(); - } - expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW; - assertEquals("Incorrect trigger when no quota available", expectedNextTrigger, - mTestTimer.getElapsed()); + testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, + trigger, getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow); // Refresh the state mService.removeLocked(TEST_CALLING_UID); mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER); - firstTrigger = mNowElapsedTest + 10; - for (int i = 0; i < quota; i++) { - setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, firstTrigger + i, - getNewMockPendingIntent(), false); - } - // This delivery time maintains the quota invariant. Should not be deferred. - expectedNextTrigger = firstTrigger + ALLOW_WHILE_IDLE_WINDOW + 5; - setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, expectedNextTrigger, - getNewMockPendingIntent(), false); - for (int i = 0; i < quota; i++) { - mNowElapsedTest = mTestTimer.getElapsed(); - mTestTimer.expire(); - } - assertEquals("Incorrect trigger when no quota available", expectedNextTrigger, - mTestTimer.getElapsed()); + testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger, + getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow); } @Test @@ -1545,6 +1592,259 @@ public class AlarmManagerServiceTest { } @Test + public void canScheduleExactAlarms() throws RemoteException { + doReturn(PermissionChecker.PERMISSION_GRANTED).when( + () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); + assertTrue(mBinder.canScheduleExactAlarms()); + + doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( + () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); + assertFalse(mBinder.canScheduleExactAlarms()); + + doReturn(PermissionChecker.PERMISSION_SOFT_DENIED).when( + () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); + assertFalse(mBinder.canScheduleExactAlarms()); + } + + @Test + public void noPermissionCheckWhenChangeDisabled() throws RemoteException { + doReturn(false).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + // alarm clock + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, 0, + getNewMockPendingIntent(), null, null, null, + mock(AlarmManager.AlarmClockInfo.class)); + + // exact, allow-while-idle + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, + FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null); + + // inexact, allow-while-idle + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0, + FLAG_ALLOW_WHILE_IDLE, getNewMockPendingIntent(), null, null, null, null); + + verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM), never()); + verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); + } + + @Test + public void exactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception { + doReturn(false).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + final PendingIntent alarmPi = getNewMockPendingIntent(); + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, + FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); + + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), + eq(alarmPi), isNull(), isNull(), + eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(), + eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + + final Bundle idleOptions = bundleCaptor.getValue(); + final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType"); + assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type); + } + + @Test + public void inexactAllowWhileIdleBinderCallWhenChangeDisabled() throws Exception { + doReturn(false).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + final PendingIntent alarmPi = getNewMockPendingIntent(); + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_HEURISTIC, 0, + FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); + + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), anyLong(), eq(0L), + eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(), + isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + + final Bundle idleOptions = bundleCaptor.getValue(); + final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType"); + assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type); + } + + @Test + public void alarmClockBinderCallWhenChangeDisabled() throws Exception { + doReturn(false).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + final PendingIntent alarmPi = getNewMockPendingIntent(); + final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class); + mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0, + alarmPi, null, null, null, alarmClock); + + verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), + eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE), + isNull(), eq(alarmClock), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), isNull()); + } + + @Test + public void alarmClockBinderCall() throws RemoteException { + doReturn(true).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + doReturn(PermissionChecker.PERMISSION_GRANTED).when( + () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); + + final PendingIntent alarmPi = getNewMockPendingIntent(); + final AlarmManager.AlarmClockInfo alarmClock = mock(AlarmManager.AlarmClockInfo.class); + mBinder.set(TEST_CALLING_PACKAGE, RTC_WAKEUP, 1234, WINDOW_EXACT, 0, 0, + alarmPi, null, null, null, alarmClock); + + // Correct permission checks are invoked. + verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); + verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); + + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(mService).setImpl(eq(RTC_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), + eq(alarmPi), isNull(), isNull(), eq(FLAG_STANDALONE | FLAG_WAKE_FROM_IDLE), + isNull(), eq(alarmClock), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), + bundleCaptor.capture()); + + final Bundle idleOptions = bundleCaptor.getValue(); + final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType"); + assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type); + } + + @Test + public void exactAllowWhileIdleBinderCallWithPermission() throws RemoteException { + doReturn(true).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + // Permission check is granted by default by the mock. + final PendingIntent alarmPi = getNewMockPendingIntent(); + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, + FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); + + verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); + verify(mDeviceIdleInternal, never()).isAppOnWhitelist(anyInt()); + + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), + eq(alarmPi), isNull(), isNull(), + eq(FLAG_ALLOW_WHILE_IDLE | FLAG_STANDALONE), isNull(), isNull(), + eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + + final Bundle idleOptions = bundleCaptor.getValue(); + final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType"); + assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type); + } + + @Test + public void exactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException { + doReturn(true).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + // If permission is denied, only then allowlist will be checked. + doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( + () -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); + when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); + + final PendingIntent alarmPi = getNewMockPendingIntent(); + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 1234, WINDOW_EXACT, 0, + FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); + + verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM)); + verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid())); + + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(1234L), eq(WINDOW_EXACT), eq(0L), + eq(alarmPi), isNull(), isNull(), + eq(FLAG_ALLOW_WHILE_IDLE_COMPAT | FLAG_STANDALONE), isNull(), isNull(), + eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + + final Bundle idleOptions = bundleCaptor.getValue(); + final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType"); + assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type); + } + + @Test + public void inexactAllowWhileIdleBinderCallWithAllowlist() throws RemoteException { + doReturn(true).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(true); + final PendingIntent alarmPi = getNewMockPendingIntent(); + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0, + FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); + + verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM), never()); + verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid())); + + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L), + eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(), + isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + + final Bundle idleOptions = bundleCaptor.getValue(); + final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType"); + assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, type); + } + + @Test + public void inexactAllowWhileIdleBinderCallWithoutAllowlist() throws RemoteException { + doReturn(true).when( + () -> CompatChanges.isChangeEnabled(eq(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION), + anyString(), any(UserHandle.class))); + + when(mDeviceIdleInternal.isAppOnWhitelist(anyInt())).thenReturn(false); + final PendingIntent alarmPi = getNewMockPendingIntent(); + mBinder.set(TEST_CALLING_PACKAGE, ELAPSED_REALTIME_WAKEUP, 4321, WINDOW_HEURISTIC, 0, + FLAG_ALLOW_WHILE_IDLE, alarmPi, null, null, null, null); + + verify(() -> PermissionChecker.checkCallingOrSelfPermissionForPreflight(mMockContext, + Manifest.permission.SCHEDULE_EXACT_ALARM), never()); + verify(mDeviceIdleInternal).isAppOnWhitelist(UserHandle.getAppId(Process.myUid())); + + final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(mService).setImpl(eq(ELAPSED_REALTIME_WAKEUP), eq(4321L), anyLong(), eq(0L), + eq(alarmPi), isNull(), isNull(), eq(FLAG_ALLOW_WHILE_IDLE_COMPAT), isNull(), + isNull(), eq(Process.myUid()), eq(TEST_CALLING_PACKAGE), bundleCaptor.capture()); + + final Bundle idleOptions = bundleCaptor.getValue(); + final int type = idleOptions.getInt("android:broadcast.temporaryAppWhitelistType"); + assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED, type); + } + + @Test + public void idleOptionsSentOnExpiration() throws Exception { + final long triggerTime = mNowElapsedTest + 5000; + final PendingIntent alarmPi = getNewMockPendingIntent(); + final Bundle idleOptions = new Bundle(); + idleOptions.putChar("TEST_CHAR_KEY", 'x'); + idleOptions.putInt("TEST_INT_KEY", 53); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi, 0, 0, TEST_CALLING_UID, + idleOptions); + + mNowElapsedTest = mTestTimer.getElapsed(); + mTestTimer.expire(); + + verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class), + any(), any(Handler.class), isNull(), eq(idleOptions)); + } + + @Test public void alarmStoreMigration() { setDeviceConfigBoolean(KEY_LAZY_BATCHING, false); final int numAlarms = 10; diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java index 42fa3d480046..12894ae4f75e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java @@ -69,12 +69,12 @@ public class AlarmStoreTest { mock(PendingIntent.class)); return new Alarm(ELAPSED_REALTIME_WAKEUP, whenElapsed, whenElapsed, 0, 0, mock(PendingIntent.class), null, null, null, 0, info, TEST_CALLING_UID, - TEST_CALLING_PACKAGE); + TEST_CALLING_PACKAGE, null); } private static Alarm createAlarm(int type, long whenElapsed, long windowLength, int flags) { return new Alarm(type, whenElapsed, whenElapsed, windowLength, 0, mock(PendingIntent.class), - null, null, null, flags, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE); + null, null, null, flags, null, TEST_CALLING_UID, TEST_CALLING_PACKAGE, null); } private void addAlarmsToStore(Alarm... alarms) { diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java index e80f0655e641..b64528c2f4d4 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java @@ -17,10 +17,17 @@ package com.android.server.alarm; import static android.app.AlarmManager.ELAPSED_REALTIME; +import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE; +import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_COMPAT; +import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; +import static android.app.AlarmManager.FLAG_STANDALONE; +import static android.app.AlarmManager.FLAG_WAKE_FROM_IDLE; +import static android.app.AlarmManager.RTC_WAKEUP; import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX; import static com.android.server.alarm.Alarm.NUM_POLICIES; import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX; +import static com.android.server.alarm.AlarmManagerService.isExemptFromAppStandby; import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE; import static com.android.server.alarm.Constants.TEST_CALLING_UID; @@ -28,7 +35,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import android.app.AlarmManager; import android.app.PendingIntent; import android.platform.test.annotations.Presubmit; @@ -43,15 +52,29 @@ import java.util.Random; @RunWith(AndroidJUnit4.class) public class AlarmTest { - private Alarm createDefaultAlarm(long requestedElapsed, long windowLength) { + private Alarm createDefaultAlarm(long requestedElapsed, long windowLength, int flags) { return new Alarm(ELAPSED_REALTIME, 0, requestedElapsed, windowLength, 0, - mock(PendingIntent.class), null, null, null, 0, null, TEST_CALLING_UID, - TEST_CALLING_PACKAGE); + createAlarmSender(), null, null, null, flags, null, TEST_CALLING_UID, + TEST_CALLING_PACKAGE, null); + } + + private Alarm createAlarmClock(long requestedRtc) { + final AlarmManager.AlarmClockInfo info = mock(AlarmManager.AlarmClockInfo.class); + return new Alarm(RTC_WAKEUP, requestedRtc, requestedRtc, 0, 0, createAlarmSender(), + null, null, null, FLAG_WAKE_FROM_IDLE | FLAG_STANDALONE, info, TEST_CALLING_UID, + TEST_CALLING_PACKAGE, null); + } + + private PendingIntent createAlarmSender() { + final PendingIntent alarmPi = mock(PendingIntent.class); + when(alarmPi.getCreatorPackage()).thenReturn(TEST_CALLING_PACKAGE); + when(alarmPi.getCreatorUid()).thenReturn(TEST_CALLING_UID); + return alarmPi; } @Test public void initSetsOnlyRequesterPolicy() { - final Alarm a = createDefaultAlarm(4567, 2); + final Alarm a = createDefaultAlarm(4567, 2, 0); for (int i = 0; i < NUM_POLICIES; i++) { if (i == REQUESTER_POLICY_INDEX) { @@ -86,7 +109,7 @@ public class AlarmTest { @Test public void whenElapsed() { - final Alarm a = createDefaultAlarm(0, 0); + final Alarm a = createDefaultAlarm(0, 0, 0); final long[][] uniqueData = generatePolicyTestMatrix(NUM_POLICIES); for (int i = 0; i < NUM_POLICIES; i++) { @@ -104,7 +127,7 @@ public class AlarmTest { @Test public void maxWhenElapsed() { - final Alarm a = createDefaultAlarm(10, 12); + final Alarm a = createDefaultAlarm(10, 12, 0); assertEquals(22, a.getMaxWhenElapsed()); a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 15); @@ -128,7 +151,7 @@ public class AlarmTest { @Test public void setPolicyElapsedExact() { - final Alarm exactAlarm = createDefaultAlarm(10, 0); + final Alarm exactAlarm = createDefaultAlarm(10, 0, 0); assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4)); assertTrue(exactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10)); @@ -143,7 +166,7 @@ public class AlarmTest { @Test public void setPolicyElapsedInexact() { - final Alarm inexactAlarm = createDefaultAlarm(10, 5); + final Alarm inexactAlarm = createDefaultAlarm(10, 5, 0); assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4)); assertTrue(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10)); @@ -154,4 +177,20 @@ public class AlarmTest { assertFalse(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 8)); } + + @Test + public void isExemptFromStandby() { + final long anything = 35412; // Arbitrary number, doesn't matter for this test. + + assertFalse("Basic alarm exempt", isExemptFromAppStandby( + createDefaultAlarm(anything, anything, 0))); + assertFalse("FLAG_ALLOW_WHILE_IDLE_COMPAT exempt", isExemptFromAppStandby( + createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_COMPAT))); + + assertTrue("ALLOW_WHILE_IDLE not exempt", isExemptFromAppStandby( + createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE))); + assertTrue("ALLOW_WHILE_IDLE_UNRESTRICTED not exempt", isExemptFromAppStandby( + createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED))); + assertTrue("Alarm clock not exempt", isExemptFromAppStandby(createAlarmClock(anything))); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java index 5bb6a42b2604..0e795a938d48 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java @@ -45,7 +45,7 @@ public class BackgroundRestrictedAlarmsTest { } uidAlarms.add(new Alarm( removeIt ? RTC : RTC_WAKEUP, - 0, 0, 0, 0, null, null, null, null, 0, null, uid, name)); + 0, 0, 0, 0, null, null, null, null, 0, null, uid, name, null)); return all; } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java index 56d30ccdf59f..20a58426f1eb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java @@ -139,7 +139,8 @@ public final class CachedAppOptimizerTest { app.info.uid = packageUid; // Exact value does not mater, it can be any state for which compaction is allowed. app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE); - app.mState.setSetAdj(905); + app.mState.setSetAdj(899); + app.mState.setCurAdj(940); return app; } @@ -164,8 +165,6 @@ public final class CachedAppOptimizerTest { CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE); assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo( CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE); - assertThat(mCachedAppOptimizerUnderTest.mFullAnonRssThrottleKb).isEqualTo( - CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB); assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleBFGS).isEqualTo( CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5); assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( @@ -176,6 +175,11 @@ public final class CachedAppOptimizerTest { CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB); assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo( CachedAppOptimizer.DEFAULT_USE_FREEZER); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ); + Set<Integer> expected = new HashSet<>(); for (String s : TextUtils.split( @@ -231,6 +235,14 @@ public final class CachedAppOptimizerTest { Long.toString( CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1), false); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, + Long.toString( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 10), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, + Long.toString( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 10), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false); assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo( CachedAppOptimizer.DEFAULT_USE_FREEZER); @@ -261,6 +273,12 @@ public final class CachedAppOptimizerTest { CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1); assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1); + assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 10); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 10); assertThat(mCachedAppOptimizerUnderTest.mCompactStatsdSampleRate).isEqualTo( CachedAppOptimizer.DEFAULT_STATSD_SAMPLE_RATE + 0.1f); assertThat(mCachedAppOptimizerUnderTest.mFreezerStatsdSampleRate).isEqualTo( @@ -437,7 +455,7 @@ public final class CachedAppOptimizerTest { mCachedAppOptimizerUnderTest.init(); // When we override new reasonable throttle values after init... - mCountDown = new CountDownLatch(6); + mCountDown = new CountDownLatch(8); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, CachedAppOptimizer.KEY_COMPACT_THROTTLE_1, Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1), false); @@ -456,7 +474,13 @@ public final class CachedAppOptimizerTest { DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, CachedAppOptimizer.KEY_COMPACT_THROTTLE_6, Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, + Long.toString(CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 1), false); + assertThat(mCountDown.await(7, TimeUnit.SECONDS)).isTrue(); // Then those flags values are reflected in the compactor. assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo( @@ -471,6 +495,10 @@ public final class CachedAppOptimizerTest { CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_5 + 1); assertThat(mCachedAppOptimizerUnderTest.mCompactThrottlePersistent).isEqualTo( CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_6 + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ + 1); + assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo( + CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 1); } @Test @@ -902,7 +930,6 @@ public final class CachedAppOptimizerTest { valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get( pid).getRssAfterCompaction(); assertThat(valuesAfter).isEqualTo(rssAfter3); - } @Test @@ -954,6 +981,54 @@ public final class CachedAppOptimizerTest { assertThat(valuesAfter).isEqualTo(rssAboveThresholdAfter); } + @Test + public void processWithOomAdjTooSmall_notFullCompacted() throws Exception { + // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set Min and + // Max OOM_Adj throttles. + mCachedAppOptimizerUnderTest.init(); + setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true); + setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, Long.toString(920), true); + setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, Long.toString(950), true); + initActivityManagerService(); + + // Simulate RSS memory for which compaction should occur. + long[] rssBefore = + new long[]{/*Total RSS*/ 15000, /*File RSS*/ 15000, /*Anon RSS*/ 15000, + /*Swap*/ 10000}; + long[] rssAfter = + new long[]{/*Total RSS*/ 8000, /*File RSS*/ 9000, /*Anon RSS*/ 6000, /*Swap*/5000}; + // Process that passes properties. + int pid = 1; + ProcessRecord processRecord = + makeProcessRecord(pid, 2, 3, "p1", "app1"); + mProcessDependencies.setRss(rssBefore); + mProcessDependencies.setRssAfterCompaction(rssAfter); + + // Compaction should occur if (setAdj < min for process || setAdj > max for process) && + // (MIN < curAdj < MAX) + // GIVEN OomAdj score below threshold. + processRecord.mState.setSetAdj(899); + processRecord.mState.setCurAdj(970); + // WHEN we try to run compaction + mCachedAppOptimizerUnderTest.compactAppFull(processRecord); + waitForHandler(); + // THEN process IS NOT compacted. + assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull(); + + // GIVEN (setAdj < MIN || setAdj > MAX) && (MIN < curAdj < MAX) + processRecord.mState.setSetAdj(910); + processRecord.mState.setCurAdj(930); + // WHEN we try to run compaction + mCachedAppOptimizerUnderTest.compactAppFull(processRecord); + waitForHandler(); + // THEN process IS compacted. + assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull(); + long[] valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats + .get(pid) + .getRssAfterCompaction(); + assertThat(valuesAfter).isEqualTo(rssAfter); + } + private void setFlag(String key, String value, boolean defaultValue) throws Exception { mCountDown = new CountDownLatch(1); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 27825a461b40..1c45203bb3c9 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -46,6 +46,7 @@ import static com.android.server.am.ProcessList.HEAVY_WEIGHT_APP_ADJ; import static com.android.server.am.ProcessList.HOME_APP_ADJ; import static com.android.server.am.ProcessList.PERCEPTIBLE_APP_ADJ; import static com.android.server.am.ProcessList.PERCEPTIBLE_LOW_APP_ADJ; +import static com.android.server.am.ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ; import static com.android.server.am.ProcessList.PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ; import static com.android.server.am.ProcessList.PERSISTENT_PROC_ADJ; import static com.android.server.am.ProcessList.PERSISTENT_SERVICE_ADJ; @@ -64,6 +65,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.AdditionalAnswers.answer; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.doAnswer; @@ -73,6 +75,8 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import android.app.ActivityManager; +import android.app.AppOpsManager; import android.app.IApplicationThread; import android.app.IServiceConnection; import android.content.ComponentName; @@ -178,11 +182,20 @@ public class MockingOomAdjusterTests { setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler); setFieldValue(ActivityManagerService.class, sService, "mProcLock", new ActivityManagerProcLock()); + setFieldValue(ActivityManagerService.class, sService, "mServices", + spy(new ActiveServices(sService))); + setFieldValue(ActivityManagerService.class, sService, "mInternal", + mock(ActivityManagerService.LocalService.class)); + setFieldValue(ActivityManagerService.class, sService, "mBatteryStatsService", + mock(BatteryStatsService.class)); + doReturn(mock(AppOpsManager.class)).when(sService).getAppOpsManager(); + doCallRealMethod().when(sService).enqueueOomAdjTargetLocked(any(ProcessRecord.class)); + doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(any(String.class)); setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object()); doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr) .enqueueProcessChangeItemLocked(anyInt(), anyInt()); sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, - mock(ActiveUids.class)); + new ActiveUids(sService, false)); sService.mOomAdjuster.mAdjSeq = 10000; sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE); } @@ -865,6 +878,39 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test + public void testUpdateOomAdj_DoOne_Service_MediumPerceptible() { + { + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + bindService(app, client, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); + client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); + + assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ, app.mState.getSetAdj()); + } + + { + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + WindowProcessController wpc = client.getWindowProcessController(); + doReturn(true).when(wpc).isHeavyWeightProcess(); + bindService(app, client, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); + client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); + doReturn(false).when(wpc).isHeavyWeightProcess(); + + assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ, app.mState.getSetAdj()); + } + } + + @SuppressWarnings("GuardedBy") + @Test public void testUpdateOomAdj_DoOne_Service_Other() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); @@ -1315,6 +1361,115 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test + public void testUpdateOomAdj_UidIdle_StopService() { + final ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + final ProcessRecord client1 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, + MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); + final ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP3_UID, + MOCKAPP4_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); + final ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID, + MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); + final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, sService); + final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, sService); + final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, sService); + final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, sService); + app1.setUidRecord(app1UidRecord); + app2.setUidRecord(app2UidRecord); + app3.setUidRecord(app3UidRecord); + client1.setUidRecord(clientUidRecord); + client2.setUidRecord(clientUidRecord); + + client1.mServices.setHasForegroundServices(true, 0); + client2.mState.setForcingToImportant(new Object()); + ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP(); + lru.clear(); + lru.add(app1); + lru.add(app2); + lru.add(app3); + lru.add(client1); + lru.add(client2); + sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + + final ComponentName cn1 = ComponentName.unflattenFromString( + MOCKAPP_PACKAGENAME + "/.TestService"); + final ServiceRecord s1 = bindService(app1, client1, null, 0, mock(IBinder.class)); + setFieldValue(ServiceRecord.class, s1, "name", cn1); + s1.startRequested = true; + + final ComponentName cn2 = ComponentName.unflattenFromString( + MOCKAPP2_PACKAGENAME + "/.TestService"); + final ServiceRecord s2 = bindService(app2, client2, null, 0, mock(IBinder.class)); + setFieldValue(ServiceRecord.class, s2, "name", cn2); + s2.startRequested = true; + + final ComponentName cn3 = ComponentName.unflattenFromString( + MOCKAPP5_PACKAGENAME + "/.TestService"); + final ServiceRecord s3 = bindService(app3, client1, null, 0, mock(IBinder.class)); + setFieldValue(ServiceRecord.class, s3, "name", cn3); + s3.startRequested = true; + + final ComponentName cn4 = ComponentName.unflattenFromString( + MOCKAPP3_PACKAGENAME + "/.TestService"); + final ServiceRecord c2s = makeServiceRecord(client2); + setFieldValue(ServiceRecord.class, c2s, "name", cn4); + c2s.startRequested = true; + + try { + sService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord); + sService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord); + sService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord); + sService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord); + + setServiceMap(s1, MOCKAPP_UID, cn1); + setServiceMap(s2, MOCKAPP2_UID, cn2); + setServiceMap(s3, MOCKAPP5_UID, cn3); + setServiceMap(c2s, MOCKAPP3_UID, cn4); + app2UidRecord.setIdle(false); + sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE); + + assertProcStates(app1, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + SCHED_GROUP_DEFAULT); + assertProcStates(app3, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + SCHED_GROUP_DEFAULT); + assertProcStates(client1, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + SCHED_GROUP_DEFAULT); + assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app2.mState.getSetProcState()); + assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, client2.mState.getSetProcState()); + + client1.mServices.setHasForegroundServices(false, 0); + client2.mState.setForcingToImportant(null); + app1UidRecord.reset(); + app2UidRecord.reset(); + app3UidRecord.reset(); + clientUidRecord.reset(); + app1UidRecord.setIdle(true); + app2UidRecord.setIdle(true); + app3UidRecord.setIdle(true); + clientUidRecord.setIdle(true); + doReturn(ActivityManager.APP_START_MODE_DELAYED).when(sService) + .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(), + anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); + doNothing().when(sService.mServices) + .scheduleServiceTimeoutLocked(any(ProcessRecord.class)); + sService.mOomAdjuster.updateOomAdjLocked(client1, OomAdjuster.OOM_ADJ_REASON_NONE); + + assertEquals(PROCESS_STATE_CACHED_EMPTY, client1.mState.getSetProcState()); + assertEquals(PROCESS_STATE_SERVICE, app1.mState.getSetProcState()); + assertEquals(PROCESS_STATE_SERVICE, client2.mState.getSetProcState()); + } finally { + doCallRealMethod().when(sService) + .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(), + anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); + sService.mServices.mServiceMap.clear(); + sService.mOomAdjuster.mActiveUids.clear(); + } + } + + @SuppressWarnings("GuardedBy") + @Test public void testUpdateOomAdj_DoAll_Unbound() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); @@ -1815,15 +1970,42 @@ public class MockingOomAdjusterTests { return app; } + private ServiceRecord makeServiceRecord(ProcessRecord app) { + final ServiceRecord record = mock(ServiceRecord.class); + record.app = app; + setFieldValue(ServiceRecord.class, record, "connections", + new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()); + doCallRealMethod().when(record).getConnections(); + setFieldValue(ServiceRecord.class, record, "packageName", app.info.packageName); + app.mServices.startService(record); + record.appInfo = app.info; + setFieldValue(ServiceRecord.class, record, "bindings", new ArrayMap<>()); + setFieldValue(ServiceRecord.class, record, "pendingStarts", new ArrayList<>()); + return record; + } + + private void setServiceMap(ServiceRecord s, int uid, ComponentName cn) { + ActiveServices.ServiceMap serviceMap = sService.mServices.mServiceMap.get( + UserHandle.getUserId(uid)); + if (serviceMap == null) { + serviceMap = mock(ActiveServices.ServiceMap.class); + setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mServicesByInstanceName", + new ArrayMap<>()); + setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mActiveForegroundApps", + new ArrayMap<>()); + setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mServicesByIntent", + new ArrayMap<>()); + setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mDelayedStartList", + new ArrayList<>()); + sService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap); + } + serviceMap.mServicesByInstanceName.put(cn, s); + } + private ServiceRecord bindService(ProcessRecord service, ProcessRecord client, ServiceRecord record, int bindFlags, IBinder binder) { if (record == null) { - record = mock(ServiceRecord.class); - record.app = service; - setFieldValue(ServiceRecord.class, record, "connections", - new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()); - service.mServices.startService(record); - doCallRealMethod().when(record).getConnections(); + record = makeServiceRecord(service); } AppBindRecord binding = new AppBindRecord(record, null, client); ConnectionRecord cr = spy(new ConnectionRecord(binding, diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index ca534927bd66..18184b0a82af 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -25,11 +25,16 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; +import android.content.res.TypedArray; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -42,6 +47,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.internal.R; import com.android.server.LocalServices; import com.android.server.display.LocalDisplayAdapter.BacklightAdapter; import com.android.server.lights.LightsManager; @@ -94,11 +100,16 @@ public class LocalDisplayAdapterTest { private Injector mInjector; + @Mock + private LocalDisplayAdapter.SurfaceControlProxy mSurfaceControlProxy; + private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f }; + private static final int[] BACKLIGHT_RANGE = { 1, 255 }; + private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f }; + @Before public void setUp() throws Exception { mMockitoSession = mockitoSession() .initMocks(this) - .mockStatic(SurfaceControl.class) .strictness(Strictness.LENIENT) .startMocking(); mHandler = new Handler(Looper.getMainLooper()); @@ -110,6 +121,18 @@ public class LocalDisplayAdapterTest { mListener, mInjector); spyOn(mAdapter); doReturn(mMockedContext).when(mAdapter).getOverlayContext(); + + TypedArray mockNitsRange = createFloatTypedArray(DISPLAY_RANGE_NITS); + when(mMockedResources.obtainTypedArray(R.array.config_screenBrightnessNits)) + .thenReturn(mockNitsRange); + when(mMockedResources.getIntArray(R.array.config_screenBrightnessBacklight)) + .thenReturn(BACKLIGHT_RANGE); + when(mMockedResources.getFloat(com.android.internal.R.dimen + .config_screenBrightnessSettingMinimumFloat)) + .thenReturn(BACKLIGHT_RANGE_ZERO_TO_ONE[0]); + when(mMockedResources.getFloat(com.android.internal.R.dimen + .config_screenBrightnessSettingMaximumFloat)) + .thenReturn(BACKLIGHT_RANGE_ZERO_TO_ONE[1]); } @After @@ -223,10 +246,11 @@ public class LocalDisplayAdapterTest { for (int i = 0; i < wrappedModes.length; i++) { modes[i] = wrappedModes[i].mode; } - display.modes = modes; + display.dynamicInfo.supportedDisplayModes = modes; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertTrue(mListener.traversalRequested); assertThat(mListener.changedDisplays.size()).isGreaterThan(0); // Verify the supported modes are updated accordingly. @@ -252,53 +276,53 @@ public class LocalDisplayAdapterTest { testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{24f, 50f}), + createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[]{24f, 50f}), new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{24f, 60f}), + createFakeDisplayMode(1, 1920, 1080, 50f, 0), new float[]{24f, 60f}), new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 24f, 0), new float[]{50f, 60f}), + createFakeDisplayMode(2, 1920, 1080, 24f, 0), new float[]{50f, 60f}), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 60f, 0), new float[]{24f, 50f}), + createFakeDisplayMode(3, 3840, 2160, 60f, 0), new float[]{24f, 50f}), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 50f, 0), new float[]{24f, 60f}), + createFakeDisplayMode(4, 3840, 2160, 50f, 0), new float[]{24f, 60f}), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 24f, 0), new float[]{50f, 60f}), + createFakeDisplayMode(5, 3840, 2160, 24f, 0), new float[]{50f, 60f}), }); testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{50f}), + createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[]{50f}), new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{60f}), + createFakeDisplayMode(1, 1920, 1080, 50f, 0), new float[]{60f}), new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 24f, 1), new float[0]), + createFakeDisplayMode(2, 1920, 1080, 24f, 1), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 60f, 2), new float[0]), + createFakeDisplayMode(3, 3840, 2160, 60f, 2), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 50f, 3), new float[]{24f}), + createFakeDisplayMode(4, 3840, 2160, 50f, 3), new float[]{24f}), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 24f, 3), new float[]{50f}), + createFakeDisplayMode(5, 3840, 2160, 24f, 3), new float[]{50f}), }); testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 60f, 0), new float[0]), + createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 50f, 1), new float[0]), + createFakeDisplayMode(1, 1920, 1080, 50f, 1), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 24f, 2), new float[0]), + createFakeDisplayMode(2, 1920, 1080, 24f, 2), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 60f, 3), new float[0]), + createFakeDisplayMode(3, 3840, 2160, 60f, 3), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 50f, 4), new float[0]), + createFakeDisplayMode(4, 3840, 2160, 50f, 4), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 24f, 5), new float[0]), + createFakeDisplayMode(5, 3840, 2160, 24f, 5), new float[0]), }); } @Test public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception { - SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(1920, 1080, 60f); + SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(0, 1920, 1080, 60f); SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{displayMode}; FakeDisplay display = new FakeDisplay(PORT_A, modes, 0); @@ -325,18 +349,15 @@ public class LocalDisplayAdapterTest { displayMode.refreshRate)).isTrue(); // Change the display - SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(3840, 2160, - 60f); + SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(1, 3840, 2160, 60f); modes = new SurfaceControl.DisplayMode[]{displayMode, addedDisplayInfo}; - display.modes = modes; - display.activeMode = 1; + display.dynamicInfo.supportedDisplayModes = modes; + display.dynamicInfo.activeDisplayModeId = 1; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); - assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1); - assertThat(SurfaceControl.getDisplayModes(display.token).length).isEqualTo(2); - + assertTrue(mListener.traversalRequested); assertThat(mListener.addedDisplays.size()).isEqualTo(1); assertThat(mListener.changedDisplays.size()).isEqualTo(1); @@ -360,8 +381,8 @@ public class LocalDisplayAdapterTest { @Test public void testAfterDisplayChange_ActiveModeIsUpdated() throws Exception { SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{ - createFakeDisplayMode(1920, 1080, 60f), - createFakeDisplayMode(1920, 1080, 50f) + createFakeDisplayMode(0, 1920, 1080, 60f), + createFakeDisplayMode(1, 1920, 1080, 50f) }; FakeDisplay display = new FakeDisplay(PORT_A, modes, /* activeMode */ 0); setUpDisplay(display); @@ -379,13 +400,12 @@ public class LocalDisplayAdapterTest { assertThat(activeMode.matches(1920, 1080, 60f)).isTrue(); // Change the display - display.activeMode = 1; + display.dynamicInfo.activeDisplayModeId = 1; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); - assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1); - + assertTrue(mListener.traversalRequested); assertThat(mListener.addedDisplays.size()).isEqualTo(1); assertThat(mListener.changedDisplays.size()).isEqualTo(1); @@ -402,7 +422,7 @@ public class LocalDisplayAdapterTest { FakeDisplay display = new FakeDisplay(PORT_A); Display.HdrCapabilities initialHdrCapabilities = new Display.HdrCapabilities(new int[0], 1000, 1000, 0); - display.hdrCapabilities = initialHdrCapabilities; + display.dynamicInfo.hdrCapabilities = initialHdrCapabilities; setUpDisplay(display); updateAvailableDisplays(); mAdapter.registerLocked(); @@ -419,11 +439,12 @@ public class LocalDisplayAdapterTest { // Change the display Display.HdrCapabilities changedHdrCapabilities = new Display.HdrCapabilities( new int[Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS], 1000, 1000, 0); - display.hdrCapabilities = changedHdrCapabilities; + display.dynamicInfo.hdrCapabilities = changedHdrCapabilities; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertTrue(mListener.traversalRequested); assertThat(mListener.addedDisplays.size()).isEqualTo(1); assertThat(mListener.changedDisplays.size()).isEqualTo(1); @@ -435,10 +456,78 @@ public class LocalDisplayAdapterTest { } @Test + public void testAfterDisplayChange_AllmSupportIsUpdated() throws Exception { + FakeDisplay display = new FakeDisplay(PORT_A); + display.dynamicInfo.autoLowLatencyModeSupported = true; + setUpDisplay(display); + updateAvailableDisplays(); + mAdapter.registerLocked(); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + assertThat(mListener.addedDisplays.size()).isEqualTo(1); + assertThat(mListener.changedDisplays).isEmpty(); + + DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) + .getDisplayDeviceInfoLocked(); + + assertThat(displayDeviceInfo.allmSupported).isTrue(); + + // Change the display + display.dynamicInfo.autoLowLatencyModeSupported = false; + setUpDisplay(display); + mInjector.getTransmitter().sendHotplug(display, /* connected */ true); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + assertTrue(mListener.traversalRequested); + assertThat(mListener.addedDisplays.size()).isEqualTo(1); + assertThat(mListener.changedDisplays.size()).isEqualTo(1); + + DisplayDevice displayDevice = mListener.changedDisplays.get(0); + displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); + displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); + + assertThat(displayDeviceInfo.allmSupported).isFalse(); + } + + @Test + public void testAfterDisplayChange_GameContentTypeSupportIsUpdated() throws Exception { + FakeDisplay display = new FakeDisplay(PORT_A); + display.dynamicInfo.gameContentTypeSupported = true; + setUpDisplay(display); + updateAvailableDisplays(); + mAdapter.registerLocked(); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + assertThat(mListener.addedDisplays.size()).isEqualTo(1); + assertThat(mListener.changedDisplays).isEmpty(); + + DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0) + .getDisplayDeviceInfoLocked(); + + assertThat(displayDeviceInfo.gameContentTypeSupported).isTrue(); + + // Change the display + display.dynamicInfo.gameContentTypeSupported = false; + setUpDisplay(display); + mInjector.getTransmitter().sendHotplug(display, /* connected */ true); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + assertTrue(mListener.traversalRequested); + assertThat(mListener.addedDisplays.size()).isEqualTo(1); + assertThat(mListener.changedDisplays.size()).isEqualTo(1); + + DisplayDevice displayDevice = mListener.changedDisplays.get(0); + displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); + displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); + + assertThat(displayDeviceInfo.gameContentTypeSupported).isFalse(); + } + + @Test public void testAfterDisplayChange_ColorModesAreUpdated() throws Exception { FakeDisplay display = new FakeDisplay(PORT_A); final int[] initialColorModes = new int[]{Display.COLOR_MODE_BT709}; - display.colorModes = initialColorModes; + display.dynamicInfo.supportedColorModes = initialColorModes; setUpDisplay(display); updateAvailableDisplays(); mAdapter.registerLocked(); @@ -455,11 +544,12 @@ public class LocalDisplayAdapterTest { // Change the display final int[] changedColorModes = new int[]{Display.COLOR_MODE_DEFAULT}; - display.colorModes = changedColorModes; + display.dynamicInfo.supportedColorModes = changedColorModes; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertTrue(mListener.traversalRequested); assertThat(mListener.addedDisplays.size()).isEqualTo(1); assertThat(mListener.changedDisplays.size()).isEqualTo(1); @@ -474,8 +564,8 @@ public class LocalDisplayAdapterTest { @Test public void testDisplayChange_withStaleDesiredDisplayModeSpecs() throws Exception { SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{ - createFakeDisplayMode(1920, 1080, 60f), - createFakeDisplayMode(1920, 1080, 50f) + createFakeDisplayMode(0, 1920, 1080, 60f), + createFakeDisplayMode(1, 1920, 1080, 50f) }; final int activeMode = 0; FakeDisplay display = new FakeDisplay(PORT_A, modes, activeMode); @@ -486,58 +576,111 @@ public class LocalDisplayAdapterTest { mAdapter.registerLocked(); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertThat(mListener.addedDisplays.size()).isEqualTo(1); + DisplayDevice displayDevice = mListener.addedDisplays.get(0); + + int baseModeId = Arrays.stream(displayDevice.getDisplayDeviceInfoLocked().supportedModes) + .filter(mode -> mode.getRefreshRate() == 60f) + .findFirst() + .get() + .getModeId(); + + displayDevice.setDesiredDisplayModeSpecsLocked( + new DisplayModeDirector.DesiredDisplayModeSpecs( + /*baseModeId*/ baseModeId, + /*allowGroupSwitching*/ false, + new DisplayModeDirector.RefreshRateRange(60f, 60f), + new DisplayModeDirector.RefreshRateRange(60f, 60f) + )); + verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token, + new SurfaceControl.DesiredDisplayModeSpecs( + /* baseModeId */ 0, + /* allowGroupSwitching */ false, + /* primaryRange */ 60f, 60f, + /* appRange */ 60f, 60f + )); + // Change the display - display.modes = new SurfaceControl.DisplayMode[]{ - createFakeDisplayMode(1920, 1080, 60f) + display.dynamicInfo.supportedDisplayModes = new SurfaceControl.DisplayMode[]{ + createFakeDisplayMode(2, 1920, 1080, 60f) }; - // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't - // trigger ArrayOutOfBoundsException. + display.dynamicInfo.activeDisplayModeId = 2; + // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't crash. display.desiredDisplayModeSpecs.defaultMode = 1; setUpDisplay(display); - updateAvailableDisplays(); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + assertTrue(mListener.traversalRequested); + + displayDevice.applyPendingDisplayDeviceInfoChangesLocked(); + + baseModeId = displayDevice.getDisplayDeviceInfoLocked().supportedModes[0].getModeId(); + + // The traversal request will call setDesiredDisplayModeSpecsLocked on the display device + displayDevice.setDesiredDisplayModeSpecsLocked( + new DisplayModeDirector.DesiredDisplayModeSpecs( + /*baseModeId*/ baseModeId, + /*allowGroupSwitching*/ false, + new DisplayModeDirector.RefreshRateRange(60f, 60f), + new DisplayModeDirector.RefreshRateRange(60f, 60f) + )); + + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + // Verify that this will reapply the desired modes. + verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token, + new SurfaceControl.DesiredDisplayModeSpecs( + /* baseModeId */ 2, + /* allowGroupSwitching */ false, + /* primaryRange */ 60f, 60f, + /* appRange */ 60f, 60f + )); } @Test public void testBacklightAdapter_withSurfaceControlSupport() { final Binder displayToken = new Binder(); - doReturn(true).when(() -> SurfaceControl.getDisplayBrightnessSupport(displayToken)); + + when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(true); // Test as default display - BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/); - ba.setBrightness(0.514f); - verify(() -> SurfaceControl.setDisplayBrightness(displayToken, 0.514f)); + BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/, + mSurfaceControlProxy); + ba.setBacklight(0.514f); + verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.514f); // Test as not default display - BacklightAdapter ba2 = new BacklightAdapter(displayToken, - false /*isDefault*/); - ba2.setBrightness(0.323f); - verify(() -> SurfaceControl.setDisplayBrightness(displayToken, 0.323f)); + BacklightAdapter ba2 = new BacklightAdapter(displayToken, false /*isDefault*/, + mSurfaceControlProxy); + ba2.setBacklight(0.323f); + verify(mSurfaceControlProxy).setDisplayBrightness(displayToken, 0.323f); } @Test public void testBacklightAdapter_withoutSourceControlSupport_defaultDisplay() { final Binder displayToken = new Binder(); - doReturn(false).when(() -> SurfaceControl.getDisplayBrightnessSupport(displayToken)); + when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(false); doReturn(mMockedBacklight).when(mMockedLightsManager) .getLight(LightsManager.LIGHT_ID_BACKLIGHT); - BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/); - ba.setBrightness(0.123f); + BacklightAdapter ba = new BacklightAdapter(displayToken, true /*isDefault*/, + mSurfaceControlProxy); + ba.setBacklight(0.123f); verify(mMockedBacklight).setBrightness(0.123f); } @Test public void testBacklightAdapter_withoutSourceControlSupport_nonDefaultDisplay() { final Binder displayToken = new Binder(); - doReturn(false).when(() -> SurfaceControl.getDisplayBrightnessSupport(displayToken)); + when(mSurfaceControlProxy.getDisplayBrightnessSupport(displayToken)).thenReturn(false); doReturn(mMockedBacklight).when(mMockedLightsManager) .getLight(LightsManager.LIGHT_ID_BACKLIGHT); - BacklightAdapter ba = new BacklightAdapter(displayToken, false /*isDefault*/); - ba.setBrightness(0.456f); + BacklightAdapter ba = new BacklightAdapter(displayToken, false /*isDefault*/, + mSurfaceControlProxy); + ba.setBacklight(0.456f); // Adapter does not forward any brightness in this case. verify(mMockedBacklight, never()).setBrightness(anyFloat()); @@ -588,12 +731,15 @@ public class LocalDisplayAdapterTest { private static class FakeDisplay { public final DisplayAddress.Physical address; public final IBinder token = new Binder(); - public final SurfaceControl.DisplayInfo info; - public SurfaceControl.DisplayMode[] modes; - public int activeMode; - public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT }; - public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0], - 1000, 1000, 0); + public final SurfaceControl.StaticDisplayInfo info; + public SurfaceControl.DynamicDisplayInfo dynamicInfo = + new SurfaceControl.DynamicDisplayInfo(); + { + dynamicInfo.supportedColorModes = new int[]{ Display.COLOR_MODE_DEFAULT }; + dynamicInfo.hdrCapabilities = new Display.HdrCapabilities(new int[0], + 1000, 1000, 0); + } + public SurfaceControl.DesiredDisplayModeSpecs desiredDisplayModeSpecs = new SurfaceControl.DesiredDisplayModeSpecs(/* defaultMode */ 0, /* allowGroupSwitching */ false, @@ -603,37 +749,32 @@ public class LocalDisplayAdapterTest { /* appRefreshRateMax */60.f); private FakeDisplay(int port) { - this.address = createDisplayAddress(port); - this.info = createFakeDisplayInfo(); - this.modes = new SurfaceControl.DisplayMode[]{ - createFakeDisplayMode(800, 600, 60f) + address = createDisplayAddress(port); + info = createFakeDisplayInfo(); + dynamicInfo.supportedDisplayModes = new SurfaceControl.DisplayMode[]{ + createFakeDisplayMode(0, 800, 600, 60f) }; - this.activeMode = 0; + dynamicInfo.activeDisplayModeId = 0; } private FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode) { - this.address = createDisplayAddress(port); - this.info = createFakeDisplayInfo(); - this.modes = modes; - this.activeMode = activeMode; + address = createDisplayAddress(port); + info = createFakeDisplayInfo(); + dynamicInfo.supportedDisplayModes = modes; + dynamicInfo.activeDisplayModeId = activeMode; } } private void setUpDisplay(FakeDisplay display) { mAddresses.add(display.address); - doReturn(display.token).when(() -> - SurfaceControl.getPhysicalDisplayToken(display.address.getPhysicalDisplayId())); - doReturn(display.info).when(() -> SurfaceControl.getDisplayInfo(display.token)); - doReturn(display.modes).when( - () -> SurfaceControl.getDisplayModes(display.token)); - doReturn(display.activeMode).when(() -> SurfaceControl.getActiveDisplayMode(display.token)); - doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token)); - doReturn(display.colorModes).when( - () -> SurfaceControl.getDisplayColorModes(display.token)); - doReturn(display.hdrCapabilities).when( - () -> SurfaceControl.getHdrCapabilities(display.token)); - doReturn(display.desiredDisplayModeSpecs) - .when(() -> SurfaceControl.getDesiredDisplayModeSpecs(display.token)); + when(mSurfaceControlProxy.getPhysicalDisplayToken(display.address.getPhysicalDisplayId())) + .thenReturn(display.token); + when(mSurfaceControlProxy.getStaticDisplayInfo(display.token)) + .thenReturn(display.info); + when(mSurfaceControlProxy.getDynamicDisplayInfo(display.token)) + .thenReturn(display.dynamicInfo); + when(mSurfaceControlProxy.getDesiredDisplayModeSpecs(display.token)) + .thenReturn(display.desiredDisplayModeSpecs); } private void updateAvailableDisplays() { @@ -643,27 +784,28 @@ public class LocalDisplayAdapterTest { ids[i] = address.getPhysicalDisplayId(); i++; } - doReturn(ids).when(() -> SurfaceControl.getPhysicalDisplayIds()); + when(mSurfaceControlProxy.getPhysicalDisplayIds()).thenReturn(ids); } private static DisplayAddress.Physical createDisplayAddress(int port) { return DisplayAddress.fromPortAndModel(port, DISPLAY_MODEL); } - private static SurfaceControl.DisplayInfo createFakeDisplayInfo() { - final SurfaceControl.DisplayInfo info = new SurfaceControl.DisplayInfo(); + private static SurfaceControl.StaticDisplayInfo createFakeDisplayInfo() { + final SurfaceControl.StaticDisplayInfo info = new SurfaceControl.StaticDisplayInfo(); info.density = 100; return info; } - private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height, + private static SurfaceControl.DisplayMode createFakeDisplayMode(int id, int width, int height, float refreshRate) { - return createFakeDisplayMode(width, height, refreshRate, 0); + return createFakeDisplayMode(id, width, height, refreshRate, /* group */ 0); } - private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height, + private static SurfaceControl.DisplayMode createFakeDisplayMode(int id, int width, int height, float refreshRate, int group) { final SurfaceControl.DisplayMode mode = new SurfaceControl.DisplayMode(); + mode.id = id; mode.width = width; mode.height = height; mode.refreshRate = refreshRate; @@ -710,14 +852,21 @@ public class LocalDisplayAdapterTest { LocalDisplayAdapter.DisplayEventListener listener) { mTransmitter = new HotplugTransmitter(looper, listener); } + public HotplugTransmitter getTransmitter() { return mTransmitter; } + + @Override + public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() { + return mSurfaceControlProxy; + } } private class TestListener implements DisplayAdapter.Listener { public ArrayList<DisplayDevice> addedDisplays = new ArrayList<>(); public ArrayList<DisplayDevice> changedDisplays = new ArrayList<>(); + public boolean traversalRequested = false; @Override public void onDisplayDeviceEvent(DisplayDevice device, int event) { @@ -730,6 +879,27 @@ public class LocalDisplayAdapterTest { @Override public void onTraversalRequested() { + traversalRequested = true; } } + + private TypedArray createFloatTypedArray(float[] vals) { + TypedArray mockArray = mock(TypedArray.class); + when(mockArray.length()).thenAnswer(invocation -> { + return vals.length; + }); + when(mockArray.getFloat(anyInt(), anyFloat())).thenAnswer(invocation -> { + final float def = (float) invocation.getArguments()[1]; + if (vals == null) { + return def; + } + int idx = (int) invocation.getArguments()[0]; + if (idx >= 0 && idx < vals.length) { + return vals[idx]; + } else { + return def; + } + }); + return mockArray; + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index 8099eda9a0af..f7f592886473 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -19,6 +19,7 @@ package com.android.server.job.controllers; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; @@ -63,7 +64,6 @@ import android.util.DataUnit; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; -import com.android.server.job.JobServiceContext; import com.android.server.net.NetworkPolicyManagerInternal; import org.junit.Before; @@ -144,8 +144,7 @@ public class ConnectivityControllerTest { .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); final ConnectivityController controller = new ConnectivityController(mService); - when(mService.getMaxJobExecutionTimeMs(any())) - .thenReturn(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS); + when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L); // Slow network is too slow assertFalse(controller.isSatisfied(createJobStatus(job), net, @@ -613,6 +612,7 @@ public class ConnectivityControllerTest { private static NetworkCapabilities createCapabilities() { return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) .addCapability(NET_CAPABILITY_VALIDATED); } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java index 6d40034c6000..91b3cb7dbdd9 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java @@ -25,6 +25,7 @@ import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.RARE_INDEX; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.WORKING_INDEX; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING; @@ -101,7 +102,7 @@ public class JobStatusTest { Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); JobSchedulerService.sUptimeMillisClock = Clock.fixed(SystemClock.uptimeClock().instant(), ZoneOffset.UTC); - JobSchedulerService.sElapsedRealtimeClock = + sElapsedRealtimeClock = Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); } @@ -204,7 +205,7 @@ public class JobStatusTest { @Test public void testFraction() throws Exception { - final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + final long now = sElapsedRealtimeClock.millis(); assertEquals(1, createJobStatus(0, Long.MAX_VALUE).getFractionRunTime(), DELTA); @@ -261,15 +262,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setChargingConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); - job.setChargingConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); markImplicitConstraintsSatisfied(job, false); - job.setChargingConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); - job.setChargingConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); } @@ -282,15 +283,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setIdleConstraintSatisfied(false); + job.setIdleConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE)); - job.setIdleConstraintSatisfied(true); + job.setIdleConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE)); markImplicitConstraintsSatisfied(job, false); - job.setIdleConstraintSatisfied(false); + job.setIdleConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE)); - job.setIdleConstraintSatisfied(true); + job.setIdleConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE)); } @@ -303,15 +304,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setBatteryNotLowConstraintSatisfied(false); + job.setBatteryNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW)); - job.setBatteryNotLowConstraintSatisfied(true); + job.setBatteryNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW)); markImplicitConstraintsSatisfied(job, false); - job.setBatteryNotLowConstraintSatisfied(false); + job.setBatteryNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW)); - job.setBatteryNotLowConstraintSatisfied(true); + job.setBatteryNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW)); } @@ -324,15 +325,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setStorageNotLowConstraintSatisfied(false); + job.setStorageNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW)); - job.setStorageNotLowConstraintSatisfied(true); + job.setStorageNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW)); markImplicitConstraintsSatisfied(job, false); - job.setStorageNotLowConstraintSatisfied(false); + job.setStorageNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW)); - job.setStorageNotLowConstraintSatisfied(true); + job.setStorageNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW)); } @@ -345,15 +346,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setTimingDelayConstraintSatisfied(false); + job.setTimingDelayConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY)); - job.setTimingDelayConstraintSatisfied(true); + job.setTimingDelayConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY)); markImplicitConstraintsSatisfied(job, false); - job.setTimingDelayConstraintSatisfied(false); + job.setTimingDelayConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY)); - job.setTimingDelayConstraintSatisfied(true); + job.setTimingDelayConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY)); } @@ -366,15 +367,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setDeadlineConstraintSatisfied(false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setDeadlineConstraintSatisfied(true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); markImplicitConstraintsSatisfied(job, false); - job.setDeadlineConstraintSatisfied(false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setDeadlineConstraintSatisfied(true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); } @@ -387,15 +388,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setConnectivityConstraintSatisfied(false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); - job.setConnectivityConstraintSatisfied(true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); markImplicitConstraintsSatisfied(job, false); - job.setConnectivityConstraintSatisfied(false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); - job.setConnectivityConstraintSatisfied(true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); } @@ -410,15 +411,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setContentTriggerConstraintSatisfied(false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setContentTriggerConstraintSatisfied(true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); markImplicitConstraintsSatisfied(job, false); - job.setContentTriggerConstraintSatisfied(false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setContentTriggerConstraintSatisfied(true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); } @@ -436,15 +437,15 @@ public class JobStatusTest { markImplicitConstraintsSatisfied(job, false); - job.setChargingConstraintSatisfied(false); - job.setConnectivityConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setChargingConstraintSatisfied(true); - job.setConnectivityConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); // Still false because implicit constraints aren't satisfied. assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); @@ -452,61 +453,61 @@ public class JobStatusTest { markImplicitConstraintsSatisfied(job, true); - job.setChargingConstraintSatisfied(false); - job.setConnectivityConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Turn on constraints one at a time. - job.setChargingConstraintSatisfied(true); - job.setConnectivityConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setChargingConstraintSatisfied(false); - job.setConnectivityConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setChargingConstraintSatisfied(false); - job.setConnectivityConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // With two of the 3 constraints satisfied (and implicit constraints also satisfied), only // the unsatisfied constraint should return true. - job.setChargingConstraintSatisfied(true); - job.setConnectivityConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setChargingConstraintSatisfied(true); - job.setConnectivityConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setChargingConstraintSatisfied(false); - job.setConnectivityConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setChargingConstraintSatisfied(true); - job.setConnectivityConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); @@ -526,15 +527,15 @@ public class JobStatusTest { markImplicitConstraintsSatisfied(job, false); - job.setChargingConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(false); - job.setDeadlineConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setChargingConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(true); - job.setDeadlineConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); // Still false because implicit constraints aren't satisfied. assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); @@ -542,18 +543,18 @@ public class JobStatusTest { markImplicitConstraintsSatisfied(job, true); - job.setChargingConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(false); - job.setDeadlineConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Once implicit constraint are satisfied, deadline constraint should always return true. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); // Turn on constraints one at a time. - job.setChargingConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(false); - job.setDeadlineConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); // Deadline should force isReady to be true, but isn't needed for the job to be // considered ready. @@ -561,17 +562,17 @@ public class JobStatusTest { // Once implicit constraint are satisfied, deadline constraint should always return true. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setChargingConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(true); - job.setDeadlineConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Once implicit constraint are satisfied, deadline constraint should always return true. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setChargingConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(false); - job.setDeadlineConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); // Since the deadline constraint is satisfied, none of the other explicit constraints are // needed. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); @@ -581,33 +582,33 @@ public class JobStatusTest { // With two of the 3 constraints satisfied (and implicit constraints also satisfied), only // the unsatisfied constraint should return true. - job.setChargingConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(true); - job.setDeadlineConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Once implicit constraint are satisfied, deadline constraint should always return true. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setChargingConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(false); - job.setDeadlineConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Once implicit constraint are satisfied, deadline constraint should always return true. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setChargingConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(true); - job.setDeadlineConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Once implicit constraint are satisfied, deadline constraint should always return true. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setChargingConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(true); - job.setDeadlineConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Once implicit constraint are satisfied, deadline constraint should always return true. @@ -621,15 +622,15 @@ public class JobStatusTest { new JobInfo.Builder(101, new ComponentName("foo", "bar")).build()); markImplicitConstraintsSatisfied(job, false); - job.setDeviceNotDozingConstraintSatisfied(false, false); + job.setDeviceNotDozingConstraintSatisfied(sElapsedRealtimeClock.millis(), false, false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEVICE_NOT_DOZING)); - job.setDeviceNotDozingConstraintSatisfied(true, false); + job.setDeviceNotDozingConstraintSatisfied(sElapsedRealtimeClock.millis(), true, false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEVICE_NOT_DOZING)); markImplicitConstraintsSatisfied(job, true); - job.setDeviceNotDozingConstraintSatisfied(false, false); + job.setDeviceNotDozingConstraintSatisfied(sElapsedRealtimeClock.millis(), false, false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEVICE_NOT_DOZING)); - job.setDeviceNotDozingConstraintSatisfied(true, false); + job.setDeviceNotDozingConstraintSatisfied(sElapsedRealtimeClock.millis(), true, false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEVICE_NOT_DOZING)); } @@ -640,15 +641,15 @@ public class JobStatusTest { new JobInfo.Builder(101, new ComponentName("foo", "bar")).build()); markImplicitConstraintsSatisfied(job, false); - job.setQuotaConstraintSatisfied(false); + job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_WITHIN_QUOTA)); - job.setQuotaConstraintSatisfied(true); + job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_WITHIN_QUOTA)); markImplicitConstraintsSatisfied(job, true); - job.setQuotaConstraintSatisfied(false); + job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_WITHIN_QUOTA)); - job.setQuotaConstraintSatisfied(true); + job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_WITHIN_QUOTA)); } @@ -659,22 +660,24 @@ public class JobStatusTest { new JobInfo.Builder(101, new ComponentName("foo", "bar")).build()); markImplicitConstraintsSatisfied(job, false); - job.setBackgroundNotRestrictedConstraintSatisfied(false); + job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); - job.setBackgroundNotRestrictedConstraintSatisfied(true); + job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); markImplicitConstraintsSatisfied(job, true); - job.setBackgroundNotRestrictedConstraintSatisfied(false); + job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); - job.setBackgroundNotRestrictedConstraintSatisfied(true); + job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); } private void markImplicitConstraintsSatisfied(JobStatus job, boolean isSatisfied) { - job.setQuotaConstraintSatisfied(isSatisfied); - job.setDeviceNotDozingConstraintSatisfied(isSatisfied, false); - job.setBackgroundNotRestrictedConstraintSatisfied(isSatisfied); + job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied); + job.setDeviceNotDozingConstraintSatisfied( + sElapsedRealtimeClock.millis(), isSatisfied, false); + job.setBackgroundNotRestrictedConstraintSatisfied( + sElapsedRealtimeClock.millis(), isSatisfied); } private static JobStatus createJobStatus(long earliestRunTimeElapsedMillis, diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index c4c9173536ad..29db7409131e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -31,6 +31,7 @@ import static com.android.server.job.JobSchedulerService.RARE_INDEX; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.WORKING_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import static com.android.server.job.JobSchedulerService.sSystemClock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -58,6 +59,7 @@ import android.app.AppGlobals; import android.app.IActivityManager; import android.app.IUidObserver; import android.app.job.JobInfo; +import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; @@ -82,11 +84,10 @@ import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.PowerAllowlistInternal; import com.android.server.job.JobSchedulerService; -import com.android.server.job.JobSchedulerService.Constants; -import com.android.server.job.JobServiceContext; import com.android.server.job.JobStore; import com.android.server.job.controllers.QuotaController.ExecutionStats; import com.android.server.job.controllers.QuotaController.QcConstants; +import com.android.server.job.controllers.QuotaController.ShrinkableDebits; import com.android.server.job.controllers.QuotaController.TimingSession; import com.android.server.usage.AppStandbyInternal; @@ -123,9 +124,11 @@ public class QuotaControllerTest { private BroadcastReceiver mChargingReceiver; private QuotaController mQuotaController; private QuotaController.QcConstants mQcConstants; + private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants(); private int mSourceUid; private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener; private IUidObserver mUidObserver; + private UsageStatsManagerInternal.UsageEventListener mUsageEventListener; DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder; private MockitoSession mMockingSession; @@ -158,7 +161,7 @@ public class QuotaControllerTest { // Called in StateController constructor. when(mJobSchedulerService.getTestableContext()).thenReturn(mContext); when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService); - when(mJobSchedulerService.getConstants()).thenReturn(mock(Constants.class)); + when(mJobSchedulerService.getConstants()).thenReturn(mConstants); // Called in QuotaController constructor. IActivityManager activityManager = ActivityManager.getService(); spyOn(activityManager); @@ -219,6 +222,8 @@ public class QuotaControllerTest { ArgumentCaptor.forClass(IUidObserver.class); ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor = ArgumentCaptor.forClass(PowerAllowlistInternal.TempAllowlistChangeListener.class); + ArgumentCaptor<UsageStatsManagerInternal.UsageEventListener> ueListenerCaptor = + ArgumentCaptor.forClass(UsageStatsManagerInternal.UsageEventListener.class); mQuotaController = new QuotaController(mJobSchedulerService, mock(BackgroundJobsController.class), mock(ConnectivityController.class)); @@ -230,6 +235,8 @@ public class QuotaControllerTest { verify(mPowerAllowlistInternal) .registerTempAllowlistChangeListener(taChangeCaptor.capture()); mTempAllowlistListener = taChangeCaptor.getValue(); + verify(mUsageStatsManager).registerListener(ueListenerCaptor.capture()); + mUsageEventListener = ueListenerCaptor.getValue(); try { verify(activityManager).registerUidObserver( uidObserverCaptor.capture(), @@ -289,7 +296,7 @@ public class QuotaControllerTest { verify(foregroundUids, timeout(2 * SECOND_IN_MILLIS).times(1)).delete(eq(uid)); assertFalse(foregroundUids.get(uid)); } - waitForQuietBackground(); + waitForNonDelayedMessagesProcessed(); } catch (Exception e) { fail("exception encountered: " + e.getMessage()); } @@ -360,8 +367,9 @@ public class QuotaControllerTest { // Make sure tests aren't passing just because the default bucket is likely ACTIVE. js.setStandbyBucket(FREQUENT_INDEX); // Make sure Doze and background-not-restricted don't affect tests. - js.setDeviceNotDozingConstraintSatisfied(/* state */ true, /* allowlisted */false); - js.setBackgroundNotRestrictedConstraintSatisfied(true); + js.setDeviceNotDozingConstraintSatisfied(/* nowElapsed */ sElapsedRealtimeClock.millis(), + /* state */ true, /* allowlisted */false); + js.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), true); return js; } @@ -385,13 +393,8 @@ public class QuotaControllerTest { } } - private void waitForQuietBackground() throws Exception { - for (int i = 0; i < 5; ++i) { - if (!mQuotaController.isActiveBackgroundProcessing()) { - break; - } - Thread.sleep(500); - } + private void waitForNonDelayedMessagesProcessed() { + mQuotaController.getHandler().runWithScissors(() -> {}, 15_000); } @Test @@ -1282,23 +1285,23 @@ public class QuotaControllerTest { } @Test - public void testGetMaxJobExecutionTimeLocked() { + public void testGetMaxJobExecutionTimeLocked_Regular() { mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0); - job.setStandbyBucket(RARE_INDEX); + setStandbyBucket(RARE_INDEX, job); setCharging(); synchronized (mQuotaController.mLock) { - assertEquals(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS, + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); } setDischarging(); setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); synchronized (mQuotaController.mLock) { - assertEquals(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS, + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); } @@ -1310,7 +1313,7 @@ public class QuotaControllerTest { } setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); synchronized (mQuotaController.mLock) { - assertEquals(JobServiceContext.DEFAULT_EXECUTING_TIMESLICE_MILLIS, + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); mQuotaController.maybeStopTrackingJobLocked(job, null, false); } @@ -1322,6 +1325,123 @@ public class QuotaControllerTest { } } + @Test + public void testGetMaxJobExecutionTimeLocked_Regular_Active() { + JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked_Regular_Active", 0); + setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 10 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 10 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 2 * HOUR_IN_MILLIS); + setDischarging(); + setStandbyBucket(ACTIVE_INDEX, job); + setProcessState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY); + + // ACTIVE apps (where allowed time = window size) should be capped at max execution limit. + synchronized (mQuotaController.mLock) { + assertEquals(2 * HOUR_IN_MILLIS, + mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + } + + // Make sure sessions are factored in properly. + mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, + createTimingSession(sElapsedRealtimeClock.millis() - (6 * HOUR_IN_MILLIS), + 30 * MINUTE_IN_MILLIS, 1), false); + synchronized (mQuotaController.mLock) { + assertEquals(90 * MINUTE_IN_MILLIS, + mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + } + + mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, + createTimingSession(sElapsedRealtimeClock.millis() - (5 * HOUR_IN_MILLIS), + 30 * MINUTE_IN_MILLIS, 1), false); + mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, + createTimingSession(sElapsedRealtimeClock.millis() - (4 * HOUR_IN_MILLIS), + 30 * MINUTE_IN_MILLIS, 1), false); + mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, + createTimingSession(sElapsedRealtimeClock.millis() - (3 * HOUR_IN_MILLIS), + 25 * MINUTE_IN_MILLIS, 1), false); + synchronized (mQuotaController.mLock) { + assertEquals(5 * MINUTE_IN_MILLIS, + mQuotaController.getMaxJobExecutionTimeMsLocked((job))); + } + } + + @Test + public void testGetMaxJobExecutionTimeLocked_EJ() { + final long timeUsedMs = 3 * MINUTE_IN_MILLIS; + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), + timeUsedMs, 5), true); + JobStatus job = createExpeditedJobStatus("testGetMaxJobExecutionTimeLocked_EJ", 0); + setStandbyBucket(RARE_INDEX, job); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + } + + setCharging(); + synchronized (mQuotaController.mLock) { + assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } + + setDischarging(); + setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_WORKING_MS / 2, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } + + // Top-started job + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.prepareForExecutionLocked(job); + } + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + mQuotaController.maybeStopTrackingJobLocked(job, null, false); + } + + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } + + // Test used quota rolling out of window. + synchronized (mQuotaController.mLock) { + mQuotaController.clearAppStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); + } + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, + createTimingSession(sElapsedRealtimeClock.millis() - mQcConstants.EJ_WINDOW_SIZE_MS, + timeUsedMs, 5), true); + + setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_WORKING_MS / 2, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } + + // Top-started job + setProcessState(ActivityManager.PROCESS_STATE_TOP); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(job, null); + mQuotaController.prepareForExecutionLocked(job); + } + setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + mQuotaController.maybeStopTrackingJobLocked(job, null, false); + } + + setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); + synchronized (mQuotaController.mLock) { + assertEquals(mQcConstants.EJ_LIMIT_RARE_MS, + mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + } + } + /** * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket * window. @@ -2508,7 +2628,7 @@ public class QuotaControllerTest { assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[WORKING_INDEX]); assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]); assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RARE_INDEX]); - assertEquals(0, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); + assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[RESTRICTED_INDEX]); assertEquals(0, mQuotaController.getEjLimitSpecialAdditionMs()); assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJLimitWindowSizeMs()); assertEquals(1, mQuotaController.getEJTopAppTimeChunkSizeMs()); @@ -5083,4 +5203,257 @@ public class QuotaControllerTest { eq(10 * SECOND_IN_MILLIS)); verify(handler, never()).sendMessageDelayed(any(), eq(remainingTimeMs)); } + + @Test + public void testEJDebitTallying() { + setStandbyBucket(RARE_INDEX); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); + // 15 seconds for each 30 second chunk. + setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 30 * SECOND_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 15 * SECOND_IN_MILLIS); + + // No history. Debits should be 0. + ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); + assertEquals(0, debit.getTallyLocked()); + assertEquals(10 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Regular job shouldn't affect EJ tally. + JobStatus regJob = createJobStatus("testEJDebitTallying", 1); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(regJob, null); + mQuotaController.prepareForExecutionLocked(regJob); + } + advanceElapsedClock(5000); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(regJob, null, false); + } + assertEquals(0, debit.getTallyLocked()); + assertEquals(10 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // EJ job should affect EJ tally. + JobStatus eJob = createExpeditedJobStatus("testEJDebitTallying", 2); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(eJob, null); + mQuotaController.prepareForExecutionLocked(eJob); + } + advanceElapsedClock(5 * MINUTE_IN_MILLIS); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStopTrackingJobLocked(eJob, null, false); + } + assertEquals(5 * MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(5 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Instantaneous event for a different user shouldn't affect tally. + advanceElapsedClock(5 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS); + + UsageEvents.Event event = + new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID + 10, event); + assertEquals(5 * MINUTE_IN_MILLIS, debit.getTallyLocked()); + + // Instantaneous event for correct user should reduce tally. + advanceElapsedClock(5 * MINUTE_IN_MILLIS); + + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(6 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Activity start shouldn't reduce tally, but duration with activity started should affect + // remaining EJ time. + advanceElapsedClock(5 * MINUTE_IN_MILLIS); + event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_RESUMED, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + advanceElapsedClock(30 * SECOND_IN_MILLIS); + assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(6 * MINUTE_IN_MILLIS + 15 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + advanceElapsedClock(30 * SECOND_IN_MILLIS); + assertEquals(4 * MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(6 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // With activity pausing/stopping/destroying, tally should be updated. + advanceElapsedClock(MINUTE_IN_MILLIS); + event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_DESTROYED, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(3 * MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(7 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + @Test + public void testEJDebitTallying_StaleSession() { + setStandbyBucket(RARE_INDEX); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_RARE_MS, 10 * MINUTE_IN_MILLIS); + + final long nowElapsed = sElapsedRealtimeClock.millis(); + TimingSession ts = new TimingSession(nowElapsed, nowElapsed + 10 * MINUTE_IN_MILLIS, 5); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true); + + // Make the session stale. + advanceElapsedClock(12 * MINUTE_IN_MILLIS + mQcConstants.EJ_WINDOW_SIZE_MS); + + // With lazy deletion, we don't update the tally until getRemainingEJExecutionTimeLocked() + // is called, so call that first. + assertEquals(10 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); + assertEquals(0, debit.getTallyLocked()); + } + + /** + * Tests that rewards are properly accounted when there's no EJ running and the rewards exceed + * the accumulated debits. + */ + @Test + public void testEJDebitTallying_RewardExceedDebits_NoActiveSession() { + setStandbyBucket(WORKING_INDEX); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 30 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS); + + final long nowElapsed = sElapsedRealtimeClock.millis(); + TimingSession ts = new TimingSession(nowElapsed - 5 * MINUTE_IN_MILLIS, + nowElapsed - 4 * MINUTE_IN_MILLIS, 2); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true); + + ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); + assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(29 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(30 * SECOND_IN_MILLIS); + UsageEvents.Event event = + new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(0, debit.getTallyLocked()); + assertEquals(30 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(MINUTE_IN_MILLIS); + assertEquals(0, debit.getTallyLocked()); + assertEquals(30 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Excessive rewards don't increase maximum quota. + event = new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(0, debit.getTallyLocked()); + assertEquals(30 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + } + + /** + * Tests that rewards are properly accounted when there's an active EJ running and the rewards + * exceed the accumulated debits. + */ + @Test + public void testEJDebitTallying_RewardExceedDebits_ActiveSession() { + setStandbyBucket(WORKING_INDEX); + setProcessState(ActivityManager.PROCESS_STATE_SERVICE); + setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 30 * MINUTE_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_INTERACTION_MS, MINUTE_IN_MILLIS); + // 15 seconds for each 30 second chunk. + setDeviceConfigLong(QcConstants.KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS, 30 * SECOND_IN_MILLIS); + setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_TOP_APP_MS, 15 * SECOND_IN_MILLIS); + + final long nowElapsed = sElapsedRealtimeClock.millis(); + TimingSession ts = new TimingSession(nowElapsed - 5 * MINUTE_IN_MILLIS, + nowElapsed - 4 * MINUTE_IN_MILLIS, 2); + mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, ts, true); + + ShrinkableDebits debit = mQuotaController.getEJDebitsLocked(SOURCE_USER_ID, SOURCE_PACKAGE); + assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(29 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // With rewards coming in while an EJ is running, the remaining execution time should be + // adjusted accordingly (decrease due to EJ running + increase from reward). + JobStatus eJob = + createExpeditedJobStatus("testEJDebitTallying_RewardExceedDebits_ActiveSession", 1); + synchronized (mQuotaController.mLock) { + mQuotaController.maybeStartTrackingJobLocked(eJob, null); + mQuotaController.prepareForExecutionLocked(eJob); + } + advanceElapsedClock(30 * SECOND_IN_MILLIS); + assertEquals(MINUTE_IN_MILLIS, debit.getTallyLocked()); + assertEquals(28 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(30 * SECOND_IN_MILLIS); + UsageEvents.Event event = + new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(0, debit.getTallyLocked()); + assertEquals(29 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(MINUTE_IN_MILLIS); + assertEquals(0, debit.getTallyLocked()); + assertEquals(28 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // Activity start shouldn't reduce tally, but duration with activity started should affect + // remaining EJ time. + event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_RESUMED, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + advanceElapsedClock(30 * SECOND_IN_MILLIS); + assertEquals(0, debit.getTallyLocked()); + // Decrease by 30 seconds for running EJ, increase by 15 seconds due to ongoing activity. + assertEquals(27 * MINUTE_IN_MILLIS + 45 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + advanceElapsedClock(30 * SECOND_IN_MILLIS); + assertEquals(0, debit.getTallyLocked()); + assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(MINUTE_IN_MILLIS); + assertEquals(0, debit.getTallyLocked()); + assertEquals(27 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + event = new UsageEvents.Event(UsageEvents.Event.USER_INTERACTION, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(0, debit.getTallyLocked()); + assertEquals(28 * MINUTE_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + advanceElapsedClock(MINUTE_IN_MILLIS); + assertEquals(0, debit.getTallyLocked()); + assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + + // At this point, with activity pausing/stopping/destroying, since we're giving a reward, + // tally should remain 0, and time remaining shouldn't change since it was accounted for + // at every step. + event = new UsageEvents.Event(UsageEvents.Event.ACTIVITY_DESTROYED, sSystemClock.millis()); + event.mPackage = SOURCE_PACKAGE; + mUsageEventListener.onUsageEvent(SOURCE_USER_ID, event); + waitForNonDelayedMessagesProcessed(); + assertEquals(0, debit.getTallyLocked()); + assertEquals(27 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, + mQuotaController.getRemainingEJExecutionTimeLocked(SOURCE_USER_ID, SOURCE_PACKAGE)); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java index 69fe140a7863..e0c8b09aae88 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java @@ -500,7 +500,7 @@ public final class FakeGnssHal extends GnssNative.GnssHal { } @Override - protected boolean isAntennaInfoListeningSupported() { + protected boolean isAntennaInfoSupported() { return mIsAntennaInfoListeningSupported; } diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java index 66b037d70a40..1b58e924dd6a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java @@ -123,9 +123,10 @@ public class LocationProviderManagerTest { .setPowerUsage(POWER_USAGE_HIGH) .setAccuracy(ProviderProperties.ACCURACY_FINE) .build(); + private static final CallerIdentity PROVIDER_IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1, + "mypackage", "attribution"); private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1, - "mypackage", - "attribution"); + "mypackage", "attribution", "listener"); private static final WorkSource WORK_SOURCE = new WorkSource(IDENTITY.getUid()); private Random mRandom; @@ -169,7 +170,7 @@ public class LocationProviderManagerTest { mPassive.startManager(); mPassive.setRealProvider(new PassiveLocationProvider(mContext)); - mProvider = new TestProvider(PROPERTIES, IDENTITY); + mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY); mProvider.setProviderAllowed(true); mManager = new LocationProviderManager(mContext, mInjector, eventLog, NAME, mPassive); @@ -351,7 +352,8 @@ public class LocationProviderManagerTest { @Test public void testGetLastLocation_ClearOnMockRemoval() { - MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, IDENTITY); + MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, PROVIDER_IDENTITY, + Collections.emptySet()); mockProvider.setAllowed(true); mManager.setMockProvider(mockProvider); @@ -441,7 +443,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_SameProcess() throws Exception { CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage", - "attribution"); + "attribution", "listener"); ILocationListener listener = createMockLocationListener(); mManager.registerLocationRequest( @@ -477,7 +479,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_Unregister_SameProcess() throws Exception { CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage", - "attribution"); + "attribution", "listener"); ILocationListener listener = createMockLocationListener(); mManager.registerLocationRequest( @@ -604,7 +606,7 @@ public class LocationProviderManagerTest { @Test public void testRegisterListener_Wakelock() throws Exception { CallerIdentity identity = CallerIdentity.forTest(CURRENT_USER, Process.myPid(), "mypackage", - "attribution"); + "attribution", "listener"); ILocationListener listener = createMockLocationListener(); mManager.registerLocationRequest( @@ -1047,7 +1049,7 @@ public class LocationProviderManagerTest { private final ArrayList<Runnable> mFlushCallbacks = new ArrayList<>(); TestProvider(ProviderProperties properties, CallerIdentity identity) { - super(DIRECT_EXECUTOR, identity, properties); + super(DIRECT_EXECUTOR, identity, properties, Collections.emptySet()); } public void setProviderAllowed(boolean allowed) { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java index 07170dacb4da..cf5db2e0db98 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java @@ -43,6 +43,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Collections; + @Presubmit @SmallTest @RunWith(AndroidJUnit4.class) @@ -71,7 +73,8 @@ public class MockableLocationProviderTest { .setPowerUsage(POWER_USAGE_LOW) .setAccuracy(ACCURACY_FINE) .build(), - CallerIdentity.forTest(0, 1, "testpackage", "test")); + CallerIdentity.forTest(0, 1, "testpackage", "test"), + Collections.emptySet()); mProvider = new MockableLocationProvider(lock); mProvider.getController().setListener(mListener); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java index 775bdd580157..2bc1268f1b85 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java @@ -23,6 +23,7 @@ import com.android.server.location.provider.AbstractLocationProvider; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Collections; public class FakeProvider extends AbstractLocationProvider { @@ -40,7 +41,7 @@ public class FakeProvider extends AbstractLocationProvider { private final FakeProviderInterface mFakeInterface; public FakeProvider(FakeProviderInterface fakeInterface) { - super(Runnable::run, null, null); + super(Runnable::run, null, null, Collections.emptySet()); mFakeInterface = fakeInterface; } diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS index d825dfd7cf00..46b797b83f1d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS +++ b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS @@ -1 +1,3 @@ include /services/core/java/com/android/server/pm/OWNERS + +per-file StagingManagerTest.java = dariofreni@google.com, ioffe@google.com, olilan@google.com diff --git a/services/tests/rescueparty/Android.bp b/services/tests/rescueparty/Android.bp index 6733af4eb26a..ed7de967f069 100644 --- a/services/tests/rescueparty/Android.bp +++ b/services/tests/rescueparty/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + cc_test { name: "log_rescueparty_reset_event_reported", srcs: ["log_rescueparty_reset_event_reported.cpp"], diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 31e2b645ac73..68f547979ce0 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -2,6 +2,15 @@ // Build FrameworksServicesTests package //######################################################################## +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"], +} + android_test { name: "FrameworksServicesTests", @@ -172,9 +181,12 @@ java_genrule { ":FrameworksServicesTests_install_split_feature_a", ":FrameworksServicesTests_install_uses_sdk_0", ":FrameworksServicesTests_install_uses_sdk_q0", - ":FrameworksServicesTests_install_uses_sdk_r", + ":FrameworksServicesTests_install_uses_sdk_q0_r0", + ":FrameworksServicesTests_install_uses_sdk_r_none", ":FrameworksServicesTests_install_uses_sdk_r0", ":FrameworksServicesTests_install_uses_sdk_r5", + ":FrameworksServicesTests_install_uses_sdk_r0_s0", + ":FrameworksServicesTests_install_uses_sdk_r0_s5", ], out: ["FrameworkServicesTests_apks_as_resources.res.zip"], tools: ["soong_zip"], diff --git a/services/tests/servicestests/aidl/Android.bp b/services/tests/servicestests/aidl/Android.bp index d4e53dddf4a7..678053192e82 100644 --- a/services/tests/servicestests/aidl/Android.bp +++ b/services/tests/servicestests/aidl/Android.bp @@ -12,6 +12,15 @@ // 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: "servicestests-aidl", sdk_version: "current", diff --git a/services/tests/servicestests/apks/Android.bp b/services/tests/servicestests/apks/Android.bp index 3e11604005a0..6c918064dbff 100644 --- a/services/tests/servicestests/apks/Android.bp +++ b/services/tests/servicestests/apks/Android.bp @@ -1,3 +1,12 @@ +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_defaults { name: "FrameworksServicesTests_apks_defaults", sdk_version: "current", diff --git a/services/tests/servicestests/apks/install-split-base/Android.bp b/services/tests/servicestests/apks/install-split-base/Android.bp index 1b62aa2a8e96..39992f600b96 100644 --- a/services/tests/servicestests/apks/install-split-base/Android.bp +++ b/services/tests/servicestests/apks/install-split-base/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksServicesTests_install_split_base", defaults: ["FrameworksServicesTests_apks_defaults"], diff --git a/services/tests/servicestests/apks/install-split-feature-a/Android.bp b/services/tests/servicestests/apks/install-split-feature-a/Android.bp index 45d891720166..ca7295e48fbc 100644 --- a/services/tests/servicestests/apks/install-split-feature-a/Android.bp +++ b/services/tests/servicestests/apks/install-split-feature-a/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test_helper_app { name: "FrameworksServicesTests_install_split_feature_a", defaults: ["FrameworksServicesTests_apks_defaults"], diff --git a/services/tests/servicestests/apks/install_intent_filters/Android.bp b/services/tests/servicestests/apks/install_intent_filters/Android.bp index 59c8524e01c2..643824de785c 100644 --- a/services/tests/servicestests/apks/install_intent_filters/Android.bp +++ b/services/tests/servicestests/apks/install_intent_filters/Android.bp @@ -1,7 +1,15 @@ +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"], +} + android_test_helper_app { name: "FrameworksServicesTests_install_intent_filters", defaults: ["FrameworksServicesTests_apks_defaults"], srcs: ["**/*.java"], } - diff --git a/services/tests/servicestests/apks/install_uses_sdk/Android.bp b/services/tests/servicestests/apks/install_uses_sdk/Android.bp index c24aa2bd8bba..a51293dc1997 100644 --- a/services/tests/servicestests/apks/install_uses_sdk/Android.bp +++ b/services/tests/servicestests/apks/install_uses_sdk/Android.bp @@ -1,39 +1,56 @@ +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"], +} + +android_test_helper_app { + name: "FrameworksServicesTests_install_uses_sdk_q0", + defaults: ["FrameworksServicesTests_apks_defaults"], + manifest: "AndroidManifest-q0.xml", +} + +android_test_helper_app { + name: "FrameworksServicesTests_install_uses_sdk_q0_r0", + defaults: ["FrameworksServicesTests_apks_defaults"], + manifest: "AndroidManifest-q0-r0.xml", +} + +android_test_helper_app { + name: "FrameworksServicesTests_install_uses_sdk_r_none", + defaults: ["FrameworksServicesTests_apks_defaults"], + manifest: "AndroidManifest-r-none.xml", +} + android_test_helper_app { name: "FrameworksServicesTests_install_uses_sdk_r0", defaults: ["FrameworksServicesTests_apks_defaults"], manifest: "AndroidManifest-r0.xml", - - srcs: ["**/*.java"], } android_test_helper_app { name: "FrameworksServicesTests_install_uses_sdk_r5", defaults: ["FrameworksServicesTests_apks_defaults"], manifest: "AndroidManifest-r5.xml", - - srcs: ["**/*.java"], } android_test_helper_app { - name: "FrameworksServicesTests_install_uses_sdk_q0", + name: "FrameworksServicesTests_install_uses_sdk_r0_s0", defaults: ["FrameworksServicesTests_apks_defaults"], - manifest: "AndroidManifest-q0.xml", - - srcs: ["**/*.java"], + manifest: "AndroidManifest-r0-s0.xml", } android_test_helper_app { - name: "FrameworksServicesTests_install_uses_sdk_r", + name: "FrameworksServicesTests_install_uses_sdk_r0_s5", defaults: ["FrameworksServicesTests_apks_defaults"], - manifest: "AndroidManifest-r.xml", - - srcs: ["**/*.java"], + manifest: "AndroidManifest-r0-s5.xml", } android_test_helper_app { name: "FrameworksServicesTests_install_uses_sdk_0", defaults: ["FrameworksServicesTests_apks_defaults"], manifest: "AndroidManifest-0.xml", - - srcs: ["**/*.java"], } diff --git a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-0.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-0.xml index 215384b2831c..90b13d45fc05 100644 --- a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-0.xml +++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-0.xml @@ -18,7 +18,7 @@ <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29"> <!-- This is invalid, because there is no sdk version specified --> - <extension-sdk android:minExtensionVersion="5" /> + <extension-sdk android:minExtensionVersion="0" /> </uses-sdk> <application> diff --git a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-q0-r0.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-q0-r0.xml new file mode 100644 index 000000000000..2a3227660686 --- /dev/null +++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-q0-r0.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.servicestests.install_uses_sdk"> + + <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29"> + <!-- This fails because 29 doesn't have an extension sdk --> + <extension-sdk android:sdkVersion="29" android:minExtensionVersion="0" /> + <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0" /> + </uses-sdk> + + <application> + </application> +</manifest> diff --git a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r-none.xml index 5d22577d118a..c79c61c3dc32 100644 --- a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r.xml +++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r-none.xml @@ -18,7 +18,7 @@ <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29"> <!-- This is invalid, because there is no minimum extension version specified --> - <extension-sdk android:sdkVersion="10000" /> + <extension-sdk android:sdkVersion="30" /> </uses-sdk> <application> diff --git a/packages/SystemUI/res/values/config_tv.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0-s0.xml index 2e776749582c..af30915b4f0b 100644 --- a/packages/SystemUI/res/values/config_tv.xml +++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0-s0.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 The Android Open Source Project +<!-- Copyright (C) 2010 The Android Open 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,9 +13,14 @@ See the License for the specific language governing permissions and limitations under the License. --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.servicestests.install_uses_sdk"> -<resources> - <!-- Whether to enable microphone disclosure indicator - (com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar). --> - <bool name="audio_recording_disclosure_enabled">true</bool> -</resources> + <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29"> + <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0" /> + <extension-sdk android:sdkVersion="31" android:minExtensionVersion="0" /> + </uses-sdk> + + <application> + </application> +</manifest> diff --git a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0-s5.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0-s5.xml new file mode 100644 index 000000000000..bafe4c4faa3f --- /dev/null +++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0-s5.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.servicestests.install_uses_sdk"> + + <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29"> + <!-- This fails because 31 is not version 5 --> + <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0" /> + <extension-sdk android:sdkVersion="31" android:minExtensionVersion="5" /> + </uses-sdk> + + <application> + </application> +</manifest> diff --git a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0.xml index c1244f246355..2920b8641586 100644 --- a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0.xml +++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0.xml @@ -17,7 +17,7 @@ package="com.android.frameworks.servicestests.install_uses_sdk"> <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29"> - <extension-sdk android:sdkVersion="10000" android:minExtensionVersion="0" /> + <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0" /> </uses-sdk> <application> diff --git a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r5.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r5.xml index 3410938b3c2d..7723d051a041 100644 --- a/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r5.xml +++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r5.xml @@ -18,7 +18,7 @@ <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29"> <!-- This will fail to install, because minExtensionVersion is not met --> - <extension-sdk android:sdkVersion="10000" android:minExtensionVersion="5" /> + <extension-sdk android:sdkVersion="30" android:minExtensionVersion="5" /> </uses-sdk> <application> diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java index 488e5cdf33b9..1870df9ecf17 100644 --- a/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java @@ -30,6 +30,7 @@ import com.android.internal.os.BatteryStatsImpl; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -59,6 +60,7 @@ public final class BatteryStatsServiceTest { } @Test + @Ignore("b/180015146") public void testAwaitCompletion() throws Exception { final CountDownLatch readyLatch = new CountDownLatch(2); final CountDownLatch startLatch = new CountDownLatch(1); diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java index 1328b91d03f9..07f67327b2bf 100644 --- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java @@ -110,6 +110,8 @@ public final class AppHibernationServiceTest { UserInfo userInfo = addUser(USER_ID_1); mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo)); doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_1); + + mAppHibernationService.mIsServiceEnabled = true; } @Test diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java index 41e15631d258..663017890b0c 100644 --- a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java @@ -66,11 +66,13 @@ public class AppOpsNotedWatcherTest { inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpNoted(eq(AppOpsManager.OP_FINE_LOCATION), eq(Process.myUid()), eq(getContext().getPackageName()), - eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); + eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), + eq(AppOpsManager.MODE_ALLOWED)); inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpNoted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), - eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); + eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), + eq(AppOpsManager.MODE_ALLOWED)); // Stop watching appOpsManager.stopWatchingNoted(listener); @@ -94,7 +96,8 @@ public class AppOpsNotedWatcherTest { verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(2)).onOpNoted(eq(AppOpsManager.OP_FINE_LOCATION), eq(Process.myUid()), eq(getContext().getPackageName()), - eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); + eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), + eq(AppOpsManager.MODE_ALLOWED)); // Finish up appOpsManager.stopWatchingNoted(listener); diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java index fec8aa9ceaff..c12eb32a9143 100644 --- a/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java @@ -63,11 +63,13 @@ public class AppOpsStartedWatcherTest { inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpStarted(eq(AppOpsManager.OP_FINE_LOCATION), eq(Process.myUid()), eq(getContext().getPackageName()), - eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); + eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), + eq(AppOpsManager.MODE_ALLOWED)); inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpStarted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), - eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); + eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), + eq(AppOpsManager.MODE_ALLOWED)); // Stop watching appOpsManager.stopWatchingStarted(listener); @@ -91,7 +93,8 @@ public class AppOpsStartedWatcherTest { verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(2)).onOpStarted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), - eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); + eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), + eq(AppOpsManager.MODE_ALLOWED)); verifyNoMoreInteractions(listener); // Finish up diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java index 73b0105210c4..6890ed1688bb 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java @@ -28,6 +28,7 @@ import android.app.appsearch.SearchSpec; import android.app.appsearch.SetSchemaResponse; import android.app.appsearch.exceptions.AppSearchException; import android.content.Context; +import android.util.ArrayMap; import android.util.ArraySet; import androidx.test.core.app.ApplicationProvider; @@ -55,6 +56,7 @@ import org.junit.rules.TemporaryFolder; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; public class AppSearchImplTest { @@ -971,21 +973,46 @@ public class AppSearchImplTest { } @Test - public void testHasSchemaType() throws Exception { - // Nothing exists yet - assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isFalse(); + 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( - "package", - "database", - Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), + "package1", + "database1", + Collections.singletonList(new AppSearchSchema.Builder("schema").build()), /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false); - assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isTrue(); + assertThat(mAppSearchImpl.getPackageToDatabases()) + .containsExactlyEntriesIn(expectedMapping); - assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "UnknownSchema")) - .isFalse(); + // Has both databases + expectedMapping.put("package1", ImmutableSet.of("database1", "database2")); + mAppSearchImpl.setSchema( + "package1", + "database2", + Collections.singletonList(new AppSearchSchema.Builder("schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*forceOverride=*/ false); + 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()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*forceOverride=*/ false); + assertThat(mAppSearchImpl.getPackageToDatabases()) + .containsExactlyEntriesIn(expectedMapping); } @Test 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 new file mode 100644 index 000000000000..4308885faaad --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java @@ -0,0 +1,124 @@ +/* + * 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 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 = 2; + static final int TEST_TOTAL_LATENCY_MILLIS = 20; + + @Test + public void testAppSearchStats_GeneralStats() { + final GeneralStats gStats = + new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) + .setStatusCode(TEST_STATUS_CODE) + .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) + .build(); + + assertThat(gStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME); + assertThat(gStats.getDatabase()).isEqualTo(TEST_DATA_BASE); + assertThat(gStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); + assertThat(gStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); + } + + @Test + public void testAppSearchStats_CallStats() { + final int estimatedBinderLatencyMillis = 1; + final int numOperationsSucceeded = 2; + final int numOperationsFailed = 3; + + final GeneralStats gStats = + new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) + .setStatusCode(TEST_STATUS_CODE) + .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) + .build(); + final @CallStats.CallType int callType = CallStats.CALL_TYPE_PUT_DOCUMENTS; + final CallStats cStats = + new CallStats.Builder(gStats) + .setCallType(callType) + .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) + .setNumOperationsSucceeded(numOperationsSucceeded) + .setNumOperationsFailed(numOperationsFailed) + .build(); + + assertThat(cStats.getGeneralStats().getPackageName()).isEqualTo(TEST_PACKAGE_NAME); + assertThat(cStats.getGeneralStats().getDatabase()).isEqualTo(TEST_DATA_BASE); + assertThat(cStats.getGeneralStats().getStatusCode()).isEqualTo(TEST_STATUS_CODE); + assertThat(cStats.getGeneralStats().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 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 int nativeNumTokensClipped = 9; + + final GeneralStats gStats = + new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) + .setStatusCode(TEST_STATUS_CODE) + .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) + .build(); + final PutDocumentStats pStats = + new PutDocumentStats.Builder(gStats) + .setGenerateDocumentProtoLatencyMillis(generateDocumentProtoLatencyMillis) + .setRewriteDocumentTypesLatencyMillis(rewriteDocumentTypesLatencyMillis) + .setNativeLatencyMillis(nativeLatencyMillis) + .setNativeDocumentStoreLatencyMillis(nativeDocumentStoreLatencyMillis) + .setNativeIndexLatencyMillis(nativeIndexLatencyMillis) + .setNativeIndexMergeLatencyMillis(nativeIndexMergeLatencyMillis) + .setNativeDocumentSizeBytes(nativeDocumentSize) + .setNativeNumTokensIndexed(nativeNumTokensIndexed) + .setNativeNumTokensClipped(nativeNumTokensClipped) + .build(); + + assertThat(pStats.getGeneralStats().getPackageName()).isEqualTo(TEST_PACKAGE_NAME); + assertThat(pStats.getGeneralStats().getDatabase()).isEqualTo(TEST_DATA_BASE); + assertThat(pStats.getGeneralStats().getStatusCode()).isEqualTo(TEST_STATUS_CODE); + assertThat(pStats.getGeneralStats().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.getNativeNumTokensClipped()).isEqualTo(nativeNumTokensClipped); + } +} diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java index b98f0257d7b7..205ff300c543 100644 --- a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java @@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; +import android.app.backup.BackupAgent; import android.app.backup.BackupManager.OperationType; import android.app.backup.IBackupManagerMonitor; import android.app.backup.IBackupObserver; @@ -30,6 +31,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import com.android.internal.backup.IBackupTransport; import android.platform.test.annotations.Presubmit; import androidx.test.runner.AndroidJUnit4; @@ -56,6 +58,7 @@ public class UserBackupManagerServiceTest { @Mock IBackupObserver mBackupObserver; @Mock PackageManager mPackageManager; @Mock TransportClient mTransportClient; + @Mock IBackupTransport mBackupTransport; @Mock BackupEligibilityRules mBackupEligibilityRules; @@ -132,6 +135,33 @@ public class UserBackupManagerServiceTest { assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules); } + @Test + public void testGetOperationTypeFromTransport_returnsBackupByDefault() + throws Exception { + when(mTransportClient.connectOrThrow(any())).thenReturn(mBackupTransport); + when(mBackupTransport.getTransportFlags()).thenReturn(0); + + int operationType = mService.getOperationTypeFromTransport(mTransportClient); + + assertThat(operationType).isEqualTo(OperationType.BACKUP); + } + + @Test + public void testGetOperationTypeFromTransport_returnsMigrationForMigrationTransport() + throws Exception { + // This is a temporary flag to control the new behaviour until it's ready to be fully + // rolled out. + mService.shouldUseNewBackupEligibilityRules = true; + + when(mTransportClient.connectOrThrow(any())).thenReturn(mBackupTransport); + when(mBackupTransport.getTransportFlags()).thenReturn( + BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER); + + int operationType = mService.getOperationTypeFromTransport(mTransportClient); + + assertThat(operationType).isEqualTo(OperationType.MIGRATION); + } + private static PackageInfo getPackageInfo(String packageName) { PackageInfo packageInfo = new PackageInfo(); packageInfo.applicationInfo = new ApplicationInfo(); @@ -141,6 +171,7 @@ public class UserBackupManagerServiceTest { private static class TestBackupService extends UserBackupManagerService { boolean isEnabledStatePersisted = false; + boolean shouldUseNewBackupEligibilityRules = false; TestBackupService(Context context, PackageManager packageManager) { super(context, packageManager); @@ -158,5 +189,10 @@ public class UserBackupManagerServiceTest { @Override void updateStateOnBackupEnabled(boolean wasEnabled, boolean enable) {} + + @Override + boolean shouldUseNewBackupEligibilityRules() { + return shouldUseNewBackupEligibilityRules; + } } } diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java index 2cbc3f381909..a694d5e37566 100644 --- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java +++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java @@ -153,4 +153,25 @@ public class SyncOperationTest extends AndroidTestCase { assertEquals("Period not restored", periodic.periodMillis, oneoff.periodMillis); assertEquals("Flex not restored", periodic.flexMillis, oneoff.flexMillis); } + + @SmallTest + public void testScheduleAsEjIsInExtras() { + Account account1 = new Account("account1", "type1"); + Bundle b1 = new Bundle(); + b1.putBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true); + + SyncOperation op1 = new SyncOperation(account1, 0, 1, "foo", 0, + SyncOperation.REASON_USER_START, "authority1", b1, false, + ContentResolver.SYNC_EXEMPTION_NONE); + assertTrue(op1.isScheduledAsExpeditedJob()); + + PersistableBundle pb = op1.toJobInfoExtras(); + assertTrue("EJ extra not found in job extras", + ((PersistableBundle) pb.get("syncExtras")) + .containsKey(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB)); + + SyncOperation op2 = SyncOperation.maybeCreateFromJobExtras(pb); + assertTrue("EJ extra not found in extras", op2.getClonedExtras() + .getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB)); + } } 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 6add8d18aa3e..87100a63e35e 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -20,6 +20,8 @@ import static android.app.Notification.EXTRA_TITLE; import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE; import static android.app.admin.DevicePolicyManager.DELEGATION_APP_RESTRICTIONS; import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO; import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI; import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID; @@ -826,7 +828,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { * {@link DevicePolicyManager#forceRemoveActiveAdmin(ComponentName, int)} */ @Test - public void testForceRemoveActiveAdmin() throws Exception { + public void testForceRemoveActiveAdmin_nonShellCaller() throws Exception { mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); // Add admin. @@ -840,8 +842,53 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Calling from a non-shell uid should fail with a SecurityException mContext.binder.callingUid = 123456; assertExpectException(SecurityException.class, - /* messageRegex =*/ "Non-shell user attempted to call", + /* messageRegex = */ null, () -> dpms.forceRemoveActiveAdmin(admin1, CALLER_USER_HANDLE)); + } + + /** + * Test for: + * {@link DevicePolicyManager#forceRemoveActiveAdmin(ComponentName, int)} + */ + @Test + public void testForceRemoveActiveAdmin_nonShellCallerWithPermission() throws Exception { + mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); + + // Add admin. + setupPackageInPackageManager(admin1.getPackageName(), + /* userId= */ CALLER_USER_HANDLE, + /* appId= */ 10138, + /* flags= */ ApplicationInfo.FLAG_TEST_ONLY); + dpm.setActiveAdmin(admin1, /* replace =*/ false); + assertThat(dpm.isAdminActive(admin1)).isTrue(); + + mContext.binder.callingUid = 123456; + mContext.callerPermissions.add( + android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); + dpms.forceRemoveActiveAdmin(admin1, CALLER_USER_HANDLE); + + mContext.callerPermissions.add(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + // Verify + assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse(); + verify(getServices().usageStatsManagerInternal).setActiveAdminApps( + null, CALLER_USER_HANDLE); + } + + /** + * Test for: + * {@link DevicePolicyManager#forceRemoveActiveAdmin(ComponentName, int)} + */ + @Test + public void testForceRemoveActiveAdmin_ShellCaller() throws Exception { + mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); + + // Add admin. + setupPackageInPackageManager(admin1.getPackageName(), + /* userId= */ CALLER_USER_HANDLE, + /* appId= */ 10138, + /* flags= */ ApplicationInfo.FLAG_TEST_ONLY); + dpm.setActiveAdmin(admin1, /* replace =*/ false); + assertThat(dpm.isAdminActive(admin1)).isTrue(); mContext.binder.callingUid = Process.SHELL_UID; dpms.forceRemoveActiveAdmin(admin1, CALLER_USER_HANDLE); @@ -7028,6 +7075,71 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + public void testSetDeviceOwnerType_throwsExceptionWhenCallerNotAuthorized() { + assertThrows(SecurityException.class, + () -> dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT)); + } + + @Test + public void testSetDeviceOwnerType_throwsExceptionWhenThereIsNoDeviceOwner() { + mContext.binder.clearCallingIdentity(); + assertThrows(IllegalStateException.class, + () -> dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT)); + } + + @Test + public void testSetDeviceOwnerType_throwsExceptionWhenNotAsDeviceOwnerAdmin() throws Exception { + setDeviceOwner(); + + assertThrows(IllegalStateException.class, + () -> dpm.setDeviceOwnerType(admin2, DEVICE_OWNER_TYPE_FINANCED)); + } + + @Test + public void testSetDeviceOwnerType_asDeviceOwner_toFinancedDevice() throws Exception { + setDeviceOwner(); + + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + int returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1); + assertThat(dpms.mOwners.hasDeviceOwner()).isTrue(); + assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED); + + initializeDpms(); + + returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1); + assertThat(dpms.mOwners.hasDeviceOwner()).isTrue(); + assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED); + } + + @Test + public void testSetDeviceOwnerType_asDeviceOwner_throwsExceptionWhenSetDeviceOwnerTypeAgain() + throws Exception { + setDeviceOwner(); + + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + int returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1); + assertThat(dpms.mOwners.hasDeviceOwner()).isTrue(); + assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED); + + assertThrows(IllegalStateException.class, + () -> dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT)); + } + + @Test + public void testGetDeviceOwnerType_throwsExceptionWhenThereIsNoDeviceOwner() { + assertThrows(IllegalStateException.class, () -> dpm.getDeviceOwnerType(admin1)); + } + + @Test + public void testGetDeviceOwnerType_throwsExceptionWhenNotAsDeviceOwnerAdmin() throws Exception { + setDeviceOwner(); + + assertThrows(IllegalStateException.class, () -> dpm.getDeviceOwnerType(admin2)); + } + + @Test public void testSetUsbDataSignalingEnabled_noDeviceOwnerOrPoOfOrgOwnedDevice() { assertThrows(SecurityException.class, () -> dpm.setUsbDataSignalingEnabled(true)); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java index bfe183cc608b..39ca925d0115 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java @@ -16,6 +16,9 @@ package com.android.server.devicepolicy; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; + import static com.google.common.truth.Truth.assertThat; import android.content.ComponentName; @@ -35,7 +38,6 @@ import org.junit.runner.RunWith; * <p>Run this test with: * * {@code atest FrameworksServicesTests:com.android.server.devicepolicy.OwnersTest} - * */ @SmallTest @RunWith(AndroidJUnit4.class) @@ -67,6 +69,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNull(); assertThat(owners.getProfileOwnerKeys()).isEmpty(); @@ -75,6 +79,12 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(20)).isFalse(); assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); + + owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(), + DEVICE_OWNER_TYPE_FINANCED); + // There is no device owner, so the default owner type should be returned. + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); } // Then re-read and check. @@ -84,6 +94,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNull(); assertThat(owners.getProfileOwnerKeys()).isEmpty(); @@ -122,6 +134,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getDeviceOwnerName()).isEqualTo(null); assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc"); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNull(); assertThat(owners.getProfileOwnerKeys()).isEmpty(); @@ -142,6 +156,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getDeviceOwnerName()).isEqualTo(null); assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc"); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNull(); assertThat(owners.getProfileOwnerKeys()).isEmpty(); @@ -180,6 +196,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); assertThat(owners.getSystemUpdatePolicy()).isNull(); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getProfileOwnerKeys()).hasSize(2); assertThat(owners.getProfileOwnerComponent(10)) @@ -208,6 +226,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); assertThat(owners.getSystemUpdatePolicy()).isNull(); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getProfileOwnerKeys()).hasSize(2); assertThat(owners.getProfileOwnerComponent(10)) @@ -260,6 +280,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getDeviceOwnerName()).isEqualTo(null); assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc"); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNotNull(); assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5); @@ -292,6 +314,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getDeviceOwnerName()).isEqualTo(null); assertThat(owners.getDeviceOwnerPackageName()).isEqualTo("com.google.android.testdpc"); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_SYSTEM); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNotNull(); assertThat(owners.getSystemUpdatePolicy().getPolicyType()).isEqualTo(5); @@ -315,12 +339,21 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); owners.setDeviceOwnerUserRestrictionsMigrated(); + + owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(), + DEVICE_OWNER_TYPE_FINANCED); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_FINANCED); } { final OwnersTestable owners = new OwnersTestable(getServices()); owners.load(); + assertThat(owners.hasDeviceOwner()).isTrue(); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_FINANCED); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue(); assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isTrue(); @@ -328,12 +361,22 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse(); owners.setProfileOwnerUserRestrictionsMigrated(11); + + owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(), + DEVICE_OWNER_TYPE_DEFAULT); + // The previous device owner type should persist. + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_FINANCED); } { final OwnersTestable owners = new OwnersTestable(getServices()); owners.load(); + assertThat(owners.hasDeviceOwner()).isTrue(); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_FINANCED); + assertThat(owners.getDeviceOwnerUserRestrictionsNeedsMigration()).isFalse(); assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(10)).isTrue(); assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(11)).isFalse(); @@ -369,6 +412,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNull(); @@ -388,6 +433,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getSystemUpdatePolicy()).isNull(); @@ -425,6 +472,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getProfileOwnerKeys()).isEmpty(); assertThat(owners.getSystemUpdatePolicy()).isNotNull(); @@ -444,6 +493,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.hasDeviceOwner()).isFalse(); assertThat(owners.getDeviceOwnerUserId()).isEqualTo(UserHandle.USER_NULL); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getProfileOwnerKeys()).isEmpty(); assertThat(owners.getSystemUpdatePolicy()).isNotNull(); @@ -472,9 +523,16 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getLegacyConfigFile().exists()).isFalse(); assertThat(owners.getDeviceOwnerFile().exists()).isTrue(); + assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); assertThat(owners.getProfileOwnerFile(10).exists()).isTrue(); assertThat(owners.getProfileOwnerFile(11).exists()).isTrue(); + String previousDeviceOwnerPackageName = owners.getDeviceOwnerPackageName(); + owners.setDeviceOwnerType(previousDeviceOwnerPackageName, DEVICE_OWNER_TYPE_FINANCED); + assertThat(owners.getDeviceOwnerType(previousDeviceOwnerPackageName)).isEqualTo( + DEVICE_OWNER_TYPE_FINANCED); + // Then clear all information and save. owners.clearDeviceOwner(); owners.clearSystemUpdatePolicy(); @@ -491,5 +549,8 @@ public class OwnersTest extends DpmTestBase { assertThat(owners.getDeviceOwnerFile().exists()).isFalse(); assertThat(owners.getProfileOwnerFile(10).exists()).isFalse(); assertThat(owners.getProfileOwnerFile(11).exists()).isFalse(); + + assertThat(owners.getDeviceOwnerType(previousDeviceOwnerPackageName)).isEqualTo( + DEVICE_OWNER_TYPE_DEFAULT); } } diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index a078a77b4498..26a549d77664 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -21,8 +21,10 @@ import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STA import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; import static org.testng.Assert.assertThrows; +import android.hardware.devicestate.DeviceStateInfo; import android.hardware.devicestate.DeviceStateRequest; import android.hardware.devicestate.IDeviceStateManagerCallback; import android.os.Binder; @@ -33,10 +35,13 @@ import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import junit.framework.Assert; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; import java.util.HashMap; import java.util.Optional; @@ -70,14 +75,14 @@ public final class DeviceStateManagerServiceTest { public void baseStateChanged() { assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState(), OTHER_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); } @@ -89,21 +94,21 @@ public final class DeviceStateManagerServiceTest { mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE); - assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState(), OTHER_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier()); assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); mPolicy.resumeConfigure(); assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); } @@ -116,7 +121,7 @@ public final class DeviceStateManagerServiceTest { assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); } @@ -129,16 +134,19 @@ public final class DeviceStateManagerServiceTest { assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); } @Test - public void supportedStatesChanged() { + public void supportedStatesChanged() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE }); @@ -146,27 +154,44 @@ public final class DeviceStateManagerServiceTest { // supported. assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); + + assertArrayEquals(callback.getLastNotifiedInfo().supportedStates, + new int[] { DEFAULT_DEVICE_STATE.getIdentifier() }); } @Test - public void supportedStatesChanged_unsupportedBaseState() { + public void supportedStatesChanged_statesRemainSame() throws RemoteException { + TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); + mService.getBinderService().registerCallback(callback); + assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); - mProvider.notifySupportedDeviceStates(new DeviceState[]{ OTHER_DEVICE_STATE }); + mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE, + OTHER_DEVICE_STATE }); - // The current requested state is cleared because it is no longer supported. + // The current committed and requests states do not change because the current state remains + // supported. assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getBaseState(), Optional.empty()); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); - mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); + // The callback wasn't notified about a change in supported states as the states have not + // changed. + assertNull(callback.getLastNotifiedInfo()); + } - assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); - assertEquals(mService.getPendingState(), Optional.empty()); - assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE); + @Test + public void getDeviceStateInfo() throws RemoteException { + DeviceStateInfo info = mService.getBinderService().getDeviceStateInfo(); + assertNotNull(info); + assertArrayEquals(info.supportedStates, + new int[] { DEFAULT_DEVICE_STATE.getIdentifier(), + OTHER_DEVICE_STATE.getIdentifier() }); + assertEquals(info.baseState, DEFAULT_DEVICE_STATE.getIdentifier()); + assertEquals(info.currentState, DEFAULT_DEVICE_STATE.getIdentifier()); } @Test @@ -175,41 +200,33 @@ public final class DeviceStateManagerServiceTest { mService.getBinderService().registerCallback(callback); mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); - assertNotNull(callback.getLastNotifiedValue()); - assertEquals(callback.getLastNotifiedValue().intValue(), + assertEquals(callback.getLastNotifiedInfo().baseState, + OTHER_DEVICE_STATE.getIdentifier()); + assertEquals(callback.getLastNotifiedInfo().currentState, OTHER_DEVICE_STATE.getIdentifier()); mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier()); - assertEquals(callback.getLastNotifiedValue().intValue(), + assertEquals(callback.getLastNotifiedInfo().baseState, + DEFAULT_DEVICE_STATE.getIdentifier()); + assertEquals(callback.getLastNotifiedInfo().currentState, DEFAULT_DEVICE_STATE.getIdentifier()); mPolicy.blockConfigure(); mProvider.setState(OTHER_DEVICE_STATE.getIdentifier()); // The callback should not have been notified of the state change as the policy is still // pending callback. - assertEquals(callback.getLastNotifiedValue().intValue(), + assertEquals(callback.getLastNotifiedInfo().baseState, + DEFAULT_DEVICE_STATE.getIdentifier()); + assertEquals(callback.getLastNotifiedInfo().currentState, DEFAULT_DEVICE_STATE.getIdentifier()); mPolicy.resumeConfigure(); // Now that the policy is finished processing the callback should be notified of the state // change. - assertEquals(callback.getLastNotifiedValue().intValue(), + assertEquals(callback.getLastNotifiedInfo().baseState, + OTHER_DEVICE_STATE.getIdentifier()); + assertEquals(callback.getLastNotifiedInfo().currentState, OTHER_DEVICE_STATE.getIdentifier()); - } - - @Test - public void registerCallback_emitsInitialValue() throws RemoteException { - TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback(); - mService.getBinderService().registerCallback(callback); - assertNotNull(callback.getLastNotifiedValue()); - assertEquals(callback.getLastNotifiedValue().intValue(), - DEFAULT_DEVICE_STATE.getIdentifier()); - } - - @Test - public void getSupportedDeviceStates() throws RemoteException { - final int[] expectedStates = new int[] { 0, 1 }; - assertEquals(mService.getBinderService().getSupportedDeviceStates(), expectedStates); } @Test @@ -228,11 +245,16 @@ public final class DeviceStateManagerServiceTest { TestDeviceStateManagerCallback.STATUS_ACTIVE); // Committed state changes as there is a requested override. assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); + assertNotNull(callback.getLastNotifiedInfo()); + assertEquals(callback.getLastNotifiedInfo().baseState, + DEFAULT_DEVICE_STATE.getIdentifier()); + assertEquals(callback.getLastNotifiedInfo().currentState, + OTHER_DEVICE_STATE.getIdentifier()); mService.getBinderService().cancelRequest(token); @@ -240,10 +262,15 @@ public final class DeviceStateManagerServiceTest { TestDeviceStateManagerCallback.STATUS_CANCELED); // Committed state is set back to the requested state once the override is cleared. assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); assertFalse(mService.getOverrideState().isPresent()); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); + + assertEquals(callback.getLastNotifiedInfo().baseState, + DEFAULT_DEVICE_STATE.getIdentifier()); + assertEquals(callback.getLastNotifiedInfo().currentState, + DEFAULT_DEVICE_STATE.getIdentifier()); } @Test @@ -263,7 +290,7 @@ public final class DeviceStateManagerServiceTest { // Committed state changes as there is a requested override. assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); @@ -275,7 +302,7 @@ public final class DeviceStateManagerServiceTest { TestDeviceStateManagerCallback.STATUS_CANCELED); // Committed state is set back to the requested state once the override is cleared. assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); - assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE); + assertEquals(mService.getBaseState(), OTHER_DEVICE_STATE); assertFalse(mService.getOverrideState().isPresent()); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); @@ -297,7 +324,7 @@ public final class DeviceStateManagerServiceTest { TestDeviceStateManagerCallback.STATUS_ACTIVE); // Committed state changes as there is a requested override. assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), OTHER_DEVICE_STATE.getIdentifier()); @@ -310,7 +337,7 @@ public final class DeviceStateManagerServiceTest { // Committed state is set back to the requested state as the override state is no longer // supported. assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE); - assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE); + assertEquals(mService.getBaseState(), DEFAULT_DEVICE_STATE); assertFalse(mService.getOverrideState().isPresent()); assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE.getIdentifier()); @@ -348,6 +375,10 @@ public final class DeviceStateManagerServiceTest { }); } + private static void assertArrayEquals(int[] expected, int[] actual) { + Assert.assertTrue(Arrays.equals(expected, actual)); + } + private static final class TestDeviceStatePolicy implements DeviceStatePolicy { private final DeviceStateProvider mProvider; private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE; @@ -429,12 +460,14 @@ public final class DeviceStateManagerServiceTest { public static final int STATUS_SUSPENDED = 2; public static final int STATUS_CANCELED = 3; - private Integer mLastNotifiedValue; + @Nullable + private DeviceStateInfo mLastNotifiedInfo; + private boolean mNotifiedOfChangeInSupportedStates; private final HashMap<IBinder, Integer> mLastNotifiedStatus = new HashMap<>(); @Override - public void onDeviceStateChanged(int deviceState) { - mLastNotifiedValue = deviceState; + public void onDeviceStateInfoChanged(DeviceStateInfo info) { + mLastNotifiedInfo = info; } @Override @@ -453,8 +486,8 @@ public final class DeviceStateManagerServiceTest { } @Nullable - Integer getLastNotifiedValue() { - return mLastNotifiedValue; + DeviceStateInfo getLastNotifiedInfo() { + return mLastNotifiedInfo; } int getLastNotifiedStatus(IBinder requestToken) { diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index 732c08c98f68..54825ee2745a 100644 --- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -30,7 +30,6 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.os.Handler; -import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -44,7 +43,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest -@Presubmit @RunWith(AndroidJUnit4.class) public class AutomaticBrightnessControllerTest { private static final float BRIGHTNESS_MIN_FLOAT = 0.0f; @@ -66,7 +64,6 @@ public class AutomaticBrightnessControllerTest { @Mock HysteresisLevels mAmbientBrightnessThresholds; @Mock HysteresisLevels mScreenBrightnessThresholds; @Mock Handler mNoOpHandler; - @Mock DisplayDeviceConfig mDisplayDeviceConfig; @Mock DisplayDevice mDisplayDevice; private static final int LIGHT_SENSOR_WARMUP_TIME = 0; diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java index 9396ed256409..285806b5dcd7 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java @@ -30,7 +30,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.hardware.display.BrightnessConfiguration; import android.os.PowerManager; -import android.platform.test.annotations.Presubmit; import android.util.MathUtils; import android.util.Spline; @@ -43,7 +42,6 @@ import org.junit.runner.RunWith; import java.util.Arrays; @SmallTest -@Presubmit @RunWith(AndroidJUnit4.class) public class BrightnessMappingStrategyTest { @@ -90,7 +88,9 @@ public class BrightnessMappingStrategyTest { }; private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f }; - private static final int[] BACKLIGHT_RANGE = { 1, 255 }; + private static final float[] DISPLAY_LEVELS_RANGE_NITS = { 13.25f, 478.5f }; + private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f }; + private static final float[] DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT = { 0.03149606299f, 1.0f }; private static final float[] EMPTY_FLOAT_ARRAY = new float[0]; private static final int[] EMPTY_INT_ARRAY = new int[0]; @@ -116,25 +116,28 @@ public class BrightnessMappingStrategyTest { }; private static final Spline GAMMA_CORRECTION_SPLINE = Spline.createSpline( new float[] { 0.0f, 100.0f, 1000.0f, 2500.0f, 4000.0f, 4900.0f, 5000.0f }, - new float[] { 0.035f, 0.035f, 0.221f, 0.523f, 0.797f, 0.980f, 1.0f }); + new float[] { 0.0475f, 0.0475f, 0.2225f, 0.5140f, 0.8056f, 0.9805f, 1.0f }); @Test public void testSimpleStrategyMappingAtControlPoints() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 0; i < LUX_LEVELS.length; i++) { - final float expectedLevel = - (float) DISPLAY_LEVELS_BACKLIGHT[i] / PowerManager.BRIGHTNESS_ON; + final float expectedLevel = MathUtils.map(PowerManager.BRIGHTNESS_OFF + 1, + PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX, DISPLAY_LEVELS_BACKLIGHT[i]); assertEquals(expectedLevel, - simple.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/); + simple.getBrightness(LUX_LEVELS[i]), 0.0001f /*tolerance*/); } } @Test public void testSimpleStrategyMappingBetweenControlPoints() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc); assertNotNull("BrightnessMappingStrategy should not be null", simple); for (int i = 1; i < LUX_LEVELS.length; i++) { final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; @@ -148,66 +151,71 @@ public class BrightnessMappingStrategyTest { @Test public void testSimpleStrategyIgnoresNewConfiguration() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); - final int N = LUX_LEVELS.length; final float[] lux = { 0f, 1f }; final float[] nits = { 0, PowerManager.BRIGHTNESS_ON }; BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) .build(); strategy.setBrightnessConfiguration(config); - assertNotEquals(1.0f, strategy.getBrightness(1f), 0.01 /*tolerance*/); + assertNotEquals(1.0f, strategy.getBrightness(1f), 0.0001f /*tolerance*/); } @Test public void testSimpleStrategyIgnoresNullConfiguration() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); strategy.setBrightnessConfiguration(null); final int N = DISPLAY_LEVELS_BACKLIGHT.length; final float expectedBrightness = (float) DISPLAY_LEVELS_BACKLIGHT[N - 1] / PowerManager.BRIGHTNESS_ON; assertEquals(expectedBrightness, - strategy.getBrightness(LUX_LEVELS[N - 1]), 0.01 /*tolerance*/); + strategy.getBrightness(LUX_LEVELS[N - 1]), 0.0001f /*tolerance*/); } @Test public void testPhysicalStrategyMappingAtControlPoints() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, - DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res); + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc); assertNotNull("BrightnessMappingStrategy should not be null", physical); for (int i = 0; i < LUX_LEVELS.length; i++) { - final float expectedLevel = DISPLAY_LEVELS_NITS[i] / DISPLAY_RANGE_NITS[1]; + final float expectedLevel = MathUtils.map(DISPLAY_RANGE_NITS[0], DISPLAY_RANGE_NITS[1], + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT[0], + DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT[1], + DISPLAY_LEVELS_NITS[i]); assertEquals(expectedLevel, - physical.getBrightness(LUX_LEVELS[i]), 0.01f /*tolerance*/); + physical.getBrightness(LUX_LEVELS[i]), + 0.0001f /*tolerance*/); } } @Test public void testPhysicalStrategyMappingBetweenControlPoints() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, - DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res); + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc); assertNotNull("BrightnessMappingStrategy should not be null", physical); - Spline backlightToBrightness = - Spline.createSpline(toFloatArray(BACKLIGHT_RANGE), DISPLAY_RANGE_NITS); + Spline brightnessToNits = + Spline.createSpline(BACKLIGHT_RANGE_ZERO_TO_ONE, DISPLAY_RANGE_NITS); for (int i = 1; i < LUX_LEVELS.length; i++) { - final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2; - final float backlight = physical.getBrightness(lux) * PowerManager.BRIGHTNESS_ON; - final float nits = backlightToBrightness.interpolate(backlight); - assertTrue("Desired brightness should be between adjacent control points.", + final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2.0f; + final float brightness = physical.getBrightness(lux); + final float nits = brightnessToNits.interpolate(brightness); + assertTrue("Desired brightness should be between adjacent control points: " + nits, nits > DISPLAY_LEVELS_NITS[i - 1] && nits < DISPLAY_LEVELS_NITS[i]); } } @Test public void testPhysicalStrategyUsesNewConfigurations() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, - DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); final float[] lux = { 0f, 1f }; final float[] nits = { @@ -218,46 +226,53 @@ public class BrightnessMappingStrategyTest { BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits) .build(); strategy.setBrightnessConfiguration(config); - assertEquals(1.0f, strategy.getBrightness(1f), 0.01 /*tolerance*/); + assertEquals(1.0f, strategy.getBrightness(1f), 0.0001f /*tolerance*/); // Check that null returns us to the default configuration. strategy.setBrightnessConfiguration(null); final int N = DISPLAY_LEVELS_NITS.length; final float expectedBrightness = DISPLAY_LEVELS_NITS[N - 1] / DISPLAY_RANGE_NITS[1]; assertEquals(expectedBrightness, - strategy.getBrightness(LUX_LEVELS[N - 1]), 0.01f /*tolerance*/); + strategy.getBrightness(LUX_LEVELS[N - 1]), 0.0001f /*tolerance*/); } @Test public void testPhysicalStrategyRecalculateSplines() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, - BACKLIGHT_RANGE); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length]; for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) { adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f; } // Default is unadjusted - assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */); - assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */); + assertEquals(DISPLAY_RANGE_NITS[0], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]), + 0.0001f /* tolerance */); + assertEquals(DISPLAY_RANGE_NITS[1], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]), + 0.0001f /* tolerance */); // When adjustment is turned on, adjustment array is used strategy.recalculateSplines(true, adjustedNits50p); - assertEquals(1.3425f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */); - assertEquals(239.25f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */); + assertEquals(DISPLAY_RANGE_NITS[0] / 2, + strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]), 0.0001f /* tolerance */); + assertEquals(DISPLAY_RANGE_NITS[1] / 2, + strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]), 0.0001f /* tolerance */); // When adjustment is turned off, adjustment array is ignored strategy.recalculateSplines(false, adjustedNits50p); - assertEquals(2.685f, strategy.convertToNits(BACKLIGHT_RANGE[0]), 0.01f /* tolerance */); - assertEquals(478.5f, strategy.convertToNits(BACKLIGHT_RANGE[1]), 0.01f /* tolerance */); + assertEquals(DISPLAY_RANGE_NITS[0], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]), + 0.0001f /* tolerance */); + assertEquals(DISPLAY_RANGE_NITS[1], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]), + 0.0001f /* tolerance */); } @Test public void testDefaultStrategyIsPhysical() { Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT, - DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy); } @@ -268,15 +283,15 @@ public class BrightnessMappingStrategyTest { int tmp = lux[idx]; lux[idx] = lux[idx+1]; lux[idx+1] = tmp; - Resources res = createResources(lux, DISPLAY_LEVELS_NITS, - DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + Resources res = createResources(lux, DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); assertNull(strategy); // And make sure we get the same result even if it's monotone but not increasing. lux[idx] = lux[idx+1]; - res = createResources(lux, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - strategy = BrightnessMappingStrategy.create(res); + res = createResources(lux, DISPLAY_LEVELS_NITS); + strategy = BrightnessMappingStrategy.create(res, ddc); assertNull(strategy); } @@ -287,13 +302,13 @@ public class BrightnessMappingStrategyTest { // Make sure it's strictly increasing so that the only failure is the differing array // lengths lux[lux.length - 1] = lux[lux.length - 2] + 1; - Resources res = createResources(lux, DISPLAY_LEVELS_NITS, - DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res); + Resources res = createResources(lux, DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc); assertNull(strategy); res = createResources(lux, DISPLAY_LEVELS_BACKLIGHT); - strategy = BrightnessMappingStrategy.create(res); + strategy = BrightnessMappingStrategy.create(res, ddc); assertNull(strategy); // Extra backlight level @@ -301,43 +316,45 @@ public class BrightnessMappingStrategyTest { DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1); backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1; res = createResources(LUX_LEVELS, backlight); - strategy = BrightnessMappingStrategy.create(res); + strategy = BrightnessMappingStrategy.create(res, ddc); assertNull(strategy); // Extra nits level final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1); nits[nits.length - 1] = nits[nits.length - 2] + 1; - res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - strategy = BrightnessMappingStrategy.create(res); + res = createResources(LUX_LEVELS, nits); + strategy = BrightnessMappingStrategy.create(res, ddc); assertNull(strategy); } @Test public void testPhysicalStrategyRequiresNitsMapping() { Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/, BACKLIGHT_RANGE); - BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res); + DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY /*nitsRange*/); + BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc); assertNull(physical); res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS, EMPTY_INT_ARRAY /*backlightRange*/); - physical = BrightnessMappingStrategy.create(res); + DISPLAY_LEVELS_NITS); + physical = BrightnessMappingStrategy.create(res, ddc); assertNull(physical); res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - DISPLAY_LEVELS_NITS, EMPTY_FLOAT_ARRAY /*nitsRange*/, - EMPTY_INT_ARRAY /*backlightRange*/); - physical = BrightnessMappingStrategy.create(res); + DISPLAY_LEVELS_NITS); + physical = BrightnessMappingStrategy.create(res, ddc); assertNull(physical); } @Test public void testStrategiesAdaptToUserDataPoint() { - Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, - DISPLAY_RANGE_NITS, BACKLIGHT_RANGE); - assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res)); + Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, + DISPLAY_LEVELS_NITS); + DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); + assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc)); + ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE); res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT); - assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res)); + assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc)); } private static void assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy strategy) { @@ -353,7 +370,7 @@ public class BrightnessMappingStrategyTest { // Then make sure that all control points after the middle lux level are also set to max... for (int i = idx; i < LUX_LEVELS.length; i++) { - assertEquals(strategy.getBrightness(LUX_LEVELS[idx]), 1.0, 0.01 /*tolerance*/); + assertEquals(strategy.getBrightness(LUX_LEVELS[idx]), 1.0, 0.0001f /*tolerance*/); } // ...and that all control points before the middle lux level are strictly less than the @@ -371,12 +388,12 @@ public class BrightnessMappingStrategyTest { strategy.clearUserDataPoints(); for (int i = 0; i < LUX_LEVELS.length; i++) { assertEquals(initialBrightnessLevels[i], strategy.getBrightness(LUX_LEVELS[i]), - 0.01 /*tolerance*/); + 0.0001f /*tolerance*/); } // Now set the middle of the lux range to something just above the minimum. float minBrightness = strategy.getBrightness(LUX_LEVELS[0]); - strategy.addUserDataPoint(LUX_LEVELS[idx], minBrightness + 0.01f); + strategy.addUserDataPoint(LUX_LEVELS[idx], minBrightness + 0.0001f); // Then make sure the curve is still monotonic. prevBrightness = 0f; @@ -391,31 +408,21 @@ public class BrightnessMappingStrategyTest { // be true assuming that there are more than two lux levels in the curve since we picked a // brightness just barely above the minimum for the middle of the curve. minBrightness = (float) MathUtils.pow(minBrightness, MAXIMUM_GAMMA); // Gamma correction. - assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.01 /*tolerance*/); - } - - private static float[] toFloatArray(int[] vals) { - float[] newVals = new float[vals.length]; - for (int i = 0; i < vals.length; i++) { - newVals[i] = (float) vals[i]; - } - return newVals; + assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.0001f /*tolerance*/); } private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight) { return createResources(luxLevels, brightnessLevelsBacklight, - EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/, EMPTY_FLOAT_ARRAY /*nitsRange*/, - EMPTY_INT_ARRAY /*backlightRange*/); + EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/); } - private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits, - float[] nitsRange, int[] backlightRange) { + private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits) { return createResources(luxLevels, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/, - brightnessLevelsNits, nitsRange, backlightRange); + brightnessLevelsNits); } private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight, - float[] brightnessLevelsNits, float[] nitsRange, int[] backlightRange) { + float[] brightnessLevelsNits) { Resources mockResources = mock(Resources.class); // For historical reasons, the lux levels resource implicitly defines the first point as 0, // so we need to chop it off of the array the mock resource object returns. @@ -432,15 +439,6 @@ public class BrightnessMappingStrategyTest { com.android.internal.R.array.config_autoBrightnessDisplayValuesNits)) .thenReturn(mockBrightnessLevelNits); - TypedArray mockNitsRange = createFloatTypedArray(nitsRange); - when(mockResources.obtainTypedArray( - com.android.internal.R.array.config_screenBrightnessNits)) - .thenReturn(mockNitsRange); - - when(mockResources.getIntArray( - com.android.internal.R.array.config_screenBrightnessBacklight)) - .thenReturn(backlightRange); - when(mockResources.getInteger( com.android.internal.R.integer.config_screenBrightnessSettingMinimum)) .thenReturn(1); @@ -453,6 +451,21 @@ public class BrightnessMappingStrategyTest { return mockResources; } + private DisplayDeviceConfig createDdc() { + return createDdc(DISPLAY_RANGE_NITS); + } + + private DisplayDeviceConfig createDdc(float[] nitsArray) { + return createDdc(nitsArray, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT); + } + + private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray) { + DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class); + when(mockDdc.getNits()).thenReturn(nitsArray); + when(mockDdc.getBrightness()).thenReturn(backlightArray); + return mockDdc; + } + private TypedArray createFloatTypedArray(float[] vals) { TypedArray mockArray = mock(TypedArray.class); when(mockArray.length()).thenAnswer(invocation -> { @@ -490,21 +503,22 @@ public class BrightnessMappingStrategyTest { final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1); final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3); - Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, - DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); + + Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc); // Let's start with a validity check: - assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */); - assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */); - assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */); + assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */); + assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(y3, strategy.getBrightness(x3), 0.0001f /* tolerance */); // OK, let's roll: float gamma = 0.5f; strategy.addUserDataPoint(x2, (float) MathUtils.pow(y2, gamma)); - assertEquals(MathUtils.pow(y1, gamma), strategy.getBrightness(x1), 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y3, gamma), strategy.getBrightness(x3), 0.01f /* tolerance */); - // The adjustment should be +0.63 (manual calculation). - assertEquals(+0.63f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + assertEquals(MathUtils.pow(y1, gamma), strategy.getBrightness(x1), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y3, gamma), strategy.getBrightness(x3), 0.0001f /* tolerance */); + // The adjustment should be +0.6308 (manual calculation). + assertEquals(+0.6308f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); } @Test @@ -518,39 +532,39 @@ public class BrightnessMappingStrategyTest { final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1); final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3); - Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, - DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); + Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc); // Validity check: - assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */); - assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */); - assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */); + assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */); + assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(y3, strategy.getBrightness(x3), 0.0001f /* tolerance */); // Let's roll: float gamma = 0.25f; final float minGamma = 1.0f / MAXIMUM_GAMMA; strategy.addUserDataPoint(x2, (float) MathUtils.pow(y2, gamma)); - assertEquals(MathUtils.pow(y1, minGamma), strategy.getBrightness(x1), - 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), - 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y3, minGamma), strategy.getBrightness(x3), - 0.01f /* tolerance */); + assertEquals(MathUtils.pow(y1, minGamma), + strategy.getBrightness(x1), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y2, gamma), + strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y3, minGamma), + strategy.getBrightness(x3), 0.0001f /* tolerance */); // The adjustment should be +1.0 (maximum adjustment). - assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); } @Test public void testGammaCorrectionExtremeChangeAtCenter() { // Extreme changes (e.g. setting brightness to 0.0 or 1.0) can't be gamma corrected, so we // just make sure the adjustment reflects the change. - Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, - DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); - assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc); + assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); strategy.addUserDataPoint(2500, 1.0f); - assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); strategy.addUserDataPoint(2500, 0.0f); - assertEquals(-1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + assertEquals(-1.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); } @Test @@ -564,28 +578,28 @@ public class BrightnessMappingStrategyTest { final float y0 = GAMMA_CORRECTION_SPLINE.interpolate(x0); final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2); final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4); - Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS, - DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT); - BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources); + Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS); + DisplayDeviceConfig ddc = createDdc(); + BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc); // Validity, as per tradition: - assertEquals(y0, strategy.getBrightness(x0), 0.01f /* tolerance */); - assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */); - assertEquals(y4, strategy.getBrightness(x4), 0.01f /* tolerance */); + assertEquals(y0, strategy.getBrightness(x0), 0.0001f /* tolerance */); + assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(y4, strategy.getBrightness(x4), 0.0001f /* tolerance */); // Rollin': float adjustment = 0.3f; float gamma = (float) MathUtils.pow(MAXIMUM_GAMMA, -adjustment); strategy.addUserDataPoint(x0, y0 + adjustment); - assertEquals(y0 + adjustment, strategy.getBrightness(x0), 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y4, gamma), strategy.getBrightness(x4), 0.01f /* tolerance */); - assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + assertEquals(y0 + adjustment, strategy.getBrightness(x0), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y4, gamma), strategy.getBrightness(x4), 0.0001f /* tolerance */); + assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); // Similarly, if we set a user data point at (x4, 1.0), the adjustment should be 1 - y4. adjustment = 1.0f - y4; gamma = (float) MathUtils.pow(MAXIMUM_GAMMA, -adjustment); strategy.addUserDataPoint(x4, 1.0f); - assertEquals(MathUtils.pow(y0, gamma), strategy.getBrightness(x0), 0.01f /* tolerance */); - assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */); - assertEquals(1.0f, strategy.getBrightness(x4), 0.01f /* tolerance */); - assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */); + assertEquals(MathUtils.pow(y0, gamma), strategy.getBrightness(x0), 0.0001f /* tolerance */); + assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.0001f /* tolerance */); + assertEquals(1.0f, strategy.getBrightness(x4), 0.0001f /* tolerance */); + assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */); } } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index 1c55072cab2c..bc86d1d39b1c 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -993,7 +993,7 @@ public class DisplayManagerServiceTest { private DisplayDeviceInfo mDisplayDeviceInfo; FakeDisplayDevice() { - super(null, null, ""); + super(null, null, "", mContext); } public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) { 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 ee0f2e8c6f6a..81b2381cd629 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -47,7 +47,6 @@ import android.hardware.SensorManager; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.Looper; -import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; import android.provider.Settings; import android.test.mock.MockContentResolver; @@ -83,7 +82,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @SmallTest -@Presubmit @RunWith(AndroidJUnit4.class) public class DisplayModeDirectorTest { // The tolerance within which we consider something approximately equals. diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java index d72606ac44e5..196454bd32ce 100644 --- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java @@ -22,7 +22,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.hardware.display.BrightnessConfiguration; -import android.platform.test.annotations.Presubmit; import android.util.Pair; import androidx.test.filters.SmallTest; @@ -41,7 +40,6 @@ import java.io.OutputStream; import java.nio.charset.StandardCharsets; @SmallTest -@Presubmit @RunWith(AndroidJUnit4.class) public class PersistentDataStoreTest { private PersistentDataStore mDataStore; diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java index 27fce3c37fd9..912da9414165 100644 --- a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java @@ -45,7 +45,7 @@ public final class PersistentSystemFontConfigTest { public void testWriteRead() throws Exception { long expectedModifiedDate = 1234567890; PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); - config.lastModifiedDate = expectedModifiedDate; + config.lastModifiedMillis = expectedModifiedDate; config.updatedFontDirs.add("~~abc"); config.updatedFontDirs.add("~~def"); @@ -65,7 +65,7 @@ public final class PersistentSystemFontConfigTest { PersistentSystemFontConfig.Config another = new PersistentSystemFontConfig.Config(); PersistentSystemFontConfig.loadFromXml(bais, another); - assertThat(another.lastModifiedDate).isEqualTo(expectedModifiedDate); + assertThat(another.lastModifiedMillis).isEqualTo(expectedModifiedDate); assertThat(another.updatedFontDirs).containsExactly("~~abc", "~~def"); assertThat(another.fontFamilies).containsExactly(fontFamily); } @@ -82,7 +82,7 @@ public final class PersistentSystemFontConfigTest { new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) { PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); PersistentSystemFontConfig.loadFromXml(bais, config); - assertThat(config.lastModifiedDate).isEqualTo(0); + assertThat(config.lastModifiedMillis).isEqualTo(0); } } diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java index 7771afc8c7f1..843296e31800 100644 --- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java +++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java @@ -151,7 +151,7 @@ public final class UpdatableFontDirTest { FakeFontFileParser parser = new FakeFontFileParser(); FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil(); PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); - config.lastModifiedDate = expectedModifiedDate; + config.lastModifiedMillis = expectedModifiedDate; writeConfig(config, mConfigFile); UpdatableFontDir dirForPreparation = new UpdatableFontDir( mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil, @@ -507,7 +507,7 @@ public final class UpdatableFontDirTest { File readonlyFile = new File(readonlyDir, "readonly_config.xml"); PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config(); - config.lastModifiedDate = expectedModifiedDate; + config.lastModifiedMillis = expectedModifiedDate; writeConfig(config, readonlyFile); assertThat(readonlyDir.setWritable(false, false)).isTrue(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java index f49cbca12fd8..9bf95c0edcdb 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java @@ -221,7 +221,8 @@ public class DeviceSelectActionTest { "testDeviceSelect"); action.start(); mTestLooper.dispatchAll(); - assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + mNativeWrapper.clearResultMessages(); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); action.processCommand(REPORT_POWER_STATUS_ON); mTestLooper.dispatchAll(); @@ -238,12 +239,15 @@ public class DeviceSelectActionTest { DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, /*isCec20=*/false); action.start(); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); action.processCommand(REPORT_POWER_STATUS_STANDBY); mTestLooper.dispatchAll(); HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed( ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER); assertThat(mNativeWrapper.getResultMessages()).contains(userControlPressed); + 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); @@ -261,6 +265,9 @@ public class DeviceSelectActionTest { DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, /*isCec20=*/false); action.start(); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + mNativeWrapper.clearResultMessages(); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); action.processCommand(REPORT_POWER_STATUS_STANDBY); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); @@ -285,6 +292,9 @@ public class DeviceSelectActionTest { DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback, /*isCec20=*/false); action.start(); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + mNativeWrapper.clearResultMessages(); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); action.processCommand(REPORT_POWER_STATUS_STANDBY); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON); @@ -330,8 +340,11 @@ public class DeviceSelectActionTest { action.start(); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH); + mNativeWrapper.clearResultMessages(); assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS); action.processCommand(REPORT_POWER_STATUS_ON); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH); assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); } @@ -354,9 +367,12 @@ public class DeviceSelectActionTest { HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed( ADDR_TV, ADDR_PLAYBACK_1, HdmiCecKeycode.CEC_KEYCODE_POWER); assertThat(mNativeWrapper.getResultMessages()).doesNotContain(userControlPressed); + 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); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH); assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS); } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java index fcbd89781de4..1958cb01b510 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java @@ -58,6 +58,7 @@ final class FakeNativeWrapper implements NativeWrapper { private int mMyPhysicalAddress = 0; private HdmiPortInfo[] mHdmiPortInfo = null; private HdmiCecController.HdmiCecCallback mCallback = null; + private int mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0; @Override public String nativeInit() { @@ -96,7 +97,7 @@ final class FakeNativeWrapper implements NativeWrapper { @Override public int nativeGetVersion() { - return HdmiControlManager.HDMI_CEC_VERSION_2_0; + return mCecVersion; } @Override @@ -132,6 +133,10 @@ final class FakeNativeWrapper implements NativeWrapper { mPortConnectionStatus.put(port, connected); } + public void setCecVersion(@HdmiControlManager.HdmiCecVersion int cecVersion) { + mCecVersion = cecVersion; + } + public void onCecMessage(HdmiCecMessage hdmiCecMessage) { if (mCallback == null) { return; diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index be584d7b4591..32a70480c39e 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -49,6 +49,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -484,17 +485,6 @@ public class HdmiControlServiceTest { } @Test - public void getCecVersion_default() { - // Set the Settings value to "null" to emulate it being empty and force the default value. - Settings.Global.putString(mContextSpy.getContentResolver(), - Settings.Global.HDMI_CEC_VERSION, - null); - mHdmiControlService.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); - assertThat(mHdmiControlService.getCecVersion()).isEqualTo( - HdmiControlManager.HDMI_CEC_VERSION_1_4_B); - } - - @Test public void getCecVersion_1_4() { mHdmiControlService.getHdmiCecConfig().setIntValue( HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, @@ -622,6 +612,45 @@ public class HdmiControlServiceTest { assertEquals(runnerUid, Binder.getCallingWorkSourceUid()); } + @Ignore("b/180499471") + @Test + public void initCecVersion_limitToMinimumSupportedVersion() { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_1_4_B); + + mHdmiControlService.initService(); + assertThat(mHdmiControlService.getCecVersion()).isEqualTo( + HdmiControlManager.HDMI_CEC_VERSION_1_4_B); + } + + @Ignore("b/180499471") + @Test + public void initCecVersion_limitToAtLeast1_4() { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + mNativeWrapper.setCecVersion(0x0); + + mHdmiControlService.initService(); + assertThat(mHdmiControlService.getCecVersion()).isEqualTo( + HdmiControlManager.HDMI_CEC_VERSION_1_4_B); + } + + @Ignore("b/180499471") + @Test + public void initCecVersion_useHighestMatchingVersion() { + mHdmiControlService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION, + HdmiControlManager.HDMI_CEC_VERSION_2_0); + mNativeWrapper.setCecVersion(HdmiControlManager.HDMI_CEC_VERSION_2_0); + + mHdmiControlService.initService(); + assertThat(mHdmiControlService.getCecVersion()).isEqualTo( + HdmiControlManager.HDMI_CEC_VERSION_2_0); + } + private static class VolumeControlFeatureCallback extends IHdmiCecVolumeControlFeatureListener.Stub { boolean mCallbackReceived = false; diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java index 3e9709d55268..f0a9a0089ec9 100644 --- a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java @@ -31,6 +31,7 @@ import android.hardware.light.ILights; import android.hardware.lights.Light; import android.hardware.lights.LightState; import android.hardware.lights.LightsManager; +import android.hardware.lights.SystemLightsManager; import android.os.Looper; import androidx.test.filters.SmallTest; @@ -93,7 +94,7 @@ public class LightsServiceTest { @Test public void testGetLights_filtersSystemLights() { LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper()); - LightsManager manager = new LightsManager(mContext, service.mManagerService); + LightsManager manager = new SystemLightsManager(mContext, service.mManagerService); // When lights are listed, only the 4 MICROPHONE lights should be visible. assertThat(manager.getLights().size()).isEqualTo(4); @@ -102,14 +103,14 @@ public class LightsServiceTest { @Test public void testControlMultipleLights() { LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper()); - LightsManager manager = new LightsManager(mContext, service.mManagerService); + LightsManager manager = new SystemLightsManager(mContext, service.mManagerService); // When the session requests to turn 3/4 lights on: LightsManager.LightsSession session = manager.openSession(); session.requestLights(new Builder() - .setLight(manager.getLights().get(0), new LightState(0xf1)) - .setLight(manager.getLights().get(1), new LightState(0xf2)) - .setLight(manager.getLights().get(2), new LightState(0xf3)) + .addLight(manager.getLights().get(0), new LightState(0xf1)) + .addLight(manager.getLights().get(1), new LightState(0xf2)) + .addLight(manager.getLights().get(2), new LightState(0xf3)) .build()); // Then all 3 should turn on. @@ -122,9 +123,9 @@ public class LightsServiceTest { } @Test - public void testControlLights_onlyEffectiveForLifetimeOfClient() { + public void testControlLights_onlyEffectiveForLifetimeOfClient() throws Exception { LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper()); - LightsManager manager = new LightsManager(mContext, service.mManagerService); + LightsManager manager = new SystemLightsManager(mContext, service.mManagerService); Light micLight = manager.getLights().get(0); // The light should begin by being off. @@ -132,38 +133,41 @@ public class LightsServiceTest { // When a session commits changes: LightsManager.LightsSession session = manager.openSession(); - session.requestLights(new Builder().setLight(micLight, new LightState(GREEN)).build()); + session.requestLights(new Builder().addLight(micLight, new LightState(GREEN)).build()); // Then the light should turn on. assertThat(manager.getLightState(micLight).getColor()).isEqualTo(GREEN); // When the session goes away: session.close(); + // Then the light should turn off. assertThat(manager.getLightState(micLight).getColor()).isEqualTo(TRANSPARENT); } @Test - public void testControlLights_firstCallerWinsContention() { + public void testControlLights_firstCallerWinsContention() throws Exception { LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper()); - LightsManager manager = new LightsManager(mContext, service.mManagerService); + LightsManager manager = new SystemLightsManager(mContext, service.mManagerService); Light micLight = manager.getLights().get(0); LightsManager.LightsSession session1 = manager.openSession(); LightsManager.LightsSession session2 = manager.openSession(); // When session1 and session2 both request the same light: - session1.requestLights(new Builder().setLight(micLight, new LightState(BLUE)).build()); - session2.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build()); + session1.requestLights(new Builder().addLight(micLight, new LightState(BLUE)).build()); + session2.requestLights(new Builder().addLight(micLight, new LightState(WHITE)).build()); // Then session1 should win because it was created first. assertThat(manager.getLightState(micLight).getColor()).isEqualTo(BLUE); // When session1 goes away: session1.close(); + // Then session2 should have its request go into effect. assertThat(manager.getLightState(micLight).getColor()).isEqualTo(WHITE); // When session2 goes away: session2.close(); + // Then the light should turn off because there are no more sessions. assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0); } @@ -171,12 +175,12 @@ public class LightsServiceTest { @Test public void testClearLight() { LightsService service = new LightsService(mContext, () -> mHal, Looper.getMainLooper()); - LightsManager manager = new LightsManager(mContext, service.mManagerService); + LightsManager manager = new SystemLightsManager(mContext, service.mManagerService); Light micLight = manager.getLights().get(0); // When the session turns a light on: LightsManager.LightsSession session = manager.openSession(); - session.requestLights(new Builder().setLight(micLight, new LightState(WHITE)).build()); + session.requestLights(new Builder().addLight(micLight, new LightState(WHITE)).build()); // And then the session clears it again: session.requestLights(new Builder().clearLight(micLight).build()); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java index 73191dca6093..4a42940832c4 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java @@ -150,7 +150,7 @@ public class LockSettingsServiceTestable extends LockSettingsService { } @Override - public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) { + public RecoverableKeyStoreManager getRecoverableKeyStoreManager() { return mRecoverableKeyStoreManager; } diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 3ebe4efee013..74bf4f5da70d 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -115,7 +115,6 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkPolicy; -import android.net.NetworkPolicyManager; import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; @@ -386,8 +385,7 @@ public class NetworkPolicyManagerServiceTest { Log.d(TAG, "set mUidObserver to " + mUidObserver); return null; } - }).when(mActivityManager).registerUidObserver(any(), anyInt(), - eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), any(String.class)); + }).when(mActivityManager).registerUidObserver(any(), anyInt(), anyInt(), any(String.class)); mFutureIntent = newRestrictBackgroundChangedFuture(); mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, @@ -2043,7 +2041,7 @@ public class NetworkPolicyManagerServiceTest { final NetworkCapabilities networkCapabilities = new NetworkCapabilities(); networkCapabilities.addTransportType(TRANSPORT_WIFI); networkCapabilities.setSSID(TEST_SSID); - return new NetworkState(TYPE_WIFI, prop, networkCapabilities, null, null, TEST_SSID); + return new NetworkState(TYPE_WIFI, prop, networkCapabilities, null, null); } private void expectHasInternetPermission(int uid, boolean hasIt) throws Exception { @@ -2067,7 +2065,7 @@ public class NetworkPolicyManagerServiceTest { new NetworkState(TYPE_MOBILE, buildLinkProperties(TEST_IFACE), buildNetworkCapabilities(TEST_SUB_ID, roaming), - new Network(TEST_NET_ID), TEST_IMSI, null) + new Network(TEST_NET_ID), TEST_IMSI) }); } diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index e46ab6b01f0a..029e9a39ea4b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -45,6 +45,13 @@ import android.app.ActivityManagerInternal; import android.app.IUidObserver; import android.app.Person; import android.app.admin.DevicePolicyManager; +import android.app.appsearch.AppSearchBatchResult; +import android.app.appsearch.AppSearchManager; +import android.app.appsearch.AppSearchResult; +import android.app.appsearch.IAppSearchBatchResultCallback; +import android.app.appsearch.IAppSearchManager; +import android.app.appsearch.IAppSearchResultCallback; +import android.app.appsearch.PackageIdentifier; import android.app.role.OnRoleHoldersChangedListener; import android.app.usage.UsageStatsManagerInternal; import android.content.ActivityNotFoundException; @@ -78,6 +85,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.FileUtils; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.PersistableBundle; import android.os.Process; @@ -150,6 +158,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { return mMockUserManager; case Context.DEVICE_POLICY_SERVICE: return mMockDevicePolicyManager; + case Context.APP_SEARCH_SERVICE: + return new AppSearchManager(getTestContext(), mMockAppSearchManager); case Context.ROLE_SERVICE: // RoleManager is final and cannot be mocked, so we only override the inject // accessor methods in ShortcutService. @@ -159,6 +169,11 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } @Override + public String getOpPackageName() { + return getTestContext().getOpPackageName(); + } + + @Override public String getSystemServiceName(Class<?> serviceClass) { return getTestContext().getSystemServiceName(serviceClass); } @@ -601,6 +616,123 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } } + protected class MockAppSearchManager implements IAppSearchManager { + + protected Map<String, List<PackageIdentifier>> mSchemasPackageAccessible = + new ArrayMap<>(1); + + @Override + public void setSchema(String packageName, String databaseName, List<Bundle> schemaBundles, + List<String> schemasNotPlatformSurfaceable, + Map<String, List<Bundle>> schemasPackageAccessibleBundles, boolean forceOverride, + int userId, IAppSearchResultCallback callback) throws RemoteException { + for (Map.Entry<String, List<Bundle>> entry : + schemasPackageAccessibleBundles.entrySet()) { + final String key = entry.getKey(); + final List<PackageIdentifier> packageIdentifiers; + if (!mSchemasPackageAccessible.containsKey(key)) { + packageIdentifiers = new ArrayList<>(entry.getValue().size()); + mSchemasPackageAccessible.put(key, packageIdentifiers); + } else { + packageIdentifiers = mSchemasPackageAccessible.get(key); + } + for (int i = 0; i < entry.getValue().size(); i++) { + packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i))); + } + } + callback.onResult(AppSearchResult.newSuccessfulResult(null)); + } + + @Override + public void getSchema(String packageName, String databaseName, int userId, + IAppSearchResultCallback callback) throws RemoteException { + ignore(callback); + } + + @Override + public void putDocuments(String packageName, String databaseName, + List<Bundle> documentBundles, int userId, IAppSearchBatchResultCallback callback) + throws RemoteException { + ignore(callback); + } + + @Override + public void getDocuments(String packageName, String databaseName, String namespace, + List<String> uris, Map<String, List<String>> typePropertyPaths, int userId, + IAppSearchBatchResultCallback callback) throws RemoteException { + ignore(callback); + } + + @Override + public void query(String packageName, String databaseName, String queryExpression, + Bundle searchSpecBundle, int userId, IAppSearchResultCallback callback) + throws RemoteException { + ignore(callback); + } + + @Override + public void globalQuery(String packageName, String queryExpression, Bundle searchSpecBundle, + int userId, IAppSearchResultCallback callback) throws RemoteException { + ignore(callback); + } + + @Override + public void getNextPage(long nextPageToken, int userId, IAppSearchResultCallback callback) + throws RemoteException { + ignore(callback); + } + + @Override + public void invalidateNextPageToken(long nextPageToken, int userId) throws RemoteException { + + } + + @Override + public void reportUsage(String packageName, String databaseName, String namespace, + String uri, long usageTimeMillis, int userId, IAppSearchResultCallback callback) + throws RemoteException { + ignore(callback); + } + + @Override + public void removeByUri(String packageName, String databaseName, String namespace, + List<String> uris, int userId, IAppSearchBatchResultCallback callback) + throws RemoteException { + ignore(callback); + } + + @Override + public void removeByQuery(String packageName, String databaseName, String queryExpression, + Bundle searchSpecBundle, int userId, IAppSearchResultCallback callback) + throws RemoteException { + ignore(callback); + } + + @Override + public void persistToDisk(int userId) throws RemoteException { + + } + + @Override + public void initialize(int userId, IAppSearchResultCallback callback) + throws RemoteException { + ignore(callback); + } + + @Override + public IBinder asBinder() { + return null; + } + + private void ignore(IAppSearchResultCallback callback) throws RemoteException { + callback.onResult(AppSearchResult.newSuccessfulResult(null)); + } + + private void ignore(IAppSearchBatchResultCallback callback) throws RemoteException { + callback.onResult(new AppSearchBatchResult.Builder().build()); + } + } + public static class ShortcutActivity extends Activity { } @@ -652,6 +784,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected PackageManagerInternal mMockPackageManagerInternal; protected UserManager mMockUserManager; protected DevicePolicyManager mMockDevicePolicyManager; + protected MockAppSearchManager mMockAppSearchManager; protected UserManagerInternal mMockUserManagerInternal; protected UsageStatsManagerInternal mMockUsageStatsManagerInternal; protected ActivityManagerInternal mMockActivityManagerInternal; @@ -801,6 +934,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { mMockPackageManagerInternal = mock(PackageManagerInternal.class); mMockUserManager = mock(UserManager.class); mMockDevicePolicyManager = mock(DevicePolicyManager.class); + mMockAppSearchManager = new MockAppSearchManager(); mMockUserManagerInternal = mock(UserManagerInternal.class); mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class); mMockActivityManagerInternal = mock(ActivityManagerInternal.class); diff --git a/services/tests/servicestests/src/com/android/server/pm/OWNERS b/services/tests/servicestests/src/com/android/server/pm/OWNERS index d825dfd7cf00..e15b5f57069c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/OWNERS +++ b/services/tests/servicestests/src/com/android/server/pm/OWNERS @@ -1 +1,3 @@ include /services/core/java/com/android/server/pm/OWNERS + +per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java new file mode 100644 index 000000000000..b17085ee0317 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java @@ -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 com.android.server.pm; + +import android.app.appsearch.PackageIdentifier; +import android.content.pm.AppSearchShortcutInfo; + +import java.util.Random; + +/** + * Tests for {@link android.app.appsearch.AppSearchManager} and relevant APIs in ShortcutManager. + * + atest -c com.android.server.pm.ShortcutManagerTest12 + */ +public class ShortcutManagerTest12 extends BaseShortcutManagerTest { + + public void testUpdateShortcutVisibility_updatesShortcutSchema() { + + final byte[] cert = new byte[20]; + new Random().nextBytes(cert); + + runWithCaller(CALLING_PACKAGE_1, USER_0, () -> { + mManager.updateShortcutVisibility(CALLING_PACKAGE_2, cert, true); + assertTrue(mMockAppSearchManager.mSchemasPackageAccessible.containsKey( + AppSearchShortcutInfo.SCHEMA_TYPE)); + assertTrue(mMockAppSearchManager.mSchemasPackageAccessible.get( + AppSearchShortcutInfo.SCHEMA_TYPE).get(0).equals( + new PackageIdentifier(CALLING_PACKAGE_2, cert))); + }); + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index ff43da6370e8..ee0a16a70265 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -86,6 +86,7 @@ public class DexManagerTests { private TestData mBarUser0DelegateLastClassLoader; private TestData mSystemServerJar; + private TestData mSystemServerJarUpdatedContext; private TestData mSystemServerJarInvalid; private int mUser0; @@ -113,6 +114,8 @@ public class DexManagerTests { mSystemServerJar = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME); mSystemServerJarInvalid = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME); + mSystemServerJarUpdatedContext = new TestData("android", isa, mUser0, + DELEGATE_LAST_CLASS_LOADER_NAME); mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock); @@ -522,6 +525,24 @@ public class DexManagerTests { } @Test + public void testSystemServerOverwritesContext() { + // Record bar secondaries with the default PathClassLoader. + List<String> secondaries = mSystemServerJar.getSecondaryDexPaths(); + + notifyDexLoad(mSystemServerJar, secondaries, mUser0); + PackageUseInfo pui = getPackageUseInfo(mSystemServerJar); + assertSecondaryUse(mSystemServerJar, pui, secondaries, /*isUsedByOtherApps*/false, mUser0); + + // Record bar secondaries again with a different class loader. This will change the context. + notifyDexLoad(mSystemServerJarUpdatedContext, secondaries, mUser0); + + pui = getPackageUseInfo(mSystemServerJar); + // We expect that all the contexts to be updated according to the last notify. + assertSecondaryUse(mSystemServerJarUpdatedContext, pui, secondaries, + /*isUsedByOtherApps*/false, mUser0); + } + + @Test public void testNotifyUnsupportedClassLoaderDoesNotChangeExisting() { List<String> secondaries = mBarUser0.getSecondaryDexPaths(); diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/OWNERS b/services/tests/servicestests/src/com/android/server/pm/dex/OWNERS new file mode 100644 index 000000000000..66ef75d6c823 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/dex/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/pm/dex/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java index adf4551e79a8..3450710f60a0 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java @@ -451,7 +451,7 @@ public class PackageDexUsageTests { "PCL[new_context.dex]"); assertTrue(record(fooSecondary1User0NewContext)); - // Not check that the context was switch to variable. + // Now check that the context was switch to variable. TestData expectedContext = mFooSecondary1User0.updateClassLoaderContext( PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT); @@ -461,6 +461,22 @@ public class PackageDexUsageTests { } @Test + public void testRecordClassLoaderContextOverwritten() { + // Record a secondary dex file. + assertTrue(record(mFooSecondary1User0)); + // Now update its context. + TestData fooSecondary1User0NewContext = mFooSecondary1User0.updateClassLoaderContext( + "PCL[new_context.dex]", true); + assertTrue(record(fooSecondary1User0NewContext)); + + // Now check that the context was overwritten. + TestData expectedContext = mFooSecondary1User0.updateClassLoaderContext( + "PCL[new_context.dex]", true); + + assertPackageDexUsage(null, expectedContext); + } + + @Test public void testDexUsageClassLoaderContext() { final boolean isUsedByOtherApps = false; final int userId = 0; @@ -642,8 +658,9 @@ public class PackageDexUsageTests { private boolean record(TestData testData) { return mPackageDexUsage.record(testData.mPackageName, testData.mDexFile, - testData.mOwnerUserId, testData.mLoaderIsa, - testData.mPrimaryOrSplit, testData.mUsedBy, testData.mClassLoaderContext); + testData.mOwnerUserId, testData.mLoaderIsa, + testData.mPrimaryOrSplit, testData.mUsedBy, testData.mClassLoaderContext, + testData.mOverwriteCLC); } private boolean record(PackageDexUsage packageDexUsage, TestData testData, Set<String> users) { @@ -651,7 +668,8 @@ public class PackageDexUsageTests { for (String user : users) { result = result && packageDexUsage.record(testData.mPackageName, testData.mDexFile, testData.mOwnerUserId, testData.mLoaderIsa, - testData.mPrimaryOrSplit, user, testData.mClassLoaderContext); + testData.mPrimaryOrSplit, user, testData.mClassLoaderContext, + testData.mOverwriteCLC); } return result; } @@ -682,15 +700,16 @@ public class PackageDexUsageTests { private final boolean mPrimaryOrSplit; private final String mUsedBy; private final String mClassLoaderContext; + private final boolean mOverwriteCLC; private TestData(String packageName, String dexFile, int ownerUserId, String loaderIsa, boolean primaryOrSplit, String usedBy) { this(packageName, dexFile, ownerUserId, loaderIsa, primaryOrSplit, - usedBy, "PCL[" + dexFile + "]"); + usedBy, "PCL[" + dexFile + "]", false); } private TestData(String packageName, String dexFile, int ownerUserId, String loaderIsa, boolean primaryOrSplit, String usedBy, - String classLoaderContext) { + String classLoaderContext, boolean overwriteCLC) { mPackageName = packageName; mDexFile = dexFile; mOwnerUserId = ownerUserId; @@ -698,16 +717,21 @@ public class PackageDexUsageTests { mPrimaryOrSplit = primaryOrSplit; mUsedBy = usedBy; mClassLoaderContext = classLoaderContext; + mOverwriteCLC = overwriteCLC; } private TestData updateClassLoaderContext(String newContext) { + return updateClassLoaderContext(newContext, mOverwriteCLC); + } + + private TestData updateClassLoaderContext(String newContext, boolean overwriteCLC) { return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, - mPrimaryOrSplit, mUsedBy, newContext); + mPrimaryOrSplit, mUsedBy, newContext, overwriteCLC); } private TestData updateUsedBy(String newUsedBy) { return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, - mPrimaryOrSplit, newUsedBy, mClassLoaderContext); + mPrimaryOrSplit, newUsedBy, mClassLoaderContext, mOverwriteCLC); } private boolean isUsedByOtherApps() { diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java index fb13d8707a44..c2b385889802 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java @@ -17,10 +17,10 @@ package com.android.server.pm.parsing; 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.junit.Assert.fail; import android.apex.ApexInfo; import android.content.Context; @@ -39,6 +39,7 @@ import android.os.Build; import android.os.Bundle; import android.os.FileUtils; import android.platform.test.annotations.Presubmit; +import android.util.Pair; import android.util.SparseIntArray; import androidx.test.InstrumentationRegistry; @@ -50,12 +51,17 @@ import com.android.internal.util.ArrayUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.ParsedPackage; +import com.google.common.truth.Expect; + +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; import java.io.InputStream; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.function.Function; /** @@ -89,6 +95,8 @@ public class PackageParserLegacyCoreTest { private static final int PLATFORM_VERSION = 20; private static final int NEWER_VERSION = 30; + @Rule public final Expect expect = Expect.create(); + private void verifyComputeMinSdkVersion(int minSdkVersion, String minSdkCodename, boolean isPlatformReleased, int expectedMinSdk) { final String[] outError = new String[1]; @@ -553,36 +561,44 @@ public class PackageParserLegacyCoreTest { @Test public void testUsesSdk() throws Exception { - ParsedPackage pkg = - parsePackage("install_uses_sdk.apk_r0", R.raw.install_uses_sdk_r0, x -> x); - SparseIntArray minExtVers = pkg.getMinExtensionVersions(); + ParsedPackage pkg; + SparseIntArray minExtVers; + pkg = parsePackage("install_uses_sdk.apk_r0", R.raw.install_uses_sdk_r0, x -> x); + minExtVers = pkg.getMinExtensionVersions(); assertEquals(1, minExtVers.size()); - assertEquals(0, minExtVers.get(10000, -1)); - - try { - parsePackage("install_uses_sdk.apk_r5", R.raw.install_uses_sdk_r5, x -> x); - fail("Expected parsing exception due to incompatible extension SDK version"); - } catch (PackageParser.PackageParserException expected) { - assertEquals(PackageManager.INSTALL_FAILED_OLDER_SDK, expected.error); - } - try { - parsePackage("install_uses_sdk.apk_q0", R.raw.install_uses_sdk_q0, x -> x); - fail("Expected parsing exception due to non-existent extension SDK"); - } catch (PackageParser.PackageParserException expected) { - assertEquals(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, expected.error); - } - try { - parsePackage("install_uses_sdk.apk_r", R.raw.install_uses_sdk_r, x -> x); - fail("Expected parsing exception due to unspecified extension SDK version"); - } catch (PackageParser.PackageParserException expected) { - assertEquals(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, expected.error); - } - try { - parsePackage("install_uses_sdk.apk_0", R.raw.install_uses_sdk_0, x -> x); - fail("Expected parsing exception due to unspecified extension SDK"); - } catch (PackageParser.PackageParserException expected) { - assertEquals(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, expected.error); + assertEquals(0, minExtVers.get(30, -1)); + + pkg = parsePackage("install_uses_sdk.apk_r0_s0", R.raw.install_uses_sdk_r0_s0, x -> x); + minExtVers = pkg.getMinExtensionVersions(); + assertEquals(2, minExtVers.size()); + assertEquals(0, minExtVers.get(30, -1)); + assertEquals(0, minExtVers.get(31, -1)); + + Map<Pair<String, Integer>, Integer> appToError = new HashMap<>(); + appToError.put(Pair.create("install_uses_sdk.apk_r5", R.raw.install_uses_sdk_r5), + PackageManager.INSTALL_FAILED_OLDER_SDK); + appToError.put(Pair.create("install_uses_sdk.apk_r0_s5", R.raw.install_uses_sdk_r0_s5), + PackageManager.INSTALL_FAILED_OLDER_SDK); + + appToError.put(Pair.create("install_uses_sdk.apk_q0", R.raw.install_uses_sdk_q0), + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED); + appToError.put(Pair.create("install_uses_sdk.apk_q0_r0", R.raw.install_uses_sdk_q0_r0), + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED); + appToError.put(Pair.create("install_uses_sdk.apk_r_none", R.raw.install_uses_sdk_r_none), + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED); + appToError.put(Pair.create("install_uses_sdk.apk_0", R.raw.install_uses_sdk_0), + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED); + + for (Map.Entry<Pair<String, Integer>, Integer> entry : appToError.entrySet()) { + String filename = entry.getKey().first; + int resId = entry.getKey().second; + int result = entry.getValue(); + try { + parsePackage(filename, resId, x -> x); + expect.withMessage("Expected parsing error %d from %s", result, filename).fail(); + } catch (PackageParser.PackageParserException expected) { + expect.that(expected.error).isEqualTo(result); + } } - } } diff --git a/services/tests/servicestests/src/com/android/server/power/FaceDownDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/FaceDownDetectorTest.java new file mode 100644 index 000000000000..ef20ee7e6ecd --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/power/FaceDownDetectorTest.java @@ -0,0 +1,165 @@ +/* + * 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.power; + +import static com.google.common.truth.Truth.assertThat; + +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorManager; +import android.testing.TestableContext; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.display.TestUtils; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.lang.reflect.Constructor; +import java.time.Duration; + +public class FaceDownDetectorTest { + @ClassRule + public static final TestableContext sContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + + private final FaceDownDetector mFaceDownDetector = + new FaceDownDetector(this::onFlip); + + @Mock private SensorManager mSensorManager; + + private long mCurrentTime; + private int mOnFaceDownCalls = 0; + private int mOnFaceDownExitCalls = 0; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + sContext.addMockSystemService(SensorManager.class, mSensorManager); + mCurrentTime = 0; + } + + @Test + public void faceDownFor2Seconds_triggersFaceDown() throws Exception { + mFaceDownDetector.systemReady(sContext); + + // Face up + // Using 0.5 on x to simulate constant acceleration, such as a sloped surface. + mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f)); + + for (int i = 0; i < 200; i++) { + advanceTime(Duration.ofMillis(20)); + mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f)); + } + + assertThat(mOnFaceDownCalls).isEqualTo(1); + assertThat(mOnFaceDownExitCalls).isEqualTo(0); + } + + @Test + public void faceDownFor2Seconds_withMotion_DoesNotTriggerFaceDown() throws Exception { + mFaceDownDetector.systemReady(sContext); + + // Face up + mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f)); + + for (int i = 0; i < 100; i++) { + advanceTime(Duration.ofMillis(20)); + //Move along x direction + mFaceDownDetector.onSensorChanged(createTestEvent(0.5f * i, 0.0f, -10.0f)); + } + + assertThat(mOnFaceDownCalls).isEqualTo(0); + assertThat(mOnFaceDownExitCalls).isEqualTo(0); + } + + @Test + public void faceDownForHalfSecond_DoesNotTriggerFaceDown() throws Exception { + mFaceDownDetector.systemReady(sContext); + + // Face up + mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f)); + + for (int i = 0; i < 100; i++) { + advanceTime(Duration.ofMillis(5)); + mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f)); + } + + assertThat(mOnFaceDownCalls).isEqualTo(0); + assertThat(mOnFaceDownExitCalls).isEqualTo(0); + } + + @Test + public void faceDownFor2Seconds_followedByFaceUp_triggersFaceDownExit() throws Exception { + mFaceDownDetector.systemReady(sContext); + + // Face up + // Using 0.5 on x to simulate constant acceleration, such as a sloped surface. + mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f)); + + // Trigger face down + for (int i = 0; i < 100; i++) { + advanceTime(Duration.ofMillis(20)); + mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f)); + } + + // Phone flips + for (int i = 0; i < 10; i++) { + advanceTime(Duration.ofMillis(5)); + mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 1.0f, 0.0f)); + } + + assertThat(mOnFaceDownCalls).isEqualTo(1); + assertThat(mOnFaceDownExitCalls).isEqualTo(1); + } + + private void advanceTime(Duration duration) { + mCurrentTime += duration.toNanos(); + } + + /** + * Create a test event to replicate an accelerometer sensor event. + * @param x Acceleration along the x dimension. + * @param y Acceleration along the y dimension. + * @param gravity Acceleration along the Z dimension. Relates to + */ + private SensorEvent createTestEvent(float x, float y, float gravity) throws Exception { + final Constructor<SensorEvent> constructor = + SensorEvent.class.getDeclaredConstructor(int.class); + constructor.setAccessible(true); + final SensorEvent event = constructor.newInstance(3); + event.sensor = + TestUtils.createSensor(Sensor.TYPE_ACCELEROMETER, Sensor.STRING_TYPE_ACCELEROMETER); + event.values[0] = x; + event.values[1] = y; + event.values[2] = gravity; + event.timestamp = mCurrentTime; + return event; + } + + private void onFlip(boolean isFaceDown) { + if (isFaceDown) { + mOnFaceDownCalls++; + } else { + mOnFaceDownExitCalls++; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java index 1c2d8e80ea99..5012ca9ab420 100644 --- a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java +++ b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java @@ -209,7 +209,8 @@ public class NotifierTest { private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, - SuspendBlocker suspendBlocker, WindowManagerPolicy policy) { + SuspendBlocker suspendBlocker, WindowManagerPolicy policy, + FaceDownDetector faceDownDetector) { return mNotifierMock; } @@ -296,6 +297,7 @@ public class NotifierTest { IBatteryStats.Stub.asInterface(ServiceManager.getService( BatteryStats.SERVICE_NAME)), mInjector.createSuspendBlocker(mService, "testBlocker"), + null, null); } } diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 592eea1cd55a..ea27331ac4ca 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -98,9 +98,11 @@ import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; /** * Tests for {@link com.android.server.power.PowerManagerService}. @@ -211,7 +213,8 @@ public class PowerManagerServiceTest { mService = new PowerManagerService(mContextSpy, new Injector() { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, - SuspendBlocker suspendBlocker, WindowManagerPolicy policy) { + SuspendBlocker suspendBlocker, WindowManagerPolicy policy, + FaceDownDetector faceDownDetector) { return mNotifierMock; } @@ -401,30 +404,33 @@ public class PowerManagerServiceTest { @Test public void testGetDesiredScreenPolicy_WithVR() throws Exception { createService(); + mService.systemReady(null); // Brighten up the screen - mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0, + null, null); + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); // Move to VR mService.setVrModeEnabled(true); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_VR); // Then take a nap - mService.setWakefulnessLocked(WAKEFULNESS_ASLEEP, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, - 0); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, + null, null); + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_OFF); // Wake up to VR - mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0, + null, null); + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_VR); // And back to normal mService.setVrModeEnabled(false); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -675,8 +681,9 @@ public class PowerManagerServiceTest { } @Test - public void testForceSuspend_forceSuspendFailurePropogated() { + public void testForceSuspend_forceSuspendFailurePropagated() throws Exception { createService(); + startSystem(); when(mNativeWrapperMock.nativeForceSuspend()).thenReturn(false); assertThat(mService.getBinderServiceInstance().forceSuspend()).isFalse(); } @@ -840,7 +847,7 @@ public class PowerManagerServiceTest { createService(); startSystem(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -859,11 +866,8 @@ public class PowerManagerServiceTest { public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() throws Exception { when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1"); createService(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( - DisplayPowerRequest.POLICY_OFF); - startSystem(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_OFF); } @@ -873,7 +877,7 @@ public class PowerManagerServiceTest { createService(); startSystem(); forceAwake(); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -889,7 +893,7 @@ public class PowerManagerServiceTest { mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); - assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + assertThat(mService.getDesiredScreenPolicyLocked(Display.DEFAULT_DISPLAY_GROUP)).isEqualTo( DisplayPowerRequest.POLICY_BRIGHT); } @@ -1163,6 +1167,87 @@ public class PowerManagerServiceTest { } @Test + public void testMultiDisplay_wakefulnessUpdates() throws Exception { + final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1; + final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener = + new AtomicReference<>(); + doAnswer((Answer<Void>) invocation -> { + listener.set(invocation.getArgument(0)); + return null; + }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any()); + + createService(); + startSystem(); + listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId); + + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + mService.setWakefulnessLocked(nonDefaultDisplayGroupId, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + } + + @Test + public void testMultiDisplay_addDisplayGroup_preservesWakefulness() throws Exception { + final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1; + final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener = + new AtomicReference<>(); + doAnswer((Answer<Void>) invocation -> { + listener.set(invocation.getArgument(0)); + return null; + }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any()); + + createService(); + startSystem(); + + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + + listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId); + + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + } + + @Test + public void testMultiDisplay_removeDisplayGroup_updatesWakefulness() throws Exception { + final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1; + final AtomicReference<DisplayManagerInternal.DisplayGroupListener> listener = + new AtomicReference<>(); + doAnswer((Answer<Void>) invocation -> { + listener.set(invocation.getArgument(0)); + return null; + }).when(mDisplayManagerInternalMock).registerDisplayGroupListener(any()); + + createService(); + startSystem(); + listener.get().onDisplayGroupAdded(nonDefaultDisplayGroupId); + + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_ASLEEP, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + + listener.get().onDisplayGroupRemoved(nonDefaultDisplayGroupId); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); + + mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0, + null, null); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + } + + @Test public void testGetFullPowerSavePolicy_returnsStateMachineResult() { createService(); mService.systemReady(null); diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index ddbe81c81d6d..26b34fdd4e04 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -318,7 +318,8 @@ public class PowerStatsServiceTest { assertTrue(pssProto.energyMeasurement.length == ENERGY_METER_COUNT); for (int i = 0; i < pssProto.energyMeasurement.length; i++) { assertTrue(pssProto.energyMeasurement[i].id == i); - assertTrue(pssProto.energyMeasurement[i].timestampMs == i); + assertTrue(pssProto.energyMeasurement[i].timestampMs == + i + mPowerStatsLogger.getStartWallTime()); assertTrue(pssProto.energyMeasurement[i].durationMs == i); assertTrue(pssProto.energyMeasurement[i].energyUws == i); } @@ -359,7 +360,8 @@ public class PowerStatsServiceTest { assertTrue(pssProto.energyConsumerResult.length == ENERGY_CONSUMER_COUNT); for (int i = 0; i < pssProto.energyConsumerResult.length; i++) { assertTrue(pssProto.energyConsumerResult[i].id == i); - assertTrue(pssProto.energyConsumerResult[i].timestampMs == i); + assertTrue(pssProto.energyConsumerResult[i].timestampMs == + i + mPowerStatsLogger.getStartWallTime()); assertTrue(pssProto.energyConsumerResult[i].energyUws == i); assertTrue(pssProto.energyConsumerResult[i].attribution.length == ENERGY_CONSUMER_ATTRIBUTION_COUNT); @@ -420,7 +422,8 @@ public class PowerStatsServiceTest { assertTrue(stateResidency.id == j); assertTrue(stateResidency.totalTimeInStateMs == j); assertTrue(stateResidency.totalStateEntryCount == j); - assertTrue(stateResidency.lastEntryTimestampMs == j); + assertTrue(stateResidency.lastEntryTimestampMs == + j + mPowerStatsLogger.getStartWallTime()); } } } diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS index 816bc6bba639..33385afbdfd6 100644 --- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS @@ -1 +1 @@ -include /core/java/android/media/soundtrigger/OWNERS +include /media/aidl/android/media/soundtrigger_middleware/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index 55748366c1ba..5f86d282406a 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -287,8 +287,7 @@ public class TimeDetectorServiceTest { } private static ExternalTimeSuggestion createExternalTimeSuggestion() { - TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L); - return new ExternalTimeSuggestion(timeValue); + return new ExternalTimeSuggestion(100L, 1_000_000L); } private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy { diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index daa1b25de22f..f7a498bc9d73 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -1483,11 +1483,8 @@ public class TimeDetectorStrategyImplTest { * reference time. */ ExternalTimeSuggestion generateExternalTimeSuggestion(Instant suggestedTime) { - TimestampedValue<Long> utcTime = - new TimestampedValue<>( - mFakeEnvironment.peekElapsedRealtimeMillis(), + return new ExternalTimeSuggestion(mFakeEnvironment.peekElapsedRealtimeMillis(), suggestedTime.toEpochMilli()); - return new ExternalTimeSuggestion(utcTime); } /** diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java index 682a80c9b297..5d2755221288 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java @@ -46,8 +46,8 @@ public class ConfigurationInternalTest { public void test_unrestricted() { ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) .setUserConfigAllowed(true) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(true) @@ -108,8 +108,8 @@ public class ConfigurationInternalTest { public void test_restricted() { ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) .setUserConfigAllowed(false) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(true) @@ -170,8 +170,8 @@ public class ConfigurationInternalTest { public void test_autoDetectNotSupported() { ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) .setUserConfigAllowed(true) - .setAutoDetectionSupported(false) - .setGeoDetectionSupported(false) + .setAutoDetectionFeatureSupported(false) + .setGeoDetectionFeatureSupported(false) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(true) @@ -232,8 +232,8 @@ public class ConfigurationInternalTest { public void test_geoDetectNotSupported() { ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) .setUserConfigAllowed(true) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(false) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(false) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(true) diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java index d2452eaee657..14e0bbd6fa42 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java @@ -364,8 +364,8 @@ public class TimeZoneDetectorServiceTest { // the tests. final boolean geoDetectionEnabled = autoDetectionEnabled; return new ConfigurationInternal.Builder(ARBITRARY_USER_ID) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setUserConfigAllowed(true) .setAutoDetectionEnabled(autoDetectionEnabled) .setLocationEnabled(geoDetectionEnabled) diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java index c8dba5f40882..f1f8b2f5e81a 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -90,8 +90,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED = new ConfigurationInternal.Builder(USER_ID) .setUserConfigAllowed(false) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setAutoDetectionEnabled(false) .setLocationEnabled(true) .setGeoDetectionEnabled(false) @@ -100,8 +100,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED = new ConfigurationInternal.Builder(USER_ID) .setUserConfigAllowed(false) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(true) @@ -110,8 +110,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED = new ConfigurationInternal.Builder(USER_ID) .setUserConfigAllowed(true) - .setAutoDetectionSupported(false) - .setGeoDetectionSupported(false) + .setAutoDetectionFeatureSupported(false) + .setGeoDetectionFeatureSupported(false) .setAutoDetectionEnabled(false) .setLocationEnabled(true) .setGeoDetectionEnabled(false) @@ -120,8 +120,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED = new ConfigurationInternal.Builder(USER_ID) .setUserConfigAllowed(true) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(false) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(false) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(true) @@ -130,8 +130,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED = new ConfigurationInternal.Builder(USER_ID) .setUserConfigAllowed(true) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setAutoDetectionEnabled(false) .setLocationEnabled(true) .setGeoDetectionEnabled(false) @@ -139,8 +139,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED = new ConfigurationInternal.Builder(USER_ID) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setUserConfigAllowed(true) .setAutoDetectionEnabled(true) .setLocationEnabled(true) @@ -149,8 +149,8 @@ public class TimeZoneDetectorStrategyImplTest { private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED = new ConfigurationInternal.Builder(USER_ID) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setUserConfigAllowed(true) .setAutoDetectionEnabled(true) .setLocationEnabled(true) diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java index d319488ba73b..8280cdcb18c4 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java @@ -44,8 +44,8 @@ final class TestSupport { @UserIdInt int userId, boolean geoDetectionEnabled) { return new ConfigurationInternal.Builder(userId) .setUserConfigAllowed(true) - .setAutoDetectionSupported(true) - .setGeoDetectionSupported(true) + .setAutoDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) .setAutoDetectionEnabled(true) .setLocationEnabled(true) .setGeoDetectionEnabled(geoDetectionEnabled) diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 11fb0021be62..624c3de650aa 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -144,6 +144,9 @@ public class AppStandbyControllerTests { private static final long RARE_THRESHOLD = 48 * HOUR_MS; private static final long RESTRICTED_THRESHOLD = 96 * HOUR_MS; + private static final int ASSERT_RETRY_ATTEMPTS = 20; + private static final int ASSERT_RETRY_DELAY_MILLISECONDS = 500; + /** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */ private static boolean isPackageInstalled = true; @@ -589,16 +592,37 @@ public class AppStandbyControllerTests { mInjector.mElapsedRealtime); } - private void assertBucket(int bucket) { + private void assertBucket(int bucket) throws InterruptedException { assertBucket(bucket, PACKAGE_1); } - private void assertBucket(int bucket, String pkg) { + private void assertBucket(int bucket, String pkg) throws InterruptedException { + int retries = 0; + do { + if (bucket == getStandbyBucket(mController, pkg)) { + // Success + return; + } + Thread.sleep(ASSERT_RETRY_DELAY_MILLISECONDS); + retries++; + } while(retries < ASSERT_RETRY_ATTEMPTS); + // try one last time assertEquals(bucket, getStandbyBucket(mController, pkg)); } - private void assertNotBucket(int bucket) { - assertNotEquals(bucket, getStandbyBucket(mController, PACKAGE_1)); + private void assertNotBucket(int bucket) throws InterruptedException { + final String pkg = PACKAGE_1; + int retries = 0; + do { + if (bucket != getStandbyBucket(mController, pkg)) { + // Success + return; + } + Thread.sleep(ASSERT_RETRY_DELAY_MILLISECONDS); + retries++; + } while(retries < ASSERT_RETRY_ATTEMPTS); + // try one last time + assertNotEquals(bucket, getStandbyBucket(mController, pkg)); } @Test @@ -996,7 +1020,7 @@ public class AppStandbyControllerTests { * a low bucket after the RESTRICTED timeout. */ @Test - public void testRestrictedTimeoutOverridesRestoredLowBucketPrediction() { + public void testRestrictedTimeoutOverridesRestoredLowBucketPrediction() throws Exception { reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); assertBucket(STANDBY_BUCKET_ACTIVE); @@ -1032,7 +1056,7 @@ public class AppStandbyControllerTests { * a low bucket after the RESTRICTED timeout. */ @Test - public void testRestrictedTimeoutOverridesPredictionLowBucket() { + public void testRestrictedTimeoutOverridesPredictionLowBucket() throws Exception { reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); // Not long enough to time out into RESTRICTED. @@ -1055,7 +1079,7 @@ public class AppStandbyControllerTests { } @Test - public void testRestrictedBucketDisabled() { + public void testRestrictedBucketDisabled() throws Exception { mInjector.mIsRestrictedBucketEnabled = false; // Get the controller to read the new value. Capturing the ContentObserver isn't possible // at the moment. @@ -1080,7 +1104,7 @@ public class AppStandbyControllerTests { } @Test - public void testRestrictedBucket_EnabledToDisabled() { + public void testRestrictedBucket_EnabledToDisabled() throws Exception { reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD; mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED, @@ -1097,7 +1121,7 @@ public class AppStandbyControllerTests { } @Test - public void testPredictionRaiseFromRestrictedTimeout_highBucket() { + public void testPredictionRaiseFromRestrictedTimeout_highBucket() throws Exception { reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); // Way past all timeouts. App times out into RESTRICTED bucket. @@ -1114,7 +1138,7 @@ public class AppStandbyControllerTests { } @Test - public void testPredictionRaiseFromRestrictedTimeout_lowBucket() { + public void testPredictionRaiseFromRestrictedTimeout_lowBucket() throws Exception { reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); // Way past all timeouts. App times out into RESTRICTED bucket. @@ -1366,7 +1390,7 @@ public class AppStandbyControllerTests { } @Test - public void testAddActiveDeviceAdmin() { + public void testAddActiveDeviceAdmin() throws Exception { assertActiveAdmins(USER_ID, (String[]) null); assertActiveAdmins(USER_ID2, (String[]) null); @@ -1402,7 +1426,7 @@ public class AppStandbyControllerTests { } @Test - public void isActiveDeviceAdmin() { + public void isActiveDeviceAdmin() throws Exception { assertActiveAdmins(USER_ID, (String[]) null); assertActiveAdmins(USER_ID2, (String[]) null); @@ -1488,7 +1512,7 @@ public class AppStandbyControllerTests { } @Test - public void testAppUpdateOnRestrictedBucketStatus() { + public void testAppUpdateOnRestrictedBucketStatus() throws Exception { // Updates shouldn't change bucket if the app timed out. // Way past all timeouts. App times out into RESTRICTED bucket. reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); @@ -1563,7 +1587,7 @@ public class AppStandbyControllerTests { } @Test - public void testSystemHeadlessAppElevated() { + public void testSystemHeadlessAppElevated() throws Exception { reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_SYSTEM_HEADFULL); @@ -1589,7 +1613,7 @@ public class AppStandbyControllerTests { } @Test - public void testWellbeingAppElevated() { + public void testWellbeingAppElevated() throws Exception { reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_WELLBEING); assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_WELLBEING); reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java index b40d59c14b8d..57e95d714593 100644 --- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.fail; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.LongSparseArray; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; @@ -619,6 +620,129 @@ public class WatcherTest { } @Test + public void testWatchedLongSparseArray() { + final String name = "WatchedLongSparseArray"; + WatchableTester tester; + + // Create a few leaves + Leaf leafA = new Leaf(); + Leaf leafB = new Leaf(); + Leaf leafC = new Leaf(); + Leaf leafD = new Leaf(); + + // Test WatchedLongSparseArray + WatchedLongSparseArray<Leaf> array = new WatchedLongSparseArray<>(); + array.put(INDEX_A, leafA); + array.put(INDEX_B, leafB); + tester = new WatchableTester(array, name); + tester.verify(0, "Initial array - no registration"); + leafA.tick(); + tester.verify(0, "Updates with no registration"); + tester.register(); + tester.verify(0, "Updates with no registration"); + leafA.tick(); + tester.verify(1, "Updates with registration"); + leafB.tick(); + tester.verify(2, "Updates with registration"); + array.remove(INDEX_B); + tester.verify(3, "Removed b"); + leafB.tick(); + tester.verify(3, "Updates with b not watched"); + array.put(INDEX_B, leafB); + array.put(INDEX_C, leafB); + tester.verify(5, "Added b twice"); + leafB.tick(); + tester.verify(6, "Changed b - single notification"); + array.remove(INDEX_C); + tester.verify(7, "Removed first b"); + leafB.tick(); + tester.verify(8, "Changed b - single notification"); + array.remove(INDEX_B); + tester.verify(9, "Removed second b"); + leafB.tick(); + tester.verify(9, "Updated leafB - no change"); + array.clear(); + tester.verify(10, "Cleared array"); + leafB.tick(); + tester.verify(10, "Change to b not in array"); + + // Special methods + array.put(INDEX_A, leafA); + array.put(INDEX_B, leafB); + array.put(INDEX_C, leafC); + tester.verify(13, "Added c"); + leafC.tick(); + tester.verify(14, "Ticked c"); + array.setValueAt(array.indexOfKey(INDEX_C), leafD); + tester.verify(15, "Replaced c with d"); + leafC.tick(); + tester.verify(15, "Ticked c (c not registered)"); + leafD.tick(); + tester.verify(16, "Ticked d and c (c not registered)"); + array.append(INDEX_D, leafC); + tester.verify(17, "Append c"); + leafC.tick(); + leafD.tick(); + tester.verify(19, "Ticked d and c"); + assertEquals("Verify four elements", 4, array.size()); + // Figure out which elements are at which indices. + Leaf[] x = new Leaf[4]; + for (int i = 0; i < 4; i++) { + x[i] = array.valueAt(i); + } + array.removeAt(1); + tester.verify(20, "Removed one element"); + x[1].tick(); + tester.verify(20, "Ticked one removed element"); + x[2].tick(); + tester.verify(21, "Ticked one remaining element"); + + // Snapshot + { + final WatchedLongSparseArray<Leaf> arraySnap = array.snapshot(); + tester.verify(21, "Generate snapshot (no changes)"); + // Verify that the snapshot is a proper copy of the source. + assertEquals(name + " snap same size", + array.size(), arraySnap.size()); + for (int i = 0; i < array.size(); i++) { + for (int j = 0; j < arraySnap.size(); j++) { + assertTrue(name + " elements differ", + array.valueAt(i) != arraySnap.valueAt(j)); + } + assertTrue(name + " element copy", + array.valueAt(i).equals(arraySnap.valueAt(i))); + } + leafD.tick(); + tester.verify(22, "Tick after snapshot"); + // Verify that the array snapshot is sealed + verifySealed(name, ()->arraySnap.put(INDEX_A, leafB)); + assertTrue(!array.isSealed()); + assertTrue(arraySnap.isSealed()); + } + // Recreate the snapshot since the test corrupted it. + { + final WatchedLongSparseArray<Leaf> arraySnap = array.snapshot(); + // Verify that elements are also snapshots + final Leaf arraySnapElement = arraySnap.valueAt(0); + verifySealed("ArraySnapshotElement", ()->arraySnapElement.tick()); + } + // Verify copy-in/out + { + final String msg = name + " copy-in/out"; + LongSparseArray<Leaf> base = new LongSparseArray<>(); + array.copyTo(base); + WatchedLongSparseArray<Leaf> copy = new WatchedLongSparseArray<>(); + copy.copyFrom(base); + final int end = array.size(); + assertTrue(msg + " size mismatch " + end + " " + copy.size(), end == copy.size()); + for (int i = 0; i < end; i++) { + final long key = array.keyAt(i); + assertTrue(msg, array.get(i) == copy.get(i)); + } + } + } + + @Test public void testWatchedSparseBooleanArray() { final String name = "WatchedSparseBooleanArray"; WatchableTester tester; @@ -733,4 +857,43 @@ public class WatcherTest { } } } + + @Test + public void testNestedArrays() { + final String name = "NestedArrays"; + WatchableTester tester; + + // Create a few leaves + Leaf leafA = new Leaf(); + Leaf leafB = new Leaf(); + Leaf leafC = new Leaf(); + Leaf leafD = new Leaf(); + + // Test nested arrays. + WatchedLongSparseArray<Leaf> lsaA = new WatchedLongSparseArray<>(); + lsaA.put(2, leafA); + WatchedLongSparseArray<Leaf> lsaB = new WatchedLongSparseArray<>(); + lsaB.put(4, leafB); + WatchedLongSparseArray<Leaf> lsaC = new WatchedLongSparseArray<>(); + lsaC.put(6, leafC); + + WatchedArrayMap<String, WatchedLongSparseArray<Leaf>> array = + new WatchedArrayMap<>(); + array.put("A", lsaA); + array.put("B", lsaB); + + // Test WatchedSparseIntArray + tester = new WatchableTester(array, name); + tester.verify(0, "Initial array - no registration"); + tester.register(); + tester.verify(0, "Initial array - post registration"); + leafA.tick(); + tester.verify(1, "tick grand-leaf"); + lsaA.put(2, leafD); + tester.verify(2, "replace leafA"); + leafA.tick(); + tester.verify(2, "tick unregistered leafA"); + leafD.tick(); + tester.verify(3, "tick leafD"); + } } diff --git a/services/tests/servicestests/test-apps/ConnTestApp/Android.bp b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp index 13e664446418..0731e2ca1f41 100644 --- a/services/tests/servicestests/test-apps/ConnTestApp/Android.bp +++ b/services/tests/servicestests/test-apps/ConnTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "ConnTestApp", diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.bp b/services/tests/servicestests/test-apps/JobTestApp/Android.bp index b29e187576c3..6458bcd4fc8f 100644 --- a/services/tests/servicestests/test-apps/JobTestApp/Android.bp +++ b/services/tests/servicestests/test-apps/JobTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "JobTestApp", diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp index f69dfe9d03c9..b11c85c008ad 100644 --- a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp +++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "PackageParserTestApp1", sdk_version: "current", diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp index 5cbd39c39434..50b89d484ee8 100644 --- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp +++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "SimpleServiceTestApp", diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp index 7257275971ab..5e77498862be 100644 --- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp +++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "SuspendTestApp", diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp index 35ca3544d62e..deabb6c3c438 100644 --- a/services/tests/shortcutmanagerutils/Android.bp +++ b/services/tests/shortcutmanagerutils/Android.bp @@ -12,6 +12,15 @@ // 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: "ShortcutManagerTestUtils", diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 1dd42127ec06..1ba414e33d1b 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -2,6 +2,15 @@ // Build FrameworksUiServicesTests package //######################################################################## +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"], +} + android_test { name: "FrameworksUiServicesTests", diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index 80a046a1e8bb..9ac755f78a06 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -17,6 +17,8 @@ package com.android.server.notification; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS; +import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING; +import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT; import static com.android.server.notification.NotificationManagerService.NotificationListeners.TAG_REQUESTED_LISTENERS; @@ -205,6 +207,52 @@ public class NotificationListenersTest extends UiServiceTestCase { } @Test + public void testEnsureFilters_newServiceWithMetadata_onlyOneListed() { + ServiceInfo si = new ServiceInfo(); + si.packageName = "new"; + si.name = "comp"; + si.metaData = new Bundle(); + si.metaData.putInt(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, 2); + + mListeners.ensureFilters(si, 0); + + assertThat(mListeners.getNotificationListenerFilter( + Pair.create(si.getComponentName(), 0)).getTypes()) + .isEqualTo(FLAG_FILTER_TYPE_ALERTING); + } + + @Test + public void testEnsureFilters_newServiceWithMetadata_disabledTypes() { + ServiceInfo si = new ServiceInfo(); + si.packageName = "new"; + si.name = "comp"; + si.metaData = new Bundle(); + si.metaData.putString(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES, "1,2"); + + mListeners.ensureFilters(si, 0); + + assertThat(mListeners.getNotificationListenerFilter( + Pair.create(si.getComponentName(), 0)).getTypes()) + .isEqualTo(FLAG_FILTER_TYPE_SILENT | FLAG_FILTER_TYPE_ONGOING); + } + + @Test + public void testEnsureFilters_newServiceWithMetadata_metaDataDisagrees() { + ServiceInfo si = new ServiceInfo(); + si.packageName = "new"; + si.name = "comp"; + si.metaData = new Bundle(); + si.metaData.putString(NotificationListenerService.META_DATA_DEFAULT_FILTER_TYPES, "1,2"); + si.metaData.putInt(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES, 1); + + mListeners.ensureFilters(si, 0); + + assertThat(mListeners.getNotificationListenerFilter( + Pair.create(si.getComponentName(), 0)).getTypes()) + .isEqualTo(FLAG_FILTER_TYPE_ALERTING); + } + + @Test public void testEnsureFilters_newServiceWithEmptyMetadata() { ServiceInfo si = new ServiceInfo(); si.packageName = "new"; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index a64050996d42..cebdbbef2329 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3671,8 +3671,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(r); final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true); - mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getSbn().getTag(), - r.getSbn().getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD, + mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getUserId(), + r.getKey(), NotificationStats.DISMISSAL_AOD, NotificationStats.DISMISS_SENTIMENT_POSITIVE, nv); waitForIdle(); @@ -3694,8 +3694,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(r); final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true); - mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getSbn().getTag(), - r.getSbn().getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD, + mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getUserId(), + r.getKey(), NotificationStats.DISMISSAL_AOD, NotificationStats.DISMISS_SENTIMENT_NEGATIVE, nv); waitForIdle(); @@ -6693,8 +6693,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final NotificationVisibility nv = NotificationVisibility.obtain(nrSummary.getKey(), 1, 2, true); mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, - nrSummary.getSbn().getTag(), - nrSummary.getSbn().getId(), nrSummary.getUserId(), nrSummary.getKey(), + nrSummary.getUserId(), nrSummary.getKey(), NotificationStats.DISMISSAL_SHADE, NotificationStats.DISMISS_SENTIMENT_NEUTRAL, nv); waitForIdle(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 35b224a24061..2ae2ef7162a5 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -51,6 +51,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.FastXmlSerializer; import com.android.server.UiServiceTestCase; +import com.android.server.pm.PackageManagerService; import org.junit.Before; import org.junit.Test; @@ -259,6 +260,17 @@ public class SnoozeHelperTest extends UiServiceTestCase { } @Test + public void testSnoozeSentToAndroid() throws Exception { + NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); + mSnoozeHelper.snooze(r, 1000); + ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm, times(1)).setExactAndAllowWhileIdle( + anyInt(), anyLong(), captor.capture()); + assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, + captor.getValue().getIntent().getPackage()); + } + + @Test public void testSnooze() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); mSnoozeHelper.snooze(r, (String) null); diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index ddf2844012e0..90dac47e353c 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -2,6 +2,15 @@ // Build WmTests package //######################################################################## +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"], +} + // Include all test java files. filegroup { name: "wmtests-sources", diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java new file mode 100644 index 000000000000..3025a95be98c --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java @@ -0,0 +1,152 @@ +/* + * 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.policy; + +import static android.view.KeyEvent.ACTION_DOWN; +import static android.view.KeyEvent.ACTION_UP; +import static android.view.KeyEvent.KEYCODE_POWER; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.server.policy.SingleKeyGestureDetector.KEY_LONGPRESS; +import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.app.Instrumentation; +import android.content.Context; +import android.os.SystemClock; +import android.view.KeyEvent; +import android.view.ViewConfiguration; + +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Test class for {@link SingleKeyGestureDetector}. + * + * Build/Install/Run: + * atest WmTests:SingleKeyGestureTests + */ +public class SingleKeyGestureTests { + private SingleKeyGestureDetector mDetector; + + private int mMaxMultiPressPowerCount = 2; + + private CountDownLatch mShortPressed = new CountDownLatch(1); + private CountDownLatch mLongPressed = new CountDownLatch(1); + private CountDownLatch mVeryLongPressed = new CountDownLatch(1); + private CountDownLatch mMultiPressed = new CountDownLatch(1); + + private final Instrumentation mInstrumentation = getInstrumentation(); + private final Context mContext = mInstrumentation.getTargetContext(); + private long mWaitTimeout; + private long mLongPressTime; + private long mVeryLongPressTime; + + @Before + public void setUp() { + mDetector = new SingleKeyGestureDetector(mContext); + initSingleKeyGestureRules(); + mWaitTimeout = ViewConfiguration.getMultiPressTimeout() + 50; + mLongPressTime = ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout() + 50; + mVeryLongPressTime = mContext.getResources().getInteger( + com.android.internal.R.integer.config_veryLongPressTimeout) + 50; + } + + private void initSingleKeyGestureRules() { + mDetector.addRule(new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER, + KEY_LONGPRESS | KEY_VERYLONGPRESS) { + @Override + int getMaxMultiPressCount() { + return mMaxMultiPressPowerCount; + } + @Override + public void onPress(long downTime) { + mShortPressed.countDown(); + } + + @Override + void onLongPress(long downTime) { + mLongPressed.countDown(); + } + + @Override + void onVeryLongPress(long downTime) { + mVeryLongPressed.countDown(); + } + + @Override + void onMultiPress(long downTime, int count) { + mMultiPressed.countDown(); + assertEquals(mMaxMultiPressPowerCount, count); + } + }); + } + + private void pressKey(long eventTime, int keyCode, long pressTime) { + final KeyEvent keyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN, + keyCode, 0 /* repeat */, 0 /* metaState */); + mDetector.interceptKey(keyDown); + + // keep press down. + try { + Thread.sleep(pressTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + eventTime += pressTime; + final KeyEvent keyUp = new KeyEvent(eventTime, eventTime, ACTION_UP, + keyCode, 0 /* repeat */, 0 /* metaState */); + + mDetector.interceptKey(keyUp); + } + + @Test + public void testShortPress() throws InterruptedException { + final long eventTime = SystemClock.uptimeMillis(); + pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */); + assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); + } + + @Test + public void testLongPress() throws InterruptedException { + final long eventTime = SystemClock.uptimeMillis(); + pressKey(eventTime, KEYCODE_POWER, mLongPressTime); + assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); + } + + @Test + public void testVeryLongPress() throws InterruptedException { + final long eventTime = SystemClock.uptimeMillis(); + pressKey(eventTime, KEYCODE_POWER, mVeryLongPressTime); + assertTrue(mVeryLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); + } + + @Test + public void testMultiPress() throws InterruptedException { + final long eventTime = SystemClock.uptimeMillis(); + pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */); + pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */); + assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS)); + } +} 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 aa1110cd55a7..d8e7582633de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -555,9 +555,10 @@ public class ActivityRecordTests extends WindowTestsBase { activity.setRequestedOrientation( isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE); - // Asserts it has orientation derived from bounds. - assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT, + // Asserts it has orientation derived requested orientation (fixed orientation letterbox). + assertEquals(isScreenPortrait ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation); + assertTrue(activity.isLetterboxedForFixedOrientationAndAspectRatio()); } @Test @@ -1964,17 +1965,17 @@ public class ActivityRecordTests extends WindowTestsBase { // Non-resizable mAtm.mForceResizableActivities = false; - mAtm.mSizeCompatFreeform = false; + mAtm.mSupportsNonResizableMultiWindow = false; assertFalse(activity.supportsFreeform()); // Force resizable mAtm.mForceResizableActivities = true; - mAtm.mSizeCompatFreeform = false; + mAtm.mSupportsNonResizableMultiWindow = false; assertTrue(activity.supportsFreeform()); // Allow non-resizable mAtm.mForceResizableActivities = false; - mAtm.mSizeCompatFreeform = true; + mAtm.mSupportsNonResizableMultiWindow = true; assertTrue(activity.supportsFreeform()); } @@ -2288,7 +2289,7 @@ public class ActivityRecordTests extends WindowTestsBase { IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); reset(task); activity.reportDescendantOrientationChangeIfNeeded(); - verify(task).onConfigurationChanged(any(Configuration.class)); + verify(task, atLeast(1)).onConfigurationChanged(any(Configuration.class)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index d13e4dcaf9fd..7df17fd4b3c6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -38,6 +38,7 @@ import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; +import static com.android.server.wm.DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID; import static com.google.common.truth.Truth.assertThat; @@ -595,6 +596,17 @@ public class DisplayAreaPolicyBuilderTest { assertThat(token.isDescendantOf(mRoot)).isTrue(); assertThat(token.isDescendantOf(mGroupRoot1)).isFalse(); assertThat(token.isDescendantOf(mGroupRoot2)).isFalse(); + + // When the window has options for target root id, attach it to the target root. + final Bundle options = new Bundle(); + options.putInt(KEY_ROOT_DISPLAY_AREA_ID, mGroupRoot2.mFeatureId); + final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class), + TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent, + true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */, + false /* fromClientToken */, options); + policy.addWindow(token2); + + assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue(); } @Test 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 781cfec77f08..137cf6523caf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -32,6 +32,8 @@ import static android.view.DisplayCutout.fromBoundingRect; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; @@ -121,6 +123,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.utils.WmDisplayCutout; import org.junit.Test; @@ -955,16 +958,14 @@ public class DisplayContentTests extends WindowTestsBase { IWindowManager.FIXED_TO_USER_ROTATION_DISABLED); final int newOrientation = getRotatedOrientation(dc); - final Task stack = new TaskBuilder(mSupervisor) + final Task task = new TaskBuilder(mSupervisor) .setDisplay(dc).setCreateActivity(true).build(); - final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity(); + final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity(); + dc.setFocusedApp(activity); activity.setRequestedOrientation(newOrientation); - final int expectedOrientation = newOrientation == SCREEN_ORIENTATION_PORTRAIT - ? Configuration.ORIENTATION_PORTRAIT - : Configuration.ORIENTATION_LANDSCAPE; - assertEquals(expectedOrientation, dc.getConfiguration().orientation); + assertTrue("The display should be rotated.", dc.getRotation() % 2 == 1); } @Test @@ -972,17 +973,42 @@ public class DisplayContentTests extends WindowTestsBase { final DisplayContent dc = createNewDisplay(); dc.getDisplayRotation().setFixedToUserRotation( IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); + dc.getDisplayRotation().setUserRotation( + WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_180); final int newOrientation = getRotatedOrientation(dc); - final Task stack = new TaskBuilder(mSupervisor) + final Task task = new TaskBuilder(mSupervisor) .setDisplay(dc).setCreateActivity(true).build(); - final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity(); + final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity(); + dc.setFocusedApp(activity); activity.setRequestedOrientation(newOrientation); verify(dc, never()).updateDisplayOverrideConfigurationLocked(any(), eq(activity), anyBoolean(), same(null)); - assertEquals(dc.getDisplayRotation().getUserRotation(), dc.getRotation()); + assertEquals(ROTATION_180, dc.getRotation()); + } + + @Test + public void testFixedToUserRotationChanged() { + final DisplayContent dc = createNewDisplay(); + dc.getDisplayRotation().setFixedToUserRotation( + IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); + dc.getDisplayRotation().setUserRotation( + WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_0); + final int newOrientation = getRotatedOrientation(dc); + + final Task task = new TaskBuilder(mSupervisor) + .setDisplay(dc).setCreateActivity(true).build(); + final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity(); + dc.setFocusedApp(activity); + + activity.setRequestedOrientation(newOrientation); + + dc.getDisplayRotation().setFixedToUserRotation( + IWindowManager.FIXED_TO_USER_ROTATION_DISABLED); + + assertTrue("The display should be rotated.", dc.getRotation() % 2 == 1); } @Test @@ -1418,7 +1444,7 @@ public class DisplayContentTests extends WindowTestsBase { // Assume the animation of PipTaskOrganizer is done and then commit fullscreen to task. pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); displayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); - assertFalse(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging()); + assertFalse(displayContent.getPinnedTaskController().isPipActiveOrWindowingModeChanging()); assertEquals(pinnedConfigOrientation, displayConfig.orientation); clearInvocations(mWm); @@ -1429,7 +1455,7 @@ public class DisplayContentTests extends WindowTestsBase { assertFalse(displayContent.hasTopFixedRotationLaunchingApp()); verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); assertEquals(homeConfigOrientation, displayConfig.orientation); - assertTrue(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging()); + assertTrue(displayContent.getPinnedTaskController().isPipActiveOrWindowingModeChanging()); } @Test @@ -1489,7 +1515,7 @@ public class DisplayContentTests extends WindowTestsBase { } @Test - public void testClearIntermediateFixedRotation() throws RemoteException { + public void testClearIntermediateFixedRotationAdjustments() throws RemoteException { final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); mDisplayContent.setFixedRotationLaunchingApp(activity, (mDisplayContent.getRotation() + 1) % 4); @@ -1508,7 +1534,8 @@ public class DisplayContentTests extends WindowTestsBase { ArgumentCaptor.forClass(FixedRotationAdjustmentsItem.class); verify(mAtm.getLifecycleManager(), atLeastOnce()).scheduleTransaction( eq(activity.app.getThread()), adjustmentsCaptor.capture()); - assertFalse(activity.hasFixedRotationTransform()); + // The transformation is kept for animation in real case. + assertTrue(activity.hasFixedRotationTransform()); final FixedRotationAdjustmentsItem clearAdjustments = FixedRotationAdjustmentsItem.obtain( activity.token, null /* fixedRotationAdjustments */); // The captor may match other items. The first one must be the item to clear adjustments. @@ -1819,6 +1846,37 @@ public class DisplayContentTests extends WindowTestsBase { verify(t).show(mDisplayContent.mImeScreenshot); } + @Test + public void testRotateBounds_keepSamePhysicalPosition() { + final DisplayContent dc = + new TestDisplayContent.Builder(mAtm, 1000, 2000).build(); + final Rect initBounds = new Rect(0, 0, 700, 1500); + final Rect rotateBounds = new Rect(initBounds); + + // Rotate from 0 to 0 + dc.rotateBounds(ROTATION_0, ROTATION_0, rotateBounds); + + assertEquals(new Rect(0, 0, 700, 1500), rotateBounds); + + // Rotate from 0 to 90 + rotateBounds.set(initBounds); + dc.rotateBounds(ROTATION_0, ROTATION_90, rotateBounds); + + assertEquals(new Rect(0, 300, 1500, 1000), rotateBounds); + + // Rotate from 0 to 180 + rotateBounds.set(initBounds); + dc.rotateBounds(ROTATION_0, ROTATION_180, rotateBounds); + + assertEquals(new Rect(300, 500, 1000, 2000), rotateBounds); + + // Rotate from 0 to 270 + rotateBounds.set(initBounds); + dc.rotateBounds(ROTATION_0, ROTATION_270, rotateBounds); + + assertEquals(new Rect(500, 0, 2000, 700), rotateBounds); + } + private boolean isOptionsPanelAtRight(int displayId) { return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index b1ea4a58d0f1..5a0466afa85b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -130,7 +130,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { // insets state with the global one. final InsetsState insetsState = win.getDisplayContent().getInsetsStateController().getRawInsetsState(); - win.mAboveInsetsState = insetsState; + win.mAboveInsetsState.set(insetsState); } public void setRotation(int rotation, boolean includingWindows) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 2163661b7506..47cf53b621d3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; +import static android.view.RoundedCorners.NO_ROUNDED_CORNERS; import static android.view.Surface.ROTATION_0; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; @@ -37,6 +38,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; +import static com.android.server.wm.utils.WmDisplayCutout.NO_CUTOUT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -300,9 +302,9 @@ public class DisplayPolicyTests extends WindowTestsBase { displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs); mNavBarWindow.getControllableInsetProvider().setServerVisible(true); final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState(); - mImeWindow.mAboveInsetsState = state; + mImeWindow.mAboveInsetsState.set(state); mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(), - state, displayInfo, null /* displayCutout */, null /* roundedCorners*/); + state, displayInfo, NO_CUTOUT, NO_ROUNDED_CORNERS); mDisplayContent.setInputMethodWindowLocked(mImeWindow); mImeWindow.mAttrs.setFitInsetsSides(Side.all() & ~Side.BOTTOM); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 20775e84fd8f..683ed889d283 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -117,7 +117,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation, boolean withDisplayCutout, boolean isLongEdgeCutout) { final DisplayInfo info = new DisplayInfo(); - WmDisplayCutout cutout = null; + WmDisplayCutout cutout = WmDisplayCutout.NO_CUTOUT; final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270; info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH; diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java index f91c9d0e9853..e9c356d6c6c4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java @@ -171,7 +171,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { final Rect activityBounds = new Rect(mFirstActivity.getBounds()); // DAG is portrait (860x1200), so Task and Activity fill DAG. - assertThat(mFirstTask.isTaskLetterboxed()).isFalse(); + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); assertThat(taskBounds).isEqualTo(dagBounds); assertThat(activityBounds).isEqualTo(taskBounds); @@ -194,8 +194,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { final Rect activityConfigBounds = new Rect(mFirstActivity.getConfiguration().windowConfiguration.getBounds()); - // DAG is landscape (1200x860), Task fills parent - assertThat(mFirstTask.isTaskLetterboxed()).isFalse(); + // DAG is landscape (1200x860), no fixed orientation letterbox + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); assertThat(mFirstActivity.inSizeCompatMode()).isTrue(); assertThat(newDagBounds.width()).isEqualTo(dagBounds.height()); assertThat(newDagBounds.height()).isEqualTo(dagBounds.width()); @@ -211,7 +211,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { } @Test - public void testLaunchLandscapeApp_taskIsLetterboxInDisplayAreaGroup() { + public void testLaunchLandscapeApp_activityIsLetterboxForFixedOrientationInDisplayAreaGroup() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); @@ -221,17 +221,18 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { final Rect taskBounds = new Rect(mFirstTask.getBounds()); final Rect activityBounds = new Rect(mFirstActivity.getBounds()); - // DAG is portrait (860x1200), so Task is letterbox (860x[860x860/1200=616]) - assertThat(mFirstTask.isTaskLetterboxed()).isTrue(); + // DAG is portrait (860x1200), and activity is letterboxed for fixed orientation + // (860x[860x860/1200=616]). Task fills DAG. + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isTrue(); assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); - assertThat(taskBounds.width()).isEqualTo(dagBounds.width()); - assertThat(taskBounds.height()) + assertThat(taskBounds).isEqualTo(dagBounds); + assertThat(activityBounds.width()).isEqualTo(dagBounds.width()); + assertThat(activityBounds.height()) .isEqualTo(dagBounds.width() * dagBounds.width() / dagBounds.height()); - assertThat(activityBounds).isEqualTo(taskBounds); } @Test - public void testLaunchLandscapeApp_taskLetterboxBecomesActivityLetterboxAfterRotation() { + public void testLaunchLandscapeApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation() { mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); @@ -245,9 +246,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { final Rect newTaskBounds = new Rect(mFirstTask.getBounds()); final Rect newActivityBounds = new Rect(mFirstActivity.getBounds()); - // DAG is landscape (1200x860), Task fills parent - // Task letterbox size - assertThat(mFirstTask.isTaskLetterboxed()).isFalse(); + // DAG is landscape (1200x860), no fixed orientation letterbox + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); assertThat(mFirstActivity.inSizeCompatMode()).isTrue(); assertThat(newDagBounds.width()).isEqualTo(dagBounds.height()); assertThat(newDagBounds.height()).isEqualTo(dagBounds.width()); @@ -311,7 +311,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { } @Test - public void testResizableFixedOrientationApp_taskLevelLetterboxing() { + public void testResizableFixedOrientationApp_fixedOrientationLetterboxing() { mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); @@ -324,7 +324,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); - assertThat(mFirstTask.isTaskLetterboxed()).isFalse(); + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); // Launch portrait on second DAG @@ -336,13 +336,13 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); assertThat(mSecondRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); assertThat(mSecondActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); - assertThat(mSecondTask.isTaskLetterboxed()).isFalse(); + assertThat(mSecondActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); assertThat(mSecondActivity.inSizeCompatMode()).isFalse(); // First activity is letterboxed in portrait as requested. assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); - assertThat(mFirstTask.isTaskLetterboxed()).isTrue(); + assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isTrue(); assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index ee293fcf70e6..be036034542e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -32,13 +32,14 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT; +import static com.android.server.wm.WindowContainer.POSITION_TOP; 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.Mockito.atLeast; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -208,7 +209,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame()); // Make sure app got notified. - verify(app, atLeast(1)).notifyInsetsChanged(); + verify(app, atLeastOnce()).notifyInsetsChanged(); // app will get visible IME insets while below IME. assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible()); @@ -336,6 +337,92 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible()); } + @Test + public void testUpdateAboveInsetsState_provideInsets() { + final WindowState app = createTestWindow("app"); + final WindowState statusBar = createTestWindow("statusBar"); + final WindowState navBar = createTestWindow("navBar"); + + getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); + + assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + + getController().updateAboveInsetsState(statusBar, true /* notifyInsetsChange */); + + assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + + verify(app, atLeastOnce()).notifyInsetsChanged(); + } + + @Test + public void testUpdateAboveInsetsState_receiveInsets() { + final WindowState app = createTestWindow("app"); + final WindowState statusBar = createTestWindow("statusBar"); + final WindowState navBar = createTestWindow("navBar"); + + getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); + getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null); + + assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR)); + + getController().updateAboveInsetsState(app, true /* notifyInsetsChange */); + + assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR)); + + verify(app, atLeastOnce()).notifyInsetsChanged(); + } + + @Test + public void testUpdateAboveInsetsState_zOrderChanged() { + final WindowState ime = createTestWindow("ime"); + final WindowState app = createTestWindow("app"); + final WindowState statusBar = createTestWindow("statusBar"); + final WindowState navBar = createTestWindow("navBar"); + + getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null); + getController().getSourceProvider(ITYPE_IME).setClientVisible(true); + getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); + getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null); + getController().updateAboveInsetsState(ime, false /* notifyInsetsChange */); + getController().updateAboveInsetsState(statusBar, false /* notifyInsetsChange */); + getController().updateAboveInsetsState(navBar, false /* notifyInsetsChange */); + + // ime is below others. + assertNull(app.mAboveInsetsState.peekSource(ITYPE_IME)); + assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_IME)); + assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_IME)); + assertNotNull(ime.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNotNull(ime.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR)); + + ime.getParent().positionChildAt(POSITION_TOP, ime, true /* includingParents */); + getController().updateAboveInsetsState(ime, true /* notifyInsetsChange */); + + // ime is above others. + assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_IME)); + assertNotNull(statusBar.mAboveInsetsState.peekSource(ITYPE_IME)); + assertNotNull(navBar.mAboveInsetsState.peekSource(ITYPE_IME)); + assertNull(ime.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNull(ime.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR)); + + verify(ime, atLeastOnce()).notifyInsetsChanged(); + verify(app, atLeastOnce()).notifyInsetsChanged(); + verify(statusBar, atLeastOnce()).notifyInsetsChanged(); + verify(navBar, atLeastOnce()).notifyInsetsChanged(); + } + + private WindowState createTestWindow(String name) { + final WindowState win = createWindow(null, TYPE_APPLICATION, name); + win.setHasSurface(true); + spyOn(win); + return win; + } + private InsetsStateController getController() { return mDisplayContent.getInsetsStateController(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index f935bfcd9068..d663b649fbba 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -122,6 +122,7 @@ public class LockTaskControllerTest { @Mock private StatusBarManagerInternal mStatusBarManagerInternal; @Mock private TelecomManager mTelecomManager; @Mock private RecentTasks mRecentTasks; + @Mock private TaskChangeNotificationController mTaskChangeNotificationController; private LockTaskController mLockTaskController; private Context mContext; @@ -145,7 +146,7 @@ public class LockTaskControllerTest { mSupervisor.mRootWindowContainer = mRootWindowContainer; mLockTaskController = new LockTaskController(mContext, mSupervisor, - new ImmediatelyExecuteHandler()); + new ImmediatelyExecuteHandler(), mTaskChangeNotificationController); mLockTaskController.setWindowManager(mWindowManager); mLockTaskController.mStatusBarService = mStatusBarService; mLockTaskController.mDevicePolicyManager = mDevicePolicyManager; diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index 15e045c85c29..2fdd63ed93d5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -16,8 +16,11 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; @@ -94,10 +97,19 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { mController = new RemoteAnimationController(mWm, mAdapter, mHandler); } + private WindowState createAppOverlayWindow() { + final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION_OVERLAY, + "testOverlayWindow"); + win.mActivityRecord = null; + win.mHasSurface = true; + return win; + } + @Test public void testRun() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); mDisplayContent.mOpeningApps.add(win.mActivityRecord); + final WindowState overlayWin = createAppOverlayWindow(); try { final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord, new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter; @@ -109,12 +121,12 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor<RemoteAnimationTarget[]> nonApsCaptor = + final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = ArgumentCaptor.forClass(RemoteAnimationTarget[].class); final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN), - appsCaptor.capture(), wallpapersCaptor.capture(), nonApsCaptor.capture(), + appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), finishedCaptor.capture()); assertEquals(1, appsCaptor.getValue().length); final RemoteAnimationTarget app = appsCaptor.getValue()[0]; @@ -130,6 +142,7 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { finishedCaptor.getValue().onAnimationFinished(); verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), eq(adapter)); + assertEquals(0, nonAppsCaptor.getValue().length); } finally { mDisplayContent.mOpeningApps.clear(); } @@ -424,6 +437,89 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { } } + @Test + public void testNonAppIncluded_keygaurdGoingAway() throws Exception { + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); + mDisplayContent.mOpeningApps.add(win.mActivityRecord); + // Add overlay window hidden by the keyguard. + final WindowState overlayWin = createAppOverlayWindow(); + overlayWin.hide(false /* doAnimation */, false /* requestAnim */); + try { + final AnimationAdapter adapter = mController.createRemoteAnimationRecord( + win.mActivityRecord, new Point(50, 100), null, + new Rect(50, 100, 150, 150), null).mAdapter; + adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, + mFinishedCallback); + mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = + ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); + verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_KEYGUARD_GOING_AWAY), + appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), + finishedCaptor.capture()); + assertEquals(1, appsCaptor.getValue().length); + final RemoteAnimationTarget app = appsCaptor.getValue()[0]; + assertEquals(new Point(50, 100), app.position); + assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds); + assertEquals(win.mActivityRecord.getPrefixOrderIndex(), app.prefixOrderIndex); + assertEquals(win.mActivityRecord.getTask().mTaskId, app.taskId); + assertEquals(mMockLeash, app.leash); + assertEquals(false, app.isTranslucent); + verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y); + verify(mMockTransaction).setWindowCrop(mMockLeash, 100, 50); + + finishedCaptor.getValue().onAnimationFinished(); + verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION), + eq(adapter)); + assertEquals(1, nonAppsCaptor.getValue().length); + } finally { + mDisplayContent.mOpeningApps.clear(); + } + } + + @Test + public void testNonAppIncluded_keygaurdGoingAwayToWallpaper() throws Exception { + final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class), + true, mDisplayContent, true /* ownerCanManageAppTokens */); + spyOn(mDisplayContent.mWallpaperController); + doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible(); + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); + mDisplayContent.mOpeningApps.add(win.mActivityRecord); + // Add overlay window hidden by the keyguard. + final WindowState overlayWin = createAppOverlayWindow(); + overlayWin.hide(false /* doAnimation */, false /* requestAnim */); + try { + final AnimationAdapter adapter = mController.createRemoteAnimationRecord( + win.mActivityRecord, new Point(50, 100), null, + new Rect(50, 100, 150, 150), null).mAdapter; + adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION, + mFinishedCallback); + mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor = + ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); + verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER), + appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(), + finishedCaptor.capture()); + assertEquals(1, wallpapersCaptor.getValue().length); + assertEquals(1, nonAppsCaptor.getValue().length); + } finally { + mDisplayContent.mOpeningApps.clear(); + } + } + private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { verify(binder, atLeast(0)).asBinder(); verifyNoMoreInteractions(binder); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index e843dd71381f..b73c66407874 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; @@ -43,7 +44,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.same; @@ -126,6 +126,7 @@ public class SizeCompatTests extends WindowTestsBase { // Put app window into freeform and then make it a compat app. final Rect bounds = new Rect(100, 100, 400, 600); mTask.setBounds(bounds); + prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); assertEquals(bounds, mActivity.getBounds()); @@ -194,12 +195,7 @@ public class SizeCompatTests extends WindowTestsBase { new TestDisplayContent.Builder(mAtm, 1000, 2000) .setDensityDpi(200).build(); - mActivity = new ActivityBuilder(mAtm) - .setTask(mTask) - .setResizeMode(RESIZE_MODE_UNRESIZEABLE) - .setMaxAspectRatio(1.5f) - .build(); - mActivity.mVisibleRequested = true; + prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); final Rect originalBounds = new Rect(mActivity.getBounds()); final int originalDpi = mActivity.getConfiguration().densityDpi; @@ -566,18 +562,18 @@ public class SizeCompatTests extends WindowTestsBase { .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE) .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) .build(); - assertTrue(activity.shouldUseSizeCompatMode()); + assertTrue(activity.shouldCreateCompatDisplayInsets()); // The non-resizable activity should not be size compat because it is on a resizable task // in multi-window mode. mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); - assertFalse(activity.shouldUseSizeCompatMode()); + assertFalse(activity.shouldCreateCompatDisplayInsets()); // The non-resizable activity should not be size compat because the display support // changing windowing mode from fullscreen to freeform. mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM); mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN); - assertFalse(activity.shouldUseSizeCompatMode()); + assertFalse(activity.shouldCreateCompatDisplayInsets()); } @Test @@ -597,7 +593,7 @@ public class SizeCompatTests extends WindowTestsBase { SizeCompatTests.class.getName())) .setUid(android.os.Process.myUid()) .build(); - assertFalse(activity.shouldUseSizeCompatMode()); + assertFalse(activity.shouldCreateCompatDisplayInsets()); } @Test @@ -653,7 +649,7 @@ public class SizeCompatTests extends WindowTestsBase { } @Test - public void testDisplayIgnoreOrientationRequest_fixedOrientationAppLaunchedInTaskLetterbox() { + public void testDisplayIgnoreOrientationRequest_fixedOrientationAppLaunchedLetterbox() { // Set up a display in landscape and ignoring orientation request. setUpDisplaySizeWithApp(2800, 1400); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -662,7 +658,6 @@ public class SizeCompatTests extends WindowTestsBase { prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); - final Rect taskBounds = new Rect(mTask.getBounds()); final Rect activityBounds = new Rect(mActivity.getBounds()); // Display shouldn't be rotated. @@ -670,19 +665,19 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mDisplayContent.getLastOrientation()); assertTrue(displayBounds.width() > displayBounds.height()); - // App should launch in task level letterboxing. - assertTrue(mTask.isTaskLetterboxed()); + // App should launch in fixed orientation letterbox. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(mActivity.inSizeCompatMode()); - assertEquals(taskBounds, activityBounds); - // Task bounds should be 700x1400 with the ratio as the display. - assertEquals(displayBounds.height(), taskBounds.height()); + // Activity bounds should be 700x1400 with the ratio as the display. + assertEquals(displayBounds.height(), activityBounds.height()); assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(), - taskBounds.width()); + activityBounds.width()); } @Test - public void testDisplayIgnoreOrientationRequest_taskLetterboxBecameSizeCompatAfterRotate() { + public void + testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() { // Set up a display in landscape and ignoring orientation request. setUpDisplaySizeWithApp(2800, 1400); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -700,7 +695,7 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(displayBounds.width() < displayBounds.height()); // App should be in size compat. - assertFalse(mTask.isTaskLetterboxed()); + assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertScaled(); assertEquals(activityBounds.width(), newActivityBounds.width()); assertEquals(activityBounds.height(), newActivityBounds.height()); @@ -719,7 +714,7 @@ public class SizeCompatTests extends WindowTestsBase { Rect activityBounds = new Rect(mActivity.getBounds()); // App should launch in fullscreen. - assertFalse(mTask.isTaskLetterboxed()); + assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(mActivity.inSizeCompatMode()); assertEquals(displayBounds, activityBounds); @@ -731,7 +726,7 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(displayBounds.width() > displayBounds.height()); // App should be in size compat. - assertFalse(mTask.isTaskLetterboxed()); + assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertScaled(); // App bounds should be 700x1400 with the ratio as the display. @@ -741,7 +736,7 @@ public class SizeCompatTests extends WindowTestsBase { } @Test - public void testDisplayIgnoreOrientationRequest_newLaunchedOrientationAppInTaskLetterbox() { + public void testDisplayIgnoreOrientationRequest_newLaunchedOrientationAppInLetterbox() { // Set up a display in landscape and ignoring orientation request. setUpDisplaySizeWithApp(2800, 1400); final DisplayContent display = mActivity.mDisplayContent; @@ -750,7 +745,7 @@ public class SizeCompatTests extends WindowTestsBase { // Portrait fixed app without max aspect. prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); - assertTrue(mTask.isTaskLetterboxed()); + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(mActivity.inSizeCompatMode()); // Launch another portrait fixed app. @@ -765,19 +760,19 @@ public class SizeCompatTests extends WindowTestsBase { // Update with new activity requested orientation and recompute bounds with no previous // size compat cache. verify(mTask).onDescendantOrientationChanged(same(newActivity)); - verify(mTask).computeFullscreenBounds(any(), any()); final Rect displayBounds = new Rect(display.getBounds()); final Rect taskBounds = new Rect(mTask.getBounds()); final Rect newActivityBounds = new Rect(newActivity.getBounds()); - // Task and app bounds should be 700x1400 with the ratio as the display. - assertTrue(mTask.isTaskLetterboxed()); + // Task and display bounds should be equal while activity should be letterboxed and + // has 700x1400 bounds with the ratio as the display. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(newActivity.inSizeCompatMode()); - assertEquals(taskBounds, newActivityBounds); - assertEquals(displayBounds.height(), taskBounds.height()); + assertEquals(taskBounds, displayBounds); + assertEquals(displayBounds.height(), newActivityBounds.height()); assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(), - taskBounds.width()); + newActivityBounds.width()); } @Test @@ -790,7 +785,7 @@ public class SizeCompatTests extends WindowTestsBase { // Portrait fixed app without max aspect. prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); - assertTrue(mTask.isTaskLetterboxed()); + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(mActivity.inSizeCompatMode()); // Launch another portrait fixed app with max aspect ratio as 1.3. @@ -806,21 +801,20 @@ public class SizeCompatTests extends WindowTestsBase { // Update with new activity requested orientation and recompute bounds with no previous // size compat cache. verify(mTask).onDescendantOrientationChanged(same(newActivity)); - verify(mTask).computeFullscreenBounds(any(), any()); final Rect displayBounds = new Rect(display.getBounds()); final Rect taskBounds = new Rect(mTask.getBounds()); final Rect newActivityBounds = new Rect(newActivity.getBounds()); - // Task bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio. - assertTrue(mTask.isTaskLetterboxed()); - assertEquals(displayBounds.height(), taskBounds.height()); - assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio), - taskBounds.width()); + // Task bounds should fill parent bounds. + assertEquals(displayBounds, taskBounds); - // App bounds should be fullscreen in Task bounds. + // Activity bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(newActivity.inSizeCompatMode()); - assertEquals(taskBounds, newActivityBounds); + assertEquals(displayBounds.height(), newActivityBounds.height()); + assertEquals((long) Math.rint(newActivityBounds.height() / newActivity.info.maxAspectRatio), + newActivityBounds.width()); } @Test @@ -834,26 +828,23 @@ public class SizeCompatTests extends WindowTestsBase { prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); clearInvocations(mActivity); - assertTrue(mTask.isTaskLetterboxed()); + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(mActivity.inSizeCompatMode()); - assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity); // Rotate display to portrait. rotateDisplay(mActivity.mDisplayContent, ROTATION_90); // App should be in size compat. - assertFalse(mTask.isTaskLetterboxed()); + assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertScaled(); - assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity); final Rect activityBounds = new Rect(mActivity.getBounds()); mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */); // App still in size compat, and the bounds don't change. verify(mActivity, never()).clearSizeCompatMode(); - assertFalse(mTask.isTaskLetterboxed()); + assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertScaled(); - assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity); assertEquals(activityBounds, mActivity.getBounds()); } @@ -867,22 +858,22 @@ public class SizeCompatTests extends WindowTestsBase { // Portrait fixed app. prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); - // In Task letterbox - assertTrue(mTask.isTaskLetterboxed()); + // In fixed orientation letterbox + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(mActivity.inSizeCompatMode()); // Rotate display to portrait. rotateDisplay(display, ROTATION_90); // App should be in size compat. - assertFalse(mTask.isTaskLetterboxed()); + assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertScaled(); // Rotate display to landscape. rotateDisplay(display, ROTATION_180); // In Task letterbox - assertTrue(mTask.isTaskLetterboxed()); + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(mActivity.inSizeCompatMode()); } @@ -898,22 +889,22 @@ public class SizeCompatTests extends WindowTestsBase { // Landscape fixed app. prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE); - // In Task letterbox - assertTrue(mTask.isTaskLetterboxed()); + // In fixed orientation letterbox + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(mActivity.inSizeCompatMode()); // Rotate display to portrait. rotateDisplay(display, ROTATION_90); // App should be in size compat. - assertFalse(mTask.isTaskLetterboxed()); + assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertScaled(); // Rotate display to landscape. rotateDisplay(display, ROTATION_180); - // In Task letterbox - assertTrue(mTask.isTaskLetterboxed()); + // In fixed orientation letterbox + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); assertFalse(mActivity.inSizeCompatMode()); } @@ -972,21 +963,21 @@ public class SizeCompatTests extends WindowTestsBase { addWindowToActivity(mActivity); mActivity.mRootWindowContainer.performSurfacePlacement(); - // Split screen is also in portrait [1000,1400], so Task should be in letterbox, and - // activity fills task. - assertEquals(ORIENTATION_LANDSCAPE, mTask.getConfiguration().orientation); + // Split screen is also in portrait [1000,1400], so activty should be in fixed orientation + // letterbox. + assertEquals(ORIENTATION_PORTRAIT, mTask.getConfiguration().orientation); assertEquals(ORIENTATION_LANDSCAPE, mActivity.getConfiguration().orientation); assertFitted(); - assertTrue(mTask.isTaskLetterboxed()); + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); - // Letterbox should fill the gap between the split screen and the letterboxed task. + // Letterbox should fill the gap between the split screen and the letterboxed activity. final Rect primarySplitBounds = new Rect(organizer.mPrimary.getBounds()); - final Rect letterboxedTaskBounds = new Rect(mTask.getBounds()); - assertTrue(primarySplitBounds.contains(letterboxedTaskBounds)); - assertEquals(new Rect(letterboxedTaskBounds.left - primarySplitBounds.left, - letterboxedTaskBounds.top - primarySplitBounds.top, - primarySplitBounds.right - letterboxedTaskBounds.right, - primarySplitBounds.bottom - letterboxedTaskBounds.bottom), + final Rect letterboxedBounds = new Rect(mActivity.getBounds()); + assertTrue(primarySplitBounds.contains(letterboxedBounds)); + assertEquals(new Rect(letterboxedBounds.left - primarySplitBounds.left, + letterboxedBounds.top - primarySplitBounds.top, + primarySplitBounds.right - letterboxedBounds.right, + primarySplitBounds.bottom - letterboxedBounds.bottom), mActivity.getLetterboxInsets()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 49847992125e..a1e5afb8b758 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -661,7 +661,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { @Test public void testForceMaximizesUnresizeableApp() { - mAtm.mSizeCompatFreeform = false; + mAtm.mSupportsNonResizableMultiWindow = false; final TestDisplayContent freeformDisplay = createNewDisplayContent( WINDOWING_MODE_FREEFORM); @@ -683,12 +683,12 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { } @Test - public void testLaunchesAppInWindowOnFreeformDisplay() { - mAtm.mSizeCompatFreeform = true; + public void testLaunchesPortraitSizeCompatOnFreeformLandscapeDisplayWithFreeformSizeCompat() { + mAtm.mSupportsNonResizableMultiWindow = true; final TestDisplayContent freeformDisplay = createNewDisplayContent( WINDOWING_MODE_FREEFORM); - Rect expectedLaunchBounds = new Rect(0, 0, 200, 100); + Rect expectedLaunchBounds = new Rect(0, 0, 100, 200); final ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); @@ -699,6 +699,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { mCurrent.mBounds.set(expectedLaunchBounds); mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE; + mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT; assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setOptions(options).calculate()); @@ -710,6 +711,38 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { } @Test + public void testLaunchesLandscapeSizeCompatOnFreeformLandscapeDisplayWithFreeformSizeCompat() { + mAtm.mSupportsNonResizableMultiWindow = true; + final TestDisplayContent freeformDisplay = createNewDisplayContent( + WINDOWING_MODE_FREEFORM); + final ActivityOptions options = ActivityOptions.makeBasic(); + mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea(); + mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE; + mActivity.info.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE; + assertEquals(RESULT_CONTINUE, + new CalculateRequestBuilder().setOptions(options).calculate()); + + assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test + public void testLaunchesPortraitUnresizableOnFreeformDisplayWithFreeformSizeCompat() { + mAtm.mSupportsNonResizableMultiWindow = true; + final TestDisplayContent freeformDisplay = createNewDisplayContent( + WINDOWING_MODE_FREEFORM); + final ActivityOptions options = ActivityOptions.makeBasic(); + mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea(); + mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE; + mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT; + assertEquals(RESULT_CONTINUE, + new CalculateRequestBuilder().setOptions(options).calculate()); + + assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test public void testSkipsForceMaximizingAppsOnNonFreeformDisplay() { final ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index 0eb8c8d2e58a..d853b930af11 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -266,8 +266,9 @@ public class TaskRecordTests extends WindowTestsBase { root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); assertEquals(root, task.getRootActivity()); assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation()); - assertThat(task.getBounds().width()).isLessThan(task.getBounds().height()); - assertEquals(fullScreenBounds.height(), task.getBounds().height()); + // Portrait orientation is enforced on activity level. Task should fill fullscreen bounds. + assertThat(task.getBounds().height()).isLessThan(task.getBounds().width()); + assertEquals(fullScreenBounds, task.getBounds()); // Top activity gets used final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setParentTask(stack) @@ -286,8 +287,11 @@ public class TaskRecordTests extends WindowTestsBase { // Fix the display orientation to portrait which is 90 degrees for the test display. dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90); - assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height()); - assertEquals(fullScreenBoundsPort.width(), task.getBounds().width()); + // Fixed orientation request should be resolved on activity level. Task fills display + // bounds. + assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width()); + assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height()); + assertEquals(fullScreenBoundsPort, task.getBounds()); // in FREEFORM, no constraint final Rect freeformBounds = new Rect(display.getBounds()); @@ -297,10 +301,11 @@ public class TaskRecordTests extends WindowTestsBase { task.setBounds(freeformBounds); assertEquals(freeformBounds, task.getBounds()); - // FULLSCREEN letterboxes bounds + // FULLSCREEN letterboxes bounds on activity level, no constraint on task level. stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height()); - assertEquals(fullScreenBoundsPort.width(), task.getBounds().width()); + assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width()); + assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height()); + assertEquals(fullScreenBoundsPort, task.getBounds()); // FREEFORM restores bounds as before stack.setWindowingMode(WINDOWING_MODE_FREEFORM); @@ -327,9 +332,10 @@ public class TaskRecordTests extends WindowTestsBase { assertEquals(fullScreenBounds, task.getBounds()); - // Setting app to fixed portrait fits within parent + // Setting app to fixed portrait fits within parent on activity level. Task fills parent. root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); - assertThat(task.getBounds().width()).isLessThan(task.getBounds().height()); + assertThat(root.getBounds().width()).isLessThan(root.getBounds().height()); + assertEquals(task.getBounds(), fullScreenBounds); assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation()); } @@ -424,7 +430,8 @@ public class TaskRecordTests extends WindowTestsBase { // to the input bounds. final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); final ActivityRecord.CompatDisplayInsets compatIntsets = - new ActivityRecord.CompatDisplayInsets(display, activity); + new ActivityRecord.CompatDisplayInsets( + display, activity, /* fixedOrientationBounds= */ null); task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets); assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 1c0f640e1a9c..c3eb5c49cea0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -257,24 +257,4 @@ public class TaskTests extends WindowTestsBase { task.resolveOverrideConfiguration(parentConfig); assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED); } - - @Test - public void testCleanUpActivityReferences_clearLastTaskBoundsComputeActivity() { - final Task rootTask = createTaskStackOnDisplay(mDisplayContent); - final Task leafTask = createTaskInStack(rootTask, 0 /* userId */); - final ActivityRecord activity2 = createActivityRecord(mDisplayContent, leafTask); - final ActivityRecord activity1 = createActivityRecord(mDisplayContent, leafTask); - activity1.finishing = false; - leafTask.resolveOverrideConfiguration(rootTask.getConfiguration()); - - assertEquals(activity1, leafTask.getLastTaskBoundsComputeActivity()); - - leafTask.cleanUpActivityReferences(activity2); - - assertNotNull(leafTask.getLastTaskBoundsComputeActivity()); - - leafTask.cleanUpActivityReferences(activity1); - - assertNull(leafTask.getLastTaskBoundsComputeActivity()); - } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index e4b865fd2941..86d8eee878fd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -91,11 +91,6 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { return attrs.type == TYPE_NOTIFICATION_SHADE; } - @Override - public boolean canBeHiddenByKeyguardLw(WindowState win) { - return false; - } - /** * Sets a runnable to run when adding a splash screen which gets executed after the window has * been added but before returning the surface. diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java index 0dd8d2305f4c..18c8cf5dfa05 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java @@ -18,7 +18,6 @@ package com.android.server.wm; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW; -import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH; @@ -119,20 +118,6 @@ public class WindowManagerSettingsTests extends WindowTestsBase { } @Test - public void testEnableSizeCompatFreeform() { - try (BoolSettingsSession enableSizeCompatFreeformSession = new - BoolSettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) { - final boolean enableSizeCompatFreeform = - !enableSizeCompatFreeformSession.getSetting(); - final Uri enableSizeCompatFreeformUri = - enableSizeCompatFreeformSession.setSetting(enableSizeCompatFreeform); - mWm.mSettingsObserver.onChange(false, enableSizeCompatFreeformUri); - - assertEquals(mWm.mAtmService.mSizeCompatFreeform, enableSizeCompatFreeform); - } - } - - @Test public void testSupportsNonResizableMultiWindow() { try (BoolSettingsSession supportsNonResizableMultiWindowSession = new BoolSettingsSession(DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 9c1614393554..2c2c09a5750a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -61,6 +61,7 @@ import static org.mockito.Mockito.clearInvocations; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager.RootTaskInfo; +import android.app.IRequestFinishCallback; import android.app.PictureInPictureParams; import android.content.pm.ActivityInfo; import android.content.pm.ParceledListSlice; @@ -976,7 +977,8 @@ public class WindowOrganizerTests extends WindowTestsBase { assertTrue(stack2.isOrganized()); // Verify a back pressed does not call the organizer - mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); + mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token, + new IRequestFinishCallback.Default()); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, never()).onBackPressedOnTaskRoot(any()); @@ -986,7 +988,8 @@ public class WindowOrganizerTests extends WindowTestsBase { stack.mRemoteToken.toWindowContainerToken(), true); // Verify now that the back press does call the organizer - mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); + mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token, + new IRequestFinishCallback.Default()); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); @@ -996,7 +999,8 @@ public class WindowOrganizerTests extends WindowTestsBase { stack.mRemoteToken.toWindowContainerToken(), false); // Verify now that the back press no longer calls the organizer - mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token); + mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token, + new IRequestFinishCallback.Default()); // Ensure events dispatch to organizer. mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents(); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); @@ -1201,7 +1205,8 @@ public class WindowOrganizerTests extends WindowTestsBase { mWm.mWindowPlacerLocked.deferLayout(); stack.removeImmediately(); - mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token); + mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token, + new IRequestFinishCallback.Default()); waitUntilHandlersIdle(); ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 896969548af3..51aec65f7285 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -251,7 +251,7 @@ public class WindowStateTests extends WindowTestsBase { // Simulate the window is in split screen primary stack and the current state is // minimized and home stack is resizable, so that we should ignore input for the stack. - final DockedStackDividerController controller = + final DockedTaskDividerController controller = mDisplayContent.getDockedDividerController(); final Task stack = createTaskStackOnDisplay(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDisplayContent); diff --git a/services/texttospeech/Android.bp b/services/texttospeech/Android.bp index bacc932f760f..391ab8946c2c 100644 --- a/services/texttospeech/Android.bp +++ b/services/texttospeech/Android.bp @@ -1,3 +1,12 @@ +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: "services.texttospeech-sources", srcs: ["java/**/*.java"], @@ -10,4 +19,4 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [":services.texttospeech-sources"], libs: ["services.core"], -}
\ No newline at end of file +} diff --git a/services/translation/Android.bp b/services/translation/Android.bp index 804a6177e94e..f257f1b7339a 100644 --- a/services/translation/Android.bp +++ b/services/translation/Android.bp @@ -1,3 +1,12 @@ +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: "services.translation-sources", srcs: ["java/**/*.java"], @@ -10,4 +19,4 @@ java_library_static { defaults: ["platform_service_defaults"], srcs: [":services.translation-sources"], libs: ["services.core"], -}
\ No newline at end of file +} diff --git a/services/usage/Android.bp b/services/usage/Android.bp index 80f040b1e15e..867773da7ba9 100644 --- a/services/usage/Android.bp +++ b/services/usage/Android.bp @@ -1,3 +1,12 @@ +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: "services.usage-sources", srcs: ["java/**/*.java"], diff --git a/services/usb/Android.bp b/services/usb/Android.bp index 89e5d5f15242..01feacd826c5 100644 --- a/services/usb/Android.bp +++ b/services/usb/Android.bp @@ -1,3 +1,12 @@ +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: "services.usb-sources", srcs: ["java/**/*.java"], diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java index 6bf67154efd5..c53c95cc982e 100644 --- a/services/usb/java/com/android/server/usb/UsbPortManager.java +++ b/services/usb/java/com/android/server/usb/UsbPortManager.java @@ -42,13 +42,13 @@ import android.hardware.usb.ParcelableUsbPort; import android.hardware.usb.UsbManager; import android.hardware.usb.UsbPort; import android.hardware.usb.UsbPortStatus; +import android.hardware.usb.V1_0.IUsb; import android.hardware.usb.V1_0.PortRole; import android.hardware.usb.V1_0.PortRoleType; import android.hardware.usb.V1_0.Status; import android.hardware.usb.V1_1.PortStatus_1_1; import android.hardware.usb.V1_2.IUsbCallback; import android.hardware.usb.V1_2.PortStatus; -import android.hardware.usb.V1_3.IUsb; import android.hidl.manager.V1_0.IServiceManager; import android.hidl.manager.V1_0.IServiceNotification; import android.os.Bundle; diff --git a/services/voiceinteraction/Android.bp b/services/voiceinteraction/Android.bp index 02061be73920..702ddb1b98fe 100644 --- a/services/voiceinteraction/Android.bp +++ b/services/voiceinteraction/Android.bp @@ -1,3 +1,12 @@ +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: "services.voiceinteraction-sources", srcs: ["java/**/*.java"], diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 35e65c105055..ed71d17b0dde 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -22,25 +22,49 @@ import android.content.Context; import android.content.Intent; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.SoundTrigger; +import android.media.AudioAttributes; +import android.media.AudioRecord; +import android.media.MediaRecorder; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.service.voice.AlwaysOnHotwordDetector; import android.service.voice.HotwordDetectionService; import android.service.voice.IDspHotwordDetectionCallback; import android.service.voice.IHotwordDetectionService; +import android.util.Pair; import android.util.Slog; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.infra.ServiceConnector; +import java.io.IOException; +import java.io.OutputStream; import java.io.PrintWriter; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; /** * A class that provides the communication with the HotwordDetectionService. */ final class HotwordDetectionConnection { - static final String TAG = "HotwordDetectionConnection"; + private static final String TAG = "HotwordDetectionConnection"; // TODO (b/177502877): Set the Debug flag to false before shipping. - static final boolean DEBUG = true; + private static final boolean DEBUG = true; + + // Number of bytes per sample of audio (which is a short). + private static final int BYTES_PER_SAMPLE = 2; + // TODO: These constants need to be refined. + private static final long VALIDATION_TIMEOUT_MILLIS = 3000; + private static final long VOICE_INTERACTION_TIMEOUT_TO_OPEN_MIC_MILLIS = 2000; + private static final int MAX_STREAMING_SECONDS = 10; + + private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool(); + // TODO: This may need to be a Handler(looper) + private final ScheduledExecutorService mScheduledExecutorService = + Executors.newSingleThreadScheduledExecutor(); final Object mLock; final ComponentName mDetectionComponentName; @@ -93,42 +117,106 @@ final class HotwordDetectionConnection { } } - private void detectFromDspSource(int sessionId, IDspHotwordDetectionCallback callback) { + private void detectFromDspSource(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent, + IHotwordRecognitionStatusCallback externalCallback) { if (DEBUG) { Slog.d(TAG, "detectFromDspSource"); } + + AudioRecord record = createAudioRecord(recognitionEvent); + + Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe(); + + if (clientPipe == null) { + // Error. + // Need to propagate as unknown error or something? + return; + } + ParcelFileDescriptor audioSink = clientPipe.second; + ParcelFileDescriptor clientRead = clientPipe.first; + + record.startRecording(); + + mAudioCopyExecutor.execute(() -> { + try (OutputStream fos = + new ParcelFileDescriptor.AutoCloseOutputStream(audioSink)) { + byte[] buffer = new byte[1024]; + + while (true) { + int bytesRead = record.read(buffer, 0, 1024); + + if (bytesRead < 0) { + break; + } + + fos.write(buffer, 0, bytesRead); + } + } catch (IOException e) { + Slog.w(TAG, "Failed supplying audio data to validator", e); + } + }); + + Runnable cancellingJob = () -> { + record.stop(); + bestEffortCloseFileDescriptor(audioSink); + // TODO: consider calling externalCallback.onRejected(ERROR_TIMEOUT). + }; + + ScheduledFuture<?> cancelingFuture = + mScheduledExecutorService.schedule( + cancellingJob, VALIDATION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + + // TODO: consider making this a non-anonymous class. + IDspHotwordDetectionCallback internalCallback = new IDspHotwordDetectionCallback.Stub() { + @Override + public void onDetected() throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "onDetected"); + } + bestEffortCloseFileDescriptor(audioSink); + cancelingFuture.cancel(true); + + // Give 2 more seconds for the interactor to start consuming the mic. If it fails to + // do so under the given time, we'll force-close the mic to make sure resources are + // freed up. + // TODO: consider modelling these 2 seconds in the API. + mScheduledExecutorService.schedule( + cancellingJob, + VOICE_INTERACTION_TIMEOUT_TO_OPEN_MIC_MILLIS, + TimeUnit.MILLISECONDS); + + externalCallback.onKeyphraseDetected(recognitionEvent); + } + + @Override + public void onRejected() throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "onRejected"); + } + cancelingFuture.cancel(true); + externalCallback.onRejected( + AlwaysOnHotwordDetector.HOTWORD_DETECTION_FALSE_ALERT); + } + }; + mRemoteHotwordDetectionService.run( - service -> service.detectFromDspSource(sessionId, callback)); + service -> service.detectFromDspSource( + clientRead, + recognitionEvent.getCaptureFormat(), + VALIDATION_TIMEOUT_MILLIS, + internalCallback)); + bestEffortCloseFileDescriptor(clientRead); } static final class SoundTriggerCallback extends IRecognitionStatusCallback.Stub { private SoundTrigger.KeyphraseRecognitionEvent mRecognitionEvent; private final HotwordDetectionConnection mHotwordDetectionConnection; private final IHotwordRecognitionStatusCallback mExternalCallback; - private final IDspHotwordDetectionCallback mInternalCallback; SoundTriggerCallback(IHotwordRecognitionStatusCallback callback, HotwordDetectionConnection connection) { mHotwordDetectionConnection = connection; mExternalCallback = callback; - mInternalCallback = new IDspHotwordDetectionCallback.Stub() { - @Override - public void onDetected() throws RemoteException { - if (DEBUG) { - Slog.d(TAG, "onDetected"); - } - mExternalCallback.onKeyphraseDetected(mRecognitionEvent); - } - - @Override - public void onRejected() throws RemoteException { - if (DEBUG) { - Slog.d(TAG, "onRejected"); - } - mExternalCallback.onRejected( - AlwaysOnHotwordDetector.HOTWORD_DETECTION_FALSE_ALERT); - } - }; } @Override @@ -142,7 +230,7 @@ final class HotwordDetectionConnection { if (useHotwordDetectionService) { mRecognitionEvent = recognitionEvent; mHotwordDetectionConnection.detectFromDspSource( - recognitionEvent.getCaptureSession(), mInternalCallback); + recognitionEvent, mExternalCallback); } else { mExternalCallback.onKeyphraseDetected(recognitionEvent); } @@ -171,6 +259,48 @@ final class HotwordDetectionConnection { } } + // TODO: figure out if we need to let the client configure some of the parameters. + private static AudioRecord createAudioRecord( + @NonNull SoundTrigger.KeyphraseRecognitionEvent recognitionEvent) { + int sampleRate = recognitionEvent.getCaptureFormat().getSampleRate(); + return new AudioRecord( + new AudioAttributes.Builder() + .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD).build(), + recognitionEvent.getCaptureFormat(), + getBufferSizeInBytes(sampleRate, MAX_STREAMING_SECONDS), + recognitionEvent.getCaptureSession()); + } + + /** + * Returns the number of bytes required to store {@code bufferLengthSeconds} of audio sampled at + * {@code sampleRate} Hz, using the format returned by DSP audio capture. + */ + private static int getBufferSizeInBytes(int sampleRate, int bufferLengthSeconds) { + return BYTES_PER_SAMPLE * sampleRate * bufferLengthSeconds; + } + + private static Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() { + ParcelFileDescriptor[] fileDescriptors; + try { + fileDescriptors = ParcelFileDescriptor.createPipe(); + } catch (IOException e) { + Slog.e(TAG, "Failed to create audio stream pipe", e); + return null; + } + + return Pair.create(fileDescriptors[0], fileDescriptors[1]); + } + + private static void bestEffortCloseFileDescriptor(ParcelFileDescriptor fd) { + try { + fd.close(); + } catch (IOException e) { + if (DEBUG) { + Slog.w(TAG, "Failed closing file descriptor", e); + } + } + } + public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("mBound="); pw.println(mBound); } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index f687e4bb9567..dce63ebb0889 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -76,6 +76,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IHotwordRecognitionStatusCallback; import com.android.internal.app.IVoiceActionCheckCallback; @@ -430,25 +431,17 @@ public class VoiceInteractionManagerService extends SystemService { // Eventually it will be an error to not specify this. setCurInteractor(new ComponentName(curInteractorInfo.getServiceInfo().packageName, curInteractorInfo.getServiceInfo().name), userHandle); - if (curInteractorInfo.getRecognitionService() != null) { - setCurRecognizer( - new ComponentName(curInteractorInfo.getServiceInfo().packageName, - curInteractorInfo.getRecognitionService()), userHandle); - return; - } + } else { + // No voice interactor, so clear the setting. + setCurInteractor(null, userHandle); } - // No voice interactor, we'll just set up a simple recognizer. - initSimpleRecognizer(curInteractorInfo, userHandle); + initRecognizer(userHandle); } - public void initSimpleRecognizer(VoiceInteractionServiceInfo curInteractorInfo, - int userHandle) { + public void initRecognizer(int userHandle) { ComponentName curRecognizer = findAvailRecognizer(null, userHandle); if (curRecognizer != null) { - if (curInteractorInfo == null) { - setCurInteractor(null, userHandle); - } setCurRecognizer(curRecognizer, userHandle); } } @@ -651,6 +644,10 @@ public class VoiceInteractionManagerService extends SystemService { } ComponentName findAvailRecognizer(String prefPackage, int userHandle) { + if (prefPackage == null) { + prefPackage = getDefaultRecognizer(); + } + List<ResolveInfo> available = mContext.getPackageManager().queryIntentServicesAsUser( new Intent(RecognitionService.SERVICE_INTERFACE), @@ -678,6 +675,12 @@ public class VoiceInteractionManagerService extends SystemService { } } + @Nullable + public String getDefaultRecognizer() { + String recognizer = mContext.getString(R.string.config_systemSpeechRecognizer); + return TextUtils.isEmpty(recognizer) ? null : recognizer; + } + ComponentName getCurRecognizer(int userHandle) { String curRecognizer = Settings.Secure.getStringForUser( mContext.getContentResolver(), @@ -1607,20 +1610,6 @@ public class VoiceInteractionManagerService extends SystemService { } } - private @NonNull String getDefaultRecognizer(@NonNull UserHandle user) { - ResolveInfo resolveInfo = mPm.resolveServiceAsUser( - new Intent(RecognitionService.SERVICE_INTERFACE), - PackageManager.GET_META_DATA, user.getIdentifier()); - - if (resolveInfo == null || resolveInfo.serviceInfo == null) { - Log.w(TAG, "Unable to resolve default voice recognition service."); - return ""; - } - - return new ComponentName(resolveInfo.serviceInfo.packageName, - resolveInfo.serviceInfo.name).flattenToShortString(); - } - /** * Convert the assistant-role holder into settings. The rest of the system uses the * settings. @@ -1642,9 +1631,6 @@ public class VoiceInteractionManagerService extends SystemService { Settings.Secure.ASSISTANT, "", userId); Settings.Secure.putStringForUser(getContext().getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE, "", userId); - Settings.Secure.putStringForUser(getContext().getContentResolver(), - Settings.Secure.VOICE_RECOGNITION_SERVICE, getDefaultRecognizer(user), - userId); } else { // Assistant is singleton role String pkg = roleHolders.get(0); @@ -1671,9 +1657,6 @@ public class VoiceInteractionManagerService extends SystemService { Settings.Secure.putStringForUser(getContext().getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName, userId); - Settings.Secure.putStringForUser(getContext().getContentResolver(), - Settings.Secure.VOICE_RECOGNITION_SERVICE, serviceRecognizerName, - userId); return; } @@ -1693,9 +1676,6 @@ public class VoiceInteractionManagerService extends SystemService { activityInfo.getComponentName().flattenToShortString(), userId); Settings.Secure.putStringForUser(getContext().getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE, "", userId); - Settings.Secure.putStringForUser(getContext().getContentResolver(), - Settings.Secure.VOICE_RECOGNITION_SERVICE, - getDefaultRecognizer(user), userId); return; } } @@ -1772,7 +1752,9 @@ public class VoiceInteractionManagerService extends SystemService { synchronized (VoiceInteractionManagerServiceStub.this) { Slog.i(TAG, "Force stopping current voice recognizer: " + getCurRecognizer(userHandle)); - initSimpleRecognizer(null, userHandle); + // TODO: Figure out why the interactor was being cleared and document it. + setCurInteractor(null, userHandle); + initRecognizer(userHandle); } } return hitInt || hitRec; @@ -1793,6 +1775,9 @@ public class VoiceInteractionManagerService extends SystemService { if (isPackageAppearing(pkgName) != PACKAGE_UNCHANGED) { return; } + if (getCurRecognizer(mCurUser) == null) { + initRecognizer(mCurUser); + } final String curInteractorStr = Settings.Secure.getStringForUser( mContext.getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser); @@ -1807,12 +1792,6 @@ public class VoiceInteractionManagerService extends SystemService { availInteractorInfo.getServiceInfo().packageName, availInteractorInfo.getServiceInfo().name); setCurInteractor(availInteractor, mCurUser); - if (getCurRecognizer(mCurUser) == null && - availInteractorInfo.getRecognitionService() != null) { - setCurRecognizer(new ComponentName( - availInteractorInfo.getServiceInfo().packageName, - availInteractorInfo.getRecognitionService()), mCurUser); - } } } else { if (didSomePackagesChange()) { @@ -1843,10 +1822,7 @@ public class VoiceInteractionManagerService extends SystemService { if (curRecognizer == null) { // Could a new recognizer appear when we don't have one pre-installed? if (anyPackagesAppearing()) { - curRecognizer = findAvailRecognizer(null, userHandle); - if (curRecognizer != null) { - setCurRecognizer(curRecognizer, userHandle); - } + initRecognizer(userHandle); } return; } diff --git a/services/wifi/Android.bp b/services/wifi/Android.bp index fcfcbeb72b0b..7f22bd8bd3a4 100644 --- a/services/wifi/Android.bp +++ b/services/wifi/Android.bp @@ -1,3 +1,12 @@ +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: "services.wifi-sources", srcs: ["java/**/*.java"], diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp index f42c755e3efd..98b9b64e1242 100644 --- a/startop/apps/test/Android.bp +++ b/startop/apps/test/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_app { name: "startop_test_app", srcs: [ diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp index 993d1e1092b9..4fdf34cc1814 100644 --- a/startop/iorap/Android.bp +++ b/startop/iorap/Android.bp @@ -12,6 +12,15 @@ // 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: "services.startop.iorap-javasources", srcs: ["src/**/*.java"], diff --git a/startop/iorap/functional_tests/Android.bp b/startop/iorap/functional_tests/Android.bp index 8a5bd34af653..43c61551cdec 100644 --- a/startop/iorap/functional_tests/Android.bp +++ b/startop/iorap/functional_tests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "iorap-functional-tests", srcs: ["src/**/*.java"], @@ -39,4 +48,3 @@ android_test { certificate: "platform", platform_apis: true, } - diff --git a/startop/iorap/stress/Android.bp b/startop/iorap/stress/Android.bp index f9f251bdd889..6e8725d091fb 100644 --- a/startop/iorap/stress/Android.bp +++ b/startop/iorap/stress/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + cc_binary { name: "iorap.stress.memory", srcs: ["main_memory.cc"], diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp index 3e60ad448cae..ad3d001ad7d4 100644 --- a/startop/iorap/tests/Android.bp +++ b/startop/iorap/tests/Android.bp @@ -13,6 +13,15 @@ // limitations under the License. // TODO: once b/80095087 is fixed, rewrite this back to android_test +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: "libiorap-java-test-lib", srcs: ["src/**/*.kt"], diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp index cac7b82adc2c..90239212f665 100644 --- a/startop/view_compiler/Android.bp +++ b/startop/view_compiler/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + cc_defaults { name: "viewcompiler_defaults", header_libs: [ diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp index f783aa68fe92..2048964f4af2 100644 --- a/startop/view_compiler/dex_builder_test/Android.bp +++ b/startop/view_compiler/dex_builder_test/Android.bp @@ -14,6 +14,15 @@ // 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: "generate_compiled_layout1", tools: [":viewcompiler"], diff --git a/telecomm/java/android/telecom/Connection.aidl b/telecomm/java/android/telecom/Connection.aidl new file mode 100644 index 000000000000..5b40036e46f4 --- /dev/null +++ b/telecomm/java/android/telecom/Connection.aidl @@ -0,0 +1,22 @@ +/* + * 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.telecom; + +/** + * {@hide} + */ +parcelable Connection.CallFilteringCompletionInfo; diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 942a54eb98ba..7c6253ce933a 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -29,6 +29,7 @@ import android.annotation.SystemApi; import android.app.Notification; import android.bluetooth.BluetoothDevice; import android.compat.annotation.UnsupportedAppUsage; +import android.content.ComponentName; import android.content.Intent; import android.hardware.camera2.CameraManager; import android.net.Uri; @@ -38,7 +39,9 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Parcel; import android.os.ParcelFileDescriptor; +import android.os.Parcelable; import android.os.RemoteException; import android.os.SystemClock; import android.telephony.ims.ImsStreamMediaProfile; @@ -3379,6 +3382,121 @@ public abstract class Connection extends Conferenceable { public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {} /** + * Information provided to a {@link Connection} upon completion of call filtering in Telecom. + * + * @hide + */ + @SystemApi + public static final class CallFilteringCompletionInfo implements Parcelable { + private final boolean mIsBlocked; + private final boolean mIsInContacts; + private final CallScreeningService.CallResponse mCallResponse; + private final ComponentName mCallScreeningComponent; + + /** + * Constructor for {@link CallFilteringCompletionInfo} + * + * @param isBlocked Whether any part of the call filtering process indicated that this call + * should be blocked. + * @param isInContacts Whether the caller is in the user's contacts. + * @param callResponse The instance of {@link CallScreeningService.CallResponse} provided + * by the {@link CallScreeningService} that processed this call, or + * {@code null} if no call screening service ran. + * @param callScreeningComponent The component of the {@link CallScreeningService} + * that processed this call, or {@link null} if no + * call screening service ran. + */ + public CallFilteringCompletionInfo(boolean isBlocked, boolean isInContacts, + @Nullable CallScreeningService.CallResponse callResponse, + @Nullable ComponentName callScreeningComponent) { + mIsBlocked = isBlocked; + mIsInContacts = isInContacts; + mCallResponse = callResponse; + mCallScreeningComponent = callScreeningComponent; + } + + /** @hide */ + protected CallFilteringCompletionInfo(Parcel in) { + mIsBlocked = in.readByte() != 0; + mIsInContacts = in.readByte() != 0; + CallScreeningService.ParcelableCallResponse response + = in.readParcelable(CallScreeningService.class.getClassLoader()); + mCallResponse = response == null ? null : response.toCallResponse(); + mCallScreeningComponent = in.readParcelable(ComponentName.class.getClassLoader()); + } + + @NonNull + public static final Creator<CallFilteringCompletionInfo> CREATOR = + new Creator<CallFilteringCompletionInfo>() { + @Override + public CallFilteringCompletionInfo createFromParcel(Parcel in) { + return new CallFilteringCompletionInfo(in); + } + + @Override + public CallFilteringCompletionInfo[] newArray(int size) { + return new CallFilteringCompletionInfo[size]; + } + }; + + /** + * @return Whether any part of the call filtering process indicated that this call should be + * blocked. + */ + public boolean isBlocked() { + return mIsBlocked; + } + + /** + * @return Whether the caller is in the user's contacts. + */ + public boolean isInContacts() { + return mIsInContacts; + } + + /** + * @return The instance of {@link CallScreeningService.CallResponse} provided + * by the {@link CallScreeningService} that processed this + * call, or {@code null} if no call screening service ran. + */ + public @Nullable CallScreeningService.CallResponse getCallResponse() { + return mCallResponse; + } + + /** + * @return The component of the {@link CallScreeningService} + * that processed this call, or {@code null} if no call screening service ran. + */ + public @Nullable ComponentName getCallScreeningComponent() { + return mCallScreeningComponent; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "CallFilteringCompletionInfo{" + + "mIsBlocked=" + mIsBlocked + + ", mIsInContacts=" + mIsInContacts + + ", mCallResponse=" + mCallResponse + + ", mCallScreeningPackageName='" + mCallScreeningComponent + '\'' + + '}'; + } + + /** @hide */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeByte((byte) (mIsBlocked ? 1 : 0)); + dest.writeByte((byte) (mIsInContacts ? 1 : 0)); + dest.writeParcelable(mCallResponse == null ? null : mCallResponse.toParcelable(), 0); + dest.writeParcelable(mCallScreeningComponent, 0); + } + } + + /** * Indicates that call filtering in Telecom is complete * * This method is called for a connection created via @@ -3386,24 +3504,13 @@ public abstract class Connection extends Conferenceable { * Telecom, including checking the blocked number db, per-contact settings, and custom call * filtering apps. * - * @param isBlocked {@code true} if the call was blocked, {@code false} otherwise. If this is - * {@code true}, {@link #onDisconnect()} will be called soon after - * this is called. - * @param isInContacts Indicates whether the caller is in the user's contacts list. - * @param callScreeningResponse The response that was returned from the - * {@link CallScreeningService} that handled this call. If no - * response was received from a call screening service, - * this will be {@code null}. - * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the - * system dialer. If {@code callScreeningResponse} is - * {@code null}, this will be {@code false}. + * @param callFilteringCompletionInfo Info provided by Telecom on the results of call filtering. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.READ_CONTACTS) - public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts, - @Nullable CallScreeningService.CallResponse callScreeningResponse, - boolean isResponseFromSystemDialer) { } + public void onCallFilteringCompleted( + @NonNull CallFilteringCompletionInfo callFilteringCompletionInfo) { } static String toLogSafePhoneNumber(String number) { // For unknown number, log empty string. diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 966ece3a3ba2..c189b19c71af 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -758,19 +758,15 @@ public abstract class ConnectionService extends Service { } @Override - public void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts, - CallScreeningService.ParcelableCallResponse callScreeningResponse, - boolean isResponseFromSystemDialer, + public void onCallFilteringCompleted(String callId, + Connection.CallFilteringCompletionInfo completionInfo, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED); try { SomeArgs args = SomeArgs.obtain(); args.arg1 = callId; - args.arg2 = isBlocked; - args.arg3 = isInContacts; - args.arg4 = callScreeningResponse; - args.arg5 = isResponseFromSystemDialer; - args.arg6 = Log.createSubsession(); + args.arg2 = completionInfo; + args.arg3 = Log.createSubsession(); mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget(); } finally { Log.endSession(); @@ -1441,16 +1437,12 @@ public abstract class ConnectionService extends Service { case MSG_ON_CALL_FILTERING_COMPLETED: { SomeArgs args = (SomeArgs) msg.obj; try { - Log.continueSession((Session) args.arg6, + Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED); String callId = (String) args.arg1; - boolean isBlocked = (boolean) args.arg2; - boolean isInContacts = (boolean) args.arg3; - CallScreeningService.ParcelableCallResponse callScreeningResponse = - (CallScreeningService.ParcelableCallResponse) args.arg4; - boolean isResponseFromSystemDialer = (boolean) args.arg5; - onCallFilteringCompleted(callId, isBlocked, isInContacts, - callScreeningResponse, isResponseFromSystemDialer); + Connection.CallFilteringCompletionInfo completionInfo = + (Connection.CallFilteringCompletionInfo) args.arg2; + onCallFilteringCompleted(callId, completionInfo); } finally { args.recycle(); Log.endSession(); @@ -2466,16 +2458,12 @@ public abstract class ConnectionService extends Service { } } - private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts, - CallScreeningService.ParcelableCallResponse callScreeningResponse, - boolean isResponseFromSystemDialer) { - Log.i(this, "onCallFilteringCompleted(%s, %b, %b, %s, %b)", callId, - isBlocked, isInContacts, callScreeningResponse, isResponseFromSystemDialer); + private void onCallFilteringCompleted(String callId, Connection.CallFilteringCompletionInfo + callFilteringCompletionInfo) { + Log.i(this, "onCallFilteringCompleted(%s, %s)", callId, callFilteringCompletionInfo); Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted"); if (connection != null) { - connection.onCallFilteringCompleted(isBlocked, isInContacts, - callScreeningResponse == null ? null : callScreeningResponse.toCallResponse(), - isResponseFromSystemDialer); + connection.onCallFilteringCompleted(callFilteringCompletionInfo); } } diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index 5cf8de8f9078..f20ee7e56d05 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -138,6 +138,24 @@ import java.util.List; * } * } * } + * + * </pre> + * <p id="companionInCallService"> + * <h3>Access to InCallService for Wearable Devices</h3> + * <ol> + * If your app is a third-party companion app and wants to access InCallService APIs, what your + * app could do are: + * <p> + * <ol> + * <li> Declare MANAGE_ONGOING_CALLS permission in your manifest + * <li> Associate with a physical wearable device via the + * {@link android.companion.CompanionDeviceManager} API as a companion app. See: + * https://developer.android.com/guide/topics/connectivity/companion-device-pairing + * <li> Implement this InCallService with BIND_INCALL_SERVICE permission + * </ol> + * </ol> + * <p> + * * </pre> * <p id="incomingCallNotification"> * <h3>Showing the Incoming Call Notification</h3> diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java index 6c6097ac71e5..7a6fddb6f029 100644 --- a/telecomm/java/android/telecom/RemoteConnection.java +++ b/telecomm/java/android/telecom/RemoteConnection.java @@ -1202,27 +1202,18 @@ public final class RemoteConnection { /** * Notifies this {@link RemoteConnection} that call filtering has completed, as well as * the results of a contacts lookup for the remote party. - * @param isBlocked Whether call filtering indicates that the call should be blocked - * @param isInContacts Whether the remote party is in the user's contacts - * @param callScreeningResponse The response that was returned from the - * {@link CallScreeningService} that handled this call. If no - * response was received from a call screening service, - * this will be {@code null}. - * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the - * system dialer. If {@code callScreeningResponse} is - * {@code null}, this will be {@code false}. + * + * @param completionInfo Info provided by Telecom on the results of call filtering. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.READ_CONTACTS) - public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts, - @Nullable CallScreeningService.CallResponse callScreeningResponse, - boolean isResponseFromSystemDialer) { + public void onCallFilteringCompleted( + @NonNull Connection.CallFilteringCompletionInfo completionInfo) { Log.startSession("RC.oCFC", getActiveOwnerInfo()); try { if (mConnected) { - mConnectionService.onCallFilteringCompleted(mConnectionId, isBlocked, isInContacts, - callScreeningResponse.toParcelable(), isResponseFromSystemDialer, + mConnectionService.onCallFilteringCompleted(mConnectionId, completionInfo, null /*Session.Info*/); } } catch (RemoteException ignored) { diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 472d63946ebc..17749e8b0a8f 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1702,22 +1702,22 @@ public class TelecomManager { } /** - * Returns whether the caller has {@link InCallService} access for companion apps. - * - * A companion app is an app associated with a physical wearable device via the - * {@link android.companion.CompanionDeviceManager} API. + * Returns whether the caller has {@link android.Manifest.permission#MANAGE_ONGOING_CALLS} + * permission. The permission can be obtained by associating with a physical wearable device + * via the {@link android.companion.CompanionDeviceManager} API as a companion app. If the + * caller app has the permission, it has {@link InCallService} access to manage ongoing calls. * * @return {@code true} if the caller has {@link InCallService} access for * companion app; {@code false} otherwise. */ - public boolean hasCompanionInCallServiceAccess() { + public boolean hasManageOngoingCallsPermission() { ITelecomService service = getTelecomService(); if (service != null) { try { - return service.hasCompanionInCallServiceAccess( + return service.hasManageOngoingCallsPermission( mContext.getOpPackageName()); } catch (RemoteException e) { - Log.e(TAG, "RemoteException calling hasCompanionInCallServiceAccess().", e); + Log.e(TAG, "RemoteException calling hasManageOngoingCallsPermission().", e); if (!isSystemProcess()) { e.rethrowAsRuntimeException(); } diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index 7599e189cc37..d72f8aa82ddb 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -20,7 +20,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.telecom.CallAudioState; -import android.telecom.CallScreeningService; +import android.telecom.Connection; import android.telecom.ConnectionRequest; import android.telecom.Logging.Session; import android.telecom.PhoneAccountHandle; @@ -119,9 +119,9 @@ oneway interface IConnectionService { void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo); - void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts, - in CallScreeningService.ParcelableCallResponse callScreeningResponse, - boolean isResponseFromSystemDialer, in Session.Info sessionInfo); + void onCallFilteringCompleted(String callId, + in Connection.CallFilteringCompletionInfo completionInfo, + in Session.Info sessionInfo); void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo); diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 88ef1b09f6b1..eb106b50f69d 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -179,9 +179,9 @@ interface ITelecomService { boolean isInCall(String callingPackage, String callingFeatureId); /** - * @see TelecomServiceImpl#hasCompanionInCallServiceAccess + * @see TelecomServiceImpl#hasManageOngoingCallsPermission */ - boolean hasCompanionInCallServiceAccess(String callingPackage); + boolean hasManageOngoingCallsPermission(String callingPackage); /** * @see TelecomServiceImpl#isInManagedCall diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 48a5ab6d204b..5e50bea05057 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -686,6 +686,67 @@ public final class TelephonyPermissions { } /** + * Given a list of permissions, check to see if the caller has at least one of them. If the + * caller has none of these permissions, throw a SecurityException. + */ + public static void enforceAnyPermissionGranted(Context context, int uid, String message, + String... permissions) { + if (permissions.length == 0) return; + boolean isGranted = false; + for (String perm : permissions) { + if (context.checkCallingOrSelfPermission(perm) == PERMISSION_GRANTED) { + isGranted = true; + break; + } + } + + if (isGranted) return; + + StringBuilder b = new StringBuilder(message); + b.append(": Neither user "); + b.append(uid); + b.append(" nor current process has "); + b.append(permissions[0]); + for (int i = 1; i < permissions.length; i++) { + b.append(" or "); + b.append(permissions[i]); + } + throw new SecurityException(b.toString()); + } + + /** + * Given a list of permissions, check to see if the caller has at least one of them granted. If + * not, check to see if the caller has carrier privileges. If the caller does not have any of + * these permissions, throw a SecurityException. + */ + public static void enforceAnyPermissionGrantedOrCarrierPrivileges(Context context, int subId, + int uid, String message, String... permissions) { + if (permissions.length == 0) return; + boolean isGranted = false; + for (String perm : permissions) { + if (context.checkCallingOrSelfPermission(perm) == PERMISSION_GRANTED) { + isGranted = true; + break; + } + } + + if (isGranted) return; + if (checkCarrierPrivilegeForSubId(context, subId)) return; + + StringBuilder b = new StringBuilder(message); + b.append(": Neither user "); + b.append(uid); + b.append(" nor current process has "); + b.append(permissions[0]); + for (int i = 1; i < permissions.length; i++) { + b.append(" or "); + b.append(permissions[i]); + } + b.append(" or carrier privileges"); + throw new SecurityException(b.toString()); + } + + /** * Throws if the caller is not of a shell (or root) UID. * * @param callingUid pass Binder.callingUid(). diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java index c6757fba4d53..4ae11b8458cb 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -111,6 +111,7 @@ public class Annotation { public @interface NetworkType { } + // TODO(b/180542000): remove and replace references with @ApnSetting.ApnType @IntDef(flag = true, prefix = {"TYPE_"}, value = { ApnSetting.TYPE_DEFAULT, ApnSetting.TYPE_MMS, @@ -124,6 +125,7 @@ public class Annotation { ApnSetting.TYPE_EMERGENCY, ApnSetting.TYPE_MCX, ApnSetting.TYPE_XCAP, + // ApnSetting.TYPE_ENTERPRISE }) @Retention(RetentionPolicy.SOURCE) public @interface ApnType { diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 6d3fa81a856d..b52f49190679 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -33,7 +33,11 @@ import android.os.RemoteException; import android.service.carrier.CarrierService; import android.telecom.TelecomManager; import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.ImsRegistrationAttributes; import android.telephony.ims.ImsSsData; +import android.telephony.ims.SipDelegateManager; +import android.telephony.ims.feature.MmTelFeature; +import android.telephony.ims.feature.RcsFeature; import com.android.internal.telephony.ICarrierConfigLoader; import com.android.telephony.Rlog; @@ -1717,8 +1721,14 @@ public class CarrierConfigManager { * Configs used for APN setup. */ public static final class Apn { - /** Prefix of all Apn.KEY_* constants. */ - private static final String KEY_PREFIX = "apn."; + /** + * Prefix of all Apn.KEY_* constants. + * + * @deprecated Since KEY_PREFIX is unnecessary to public, it will modify to private + * next android generation. + */ + @Deprecated + public static final String KEY_PREFIX = "apn."; /** IPv4 internet protocol */ public static final String PROTOCOL_IPV4 = "IP"; @@ -3666,14 +3676,13 @@ public class CarrierConfigManager { /** Prefix of all ImsServiceEntitlement.KEY_* constants. */ public static final String KEY_PREFIX = "imsserviceentitlement."; - /** The address of the entitlement configuration server. */ - public static final String KEY_AES_URL_STRING = KEY_PREFIX + "aes_url_string"; - + public static final String KEY_ENTITLEMENT_SERVER_URL_STRING = + KEY_PREFIX + "entitlement_server_url_string"; private static PersistableBundle getDefaults() { PersistableBundle defaults = new PersistableBundle(); - defaults.putString(KEY_AES_URL_STRING, ""); + defaults.putString(KEY_ENTITLEMENT_SERVER_URL_STRING, ""); return defaults; } } @@ -4001,6 +4010,43 @@ public class CarrierConfigManager { KEY_PREFIX + "enable_presence_publish_bool"; /** + * Each string in this array contains a mapping between the service-id and version portion + * of the service-description element and the associated IMS feature tag(s) that are + * associated with each element (see RCC.07 Table 7). + * <p> + * Each string contains 3 parts, which define the mapping between service-description and + * feature tag(s) that must be present in the IMS REGISTER for the RCS service to be + * published as part of the RCS PUBLISH procedure: + * [service-id]|[version]|[desc]|[feature_tag];[feature_tag];... + * <ul> + * <li>[service-id]: the service-id element associated with the RCS capability.</li> + * <li>[version]: The version element associated with that service-id</li> + * <li>[desc]: The optional desecription element associated with that service-id</li> + * <li>[feature_tag];[feature_tag]: The list of all feature tags associated with this + * capability that MUST ALL be present in the IMS registration for this this + * capability to be published to the network.</li> + * </ul> + * <p> + * Features managed by the framework will be considered capable when the ImsService reports + * that those services are capable via the + * {@link MmTelFeature#notifyCapabilitiesStatusChanged(MmTelFeature.MmTelCapabilities)} or + * {@link RcsFeature#notifyCapabilitiesStatusChanged(RcsFeature.RcsImsCapabilities)} APIs. + * For RCS services not managed by the framework, the capability of these services are + * determined by looking at the feature tags associated with the IMS registration using the + * {@link ImsRegistrationAttributes} API and mapping them to the service-description map. + * <p> + * The framework contains a default value of this key, which is based off of RCC.07 + * specification. Capabilities based of carrier extensions may be added to this list on a + * carrier-by-carrier basis as required in order to support additional services in the + * PUBLISH. If this list contains a service-id and version that overlaps with the default, + * it will override the framework default. + * @hide + */ + @SystemApi + public static final String KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY = + KEY_PREFIX + "publish_service_desc_feature_tag_map_override_string_array"; + + /** * Flag indicating whether or not this carrier supports the exchange of phone numbers with * the carrier's RCS presence server in order to retrieve the RCS capabilities of requested * contacts used in the RCS User Capability Exchange (UCE) procedure. See RCC.71, section 3 @@ -4059,6 +4105,8 @@ public class CarrierConfigManager { defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000); defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false); defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false); + defaults.putStringArray(KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY, + new String[] {}); defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false); defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false); defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true); @@ -4230,10 +4278,6 @@ public class CarrierConfigManager { public static final String KEY_RETRANSMIT_TIMER_MSEC_INT_ARRAY = KEY_PREFIX + "retransmit_timer_sec_int_array"; - /** Controls if wifi mac Id should be added to network access identifier(NAI) */ - public static final String KEY_ADD_WIFI_MAC_ADDR_TO_NAI_BOOL = - KEY_PREFIX + "add_wifi_mac_addr_to_nai_bool"; - /** * Specifies the local identity type for IKE negotiations. Possible values are {@link * #ID_TYPE_FQDN}, {@link #ID_TYPE_RFC822_ADDR}, {@link #ID_TYPE_KEY_ID} @@ -4557,7 +4601,6 @@ public class CarrierConfigManager { KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, new int[] {EPDG_ADDRESS_PLMN, EPDG_ADDRESS_STATIC}); defaults.putStringArray(KEY_MCC_MNCS_STRING_ARRAY, new String[] {}); - defaults.putBoolean(KEY_ADD_WIFI_MAC_ADDR_TO_NAI_BOOL, false); defaults.putInt(KEY_IKE_LOCAL_ID_TYPE_INT, ID_TYPE_RFC822_ADDR); defaults.putInt(KEY_IKE_REMOTE_ID_TYPE_INT, ID_TYPE_FQDN); defaults.putBoolean(KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL, false); @@ -4749,7 +4792,7 @@ public class CarrierConfigManager { public static final String KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL = "network_temp_not_metered_supported_bool"; - /* + /** * Boolean indicating whether the SIM PIN can be stored and verified * seamlessly after an unattended reboot. * @@ -4764,7 +4807,7 @@ public class CarrierConfigManager { public static final String KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL = "store_sim_pin_for_unattended_reboot_bool"; - /** + /** * Determine whether "Enable 2G" toggle can be shown. * * Used to trade privacy/security against potentially reduced carrier coverage for some @@ -4772,6 +4815,16 @@ public class CarrierConfigManager { */ public static final String KEY_HIDE_ENABLE_2G = "hide_enable_2g_bool"; + /** + * Indicates the allowed APN types that can be used for LTE initial attach. The order of APN + * types in the configuration is the order of APN types that will be used for initial attach. + * Empty list indicates that no APN types are allowed for initial attach. + * + * @hide + */ + public static final String KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY = + "allowed_initial_attach_apn_types_string_array"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -5316,7 +5369,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY, new String[0]); sDefaults.putStringArray(KEY_APN_PRIORITY_STRING_ARRAY, new String[] { - "default:0", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2", + "default:0", "enterprise:1", "mms:2", "supl:2", "dun:2", "hipri:3", "fota:2", "ims:2", "cbs:2", "ia:2", "emergency:2", "mcx:3", "xcap:3" }); sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]); @@ -5330,6 +5383,8 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0); sDefaults.putBoolean(KEY_STORE_SIM_PIN_FOR_UNATTENDED_REBOOT_BOOL, true); sDefaults.putBoolean(KEY_HIDE_ENABLE_2G, false); + sDefaults.putStringArray(KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY, + new String[]{"ia", "default", "ims", "mms", "dun", "emergency"}); } /** diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java index 99a77ae5d133..c8ed82cd2a3f 100644 --- a/telephony/java/android/telephony/DataFailCause.java +++ b/telephony/java/android/telephony/DataFailCause.java @@ -917,6 +917,10 @@ public final class DataFailCause { public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB; /** Data call fail due to the slice not being allowed for the data call. */ public static final int SLICE_REJECTED = 0x8CC; + /** No matching rule available for the request, and match-all rule is not allowed for it. */ + public static final int MATCH_ALL_RULE_NOT_ALLOWED = 0x8CD; + /** If connection failed for all matching URSP rules. */ + public static final int ALL_MATCHING_RULES_FAILED = 0x8CE; //IKE error notifications message as specified in 3GPP TS 24.302 (Section 8.1.2.2). diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java index af67ed279fab..fe7e5976b132 100644 --- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java +++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java @@ -187,7 +187,7 @@ public final class SignalStrengthUpdateRequest implements Parcelable { return mIsSystemThresholdReportingRequestedWhileIdle; } - /* + /** * @return the live token of the request * * @hide diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 6c013df66840..4926687f6724 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -2649,6 +2649,37 @@ public final class SmsManager { */ public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent) { + sendMultimediaMessage(context, contentUri, locationUrl, configOverrides, sentIntent, + 0L /* messageId */); + } + + /** + * Send an MMS message + * + * Same as {@link #sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, + * Bundle configOverrides, PendingIntent sentIntent)}, but adds an optional messageId. + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail sending the MMS message because no + * suitable default subscription could be found. In this case, if {@code sentIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the + * conditions where this operation may fail. + * </p> + * + * @param context application context + * @param contentUri the content Uri from which the message pdu will be read + * @param locationUrl the optional location url where message should be sent to + * @param configOverrides the carrier-specific messaging configuration values to override for + * sending the message. + * @param sentIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is successfully sent, or failed + * @param messageId an id that uniquely identifies the message requested to be sent. + * Used for logging and diagnostics purposes. The id may be 0. + * @throws IllegalArgumentException if contentUri is empty + */ + public void sendMultimediaMessage(@NonNull Context context, @NonNull Uri contentUri, + @Nullable String locationUrl, @Nullable Bundle configOverrides, + @Nullable PendingIntent sentIntent, long messageId) { if (contentUri == null) { throw new IllegalArgumentException("Uri contentUri null"); } @@ -2658,7 +2689,7 @@ public final class SmsManager { @Override public void onSuccess(int subId) { m.sendMultimediaMessage(subId, contentUri, locationUrl, configOverrides, - sentIntent, 0L /* messageId */); + sentIntent, messageId); } @Override @@ -2692,6 +2723,39 @@ public final class SmsManager { */ public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent) { + downloadMultimediaMessage(context, locationUrl, contentUri, configOverrides, + downloadedIntent, 0L /* messageId */); + } + + /** + * Download an MMS message from carrier by a given location URL + * + * Same as {@link #downloadMultimediaMessage(Context context, String locationUrl, + * Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent)}, + * but adds an optional messageId. + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail downloading the MMS message because no + * suitable default subscription could be found. In this case, if {@code downloadedIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the + * conditions where this operation may fail. + * </p> + * + * @param context application context + * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained + * from the MMS WAP push notification + * @param contentUri the content uri to which the downloaded pdu will be written + * @param configOverrides the carrier-specific messaging configuration values to override for + * downloading the message. + * @param downloadedIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is downloaded, or the download is failed + * @param messageId an id that uniquely identifies the message requested to be downloaded. + * Used for logging and diagnostics purposes. The id may be 0. + * @throws IllegalArgumentException if locationUrl or contentUri is empty + */ + public void downloadMultimediaMessage(@NonNull Context context, @NonNull String locationUrl, + @NonNull Uri contentUri, @Nullable Bundle configOverrides, + @Nullable PendingIntent downloadedIntent, long messageId) { if (TextUtils.isEmpty(locationUrl)) { throw new IllegalArgumentException("Empty MMS location URL"); } @@ -2704,7 +2768,7 @@ public final class SmsManager { @Override public void onSuccess(int subId) { m.downloadMultimediaMessage(subId, locationUrl, contentUri, configOverrides, - downloadedIntent, 0L /* messageId */); + downloadedIntent, messageId); } @Override diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 6a9b71d78dd9..61e809b55031 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -29,6 +29,7 @@ import android.annotation.IntDef; import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -8579,6 +8580,9 @@ public class TelephonyManager { */ @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED) @SystemApi public boolean setAllowedNetworkTypes(@NetworkTypeBitMask long allowedNetworkTypes) { try { @@ -8670,6 +8674,9 @@ public class TelephonyManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED) public void setAllowedNetworkTypesForReason(@AllowedNetworkTypesReason int reason, @NetworkTypeBitMask long allowedNetworkTypes) { if (!isValidAllowedNetworkTypesReason(reason)) { @@ -8708,6 +8715,9 @@ public class TelephonyManager { */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature( + enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", + value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED) public @NetworkTypeBitMask long getAllowedNetworkTypesForReason( @AllowedNetworkTypesReason int reason) { if (!isValidAllowedNetworkTypesReason(reason)) { @@ -11293,21 +11303,16 @@ public class TelephonyManager { * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) - * Additionally, depending on the level of location permissions the caller holds (i.e. no - * location permissions, {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, or - * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}), location-sensitive fields will - * be cleared from the return value. - * - * <p>Note also that if the caller holds any sort of location permission, a usage event for the - * {@link android.app.AppOpsManager#OPSTR_FINE_LOCATION} or - * {@link android.app.AppOpsManager#OPSTR_FINE_LOCATION} - * will be logged against the caller when calling this method. + * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. * * May return {@code null} when the subscription is inactive or when there was an error * communicating with the phone process. */ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges - @RequiresPermission(Manifest.permission.READ_PHONE_STATE) + @RequiresPermission(allOf = { + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.ACCESS_COARSE_LOCATION + }) public @Nullable ServiceState getServiceState() { return getServiceStateForSubscriber(getSubId()); } @@ -15216,8 +15221,13 @@ public class TelephonyManager { * <li>Generate the ks_NAF/ ks_Ext_NAF to be returned via the callback.</li> * </ol> * - * <p> Requires Permission: MODIFY_PHONE_STATE or that the calling app has carrier - * privileges (see {@link #hasCarrierPrivileges}). + * <p> Requires Permission: + * <ul> + * <li>{@link android.Manifest.permission#MODIFY_PHONE_STATE},</li> + * <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li> + * <li>or that the caller has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges()}).</li> + * </ul> * @param appType icc application type, like {@link #APPTYPE_USIM} or {@link * #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN} * @param nafId Network Application Function(NAF) fully qualified domain name and @@ -15244,7 +15254,8 @@ public class TelephonyManager { */ @SystemApi @WorkerThread - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresPermission(anyOf = {android.Manifest.permission.MODIFY_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void bootstrapAuthenticationRequest( @UiccAppTypeExt int appType, @NonNull Uri nafId, @NonNull UaSecurityProtocolIdentifier securityProtocol, diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index d58fa912dce2..b503733f8de9 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -28,8 +28,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.provider.Telephony; import android.provider.Telephony.Carriers; -import android.telephony.Annotation; -import android.telephony.Annotation.ApnType; import android.telephony.Annotation.NetworkType; import android.telephony.ServiceState; import android.telephony.TelephonyManager; @@ -116,6 +114,31 @@ public class ApnSetting implements Parcelable { public static final int TYPE_MCX = ApnTypes.MCX; /** APN type for XCAP. */ public static final int TYPE_XCAP = ApnTypes.XCAP; + /** + * APN type for ENTERPRISE. + * @hide + */ + public static final int TYPE_ENTERPRISE = TYPE_XCAP << 1; + + /** @hide */ + @IntDef(flag = true, prefix = {"TYPE_"}, value = { + TYPE_DEFAULT, + TYPE_MMS, + TYPE_SUPL, + TYPE_DUN, + TYPE_HIPRI, + TYPE_FOTA, + TYPE_IMS, + TYPE_CBS, + TYPE_IA, + TYPE_EMERGENCY, + TYPE_MCX, + TYPE_XCAP, + TYPE_ENTERPRISE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ApnType { + } // Possible values for authentication types. /** No authentication type. */ @@ -151,6 +174,7 @@ public class ApnSetting implements Parcelable { TYPE_MMS_STRING, TYPE_SUPL_STRING, TYPE_XCAP_STRING, + TYPE_ENTERPRISE_STRING, }, prefix = "TYPE_", suffix = "_STRING") @Retention(RetentionPolicy.SOURCE) public @interface ApnTypeString {} @@ -291,6 +315,12 @@ public class ApnSetting implements Parcelable { @SystemApi public static final String TYPE_XCAP_STRING = "xcap"; + /** + * APN type for ENTERPRISE traffic. + * @hide + */ + public static final String TYPE_ENTERPRISE_STRING = "enterprise"; + /** @hide */ @IntDef(prefix = { "AUTH_TYPE_" }, value = { @@ -370,6 +400,7 @@ public class ApnSetting implements Parcelable { APN_TYPE_STRING_MAP.put(TYPE_EMERGENCY_STRING, TYPE_EMERGENCY); APN_TYPE_STRING_MAP.put(TYPE_MCX_STRING, TYPE_MCX); APN_TYPE_STRING_MAP.put(TYPE_XCAP_STRING, TYPE_XCAP); + APN_TYPE_STRING_MAP.put(TYPE_ENTERPRISE_STRING, TYPE_ENTERPRISE); APN_TYPE_INT_MAP = new ArrayMap<>(); APN_TYPE_INT_MAP.put(TYPE_DEFAULT, TYPE_DEFAULT_STRING); @@ -384,6 +415,7 @@ public class ApnSetting implements Parcelable { APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, TYPE_EMERGENCY_STRING); APN_TYPE_INT_MAP.put(TYPE_MCX, TYPE_MCX_STRING); APN_TYPE_INT_MAP.put(TYPE_XCAP, TYPE_XCAP_STRING); + APN_TYPE_INT_MAP.put(TYPE_ENTERPRISE, TYPE_ENTERPRISE_STRING); PROTOCOL_STRING_MAP = new ArrayMap<>(); PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP); @@ -1490,7 +1522,7 @@ public class ApnSetting implements Parcelable { * @hide */ @SystemApi - public static @NonNull @ApnTypeString String getApnTypeString(@Annotation.ApnType int apnType) { + public static @NonNull @ApnTypeString String getApnTypeString(@ApnType int apnType) { if (apnType == TYPE_ALL) { return "*"; } @@ -1503,7 +1535,7 @@ public class ApnSetting implements Parcelable { * when provided with an invalid int for compatibility purposes. * @hide */ - public static @NonNull String getApnTypeStringInternal(@Annotation.ApnType int apnType) { + public static @NonNull String getApnTypeStringInternal(@ApnType int apnType) { String result = getApnTypeString(apnType); return TextUtils.isEmpty(result) ? "Unknown" : result; } @@ -1517,7 +1549,7 @@ public class ApnSetting implements Parcelable { * @hide */ @SystemApi - public static @Annotation.ApnType int getApnTypeInt(@NonNull @ApnTypeString String apnType) { + public static @ApnType int getApnTypeInt(@NonNull @ApnTypeString String apnType) { return APN_TYPE_STRING_MAP.getOrDefault(apnType.toLowerCase(), 0); } @@ -2162,7 +2194,7 @@ public class ApnSetting implements Parcelable { public ApnSetting build() { if ((mApnTypeBitmask & (TYPE_DEFAULT | TYPE_MMS | TYPE_SUPL | TYPE_DUN | TYPE_HIPRI | TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX - | TYPE_XCAP)) == 0 + | TYPE_XCAP | TYPE_ENTERPRISE)) == 0 || TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) { return null; } diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index bd4bf0740ca1..a76422977cb6 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -138,6 +138,7 @@ public final class DataCallResponse implements Parcelable { private final Qos mDefaultQos; private final List<QosBearerSession> mQosBearerSessions; private final SliceInfo mSliceInfo; + private final List<TrafficDescriptor> mTrafficDescriptors; /** * @param cause Data call fail cause. {@link DataFailCause#NONE} indicates no error. @@ -189,6 +190,7 @@ public final class DataCallResponse implements Parcelable { mDefaultQos = null; mQosBearerSessions = new ArrayList<>(); mSliceInfo = null; + mTrafficDescriptors = new ArrayList<>(); } private DataCallResponse(@DataFailureCause int cause, long suggestedRetryTime, int id, @@ -198,7 +200,7 @@ public final class DataCallResponse implements Parcelable { @Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6, @HandoverFailureMode int handoverFailureMode, int pduSessionId, @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions, - @Nullable SliceInfo sliceInfo) { + @Nullable SliceInfo sliceInfo, @Nullable List<TrafficDescriptor> trafficDescriptors) { mCause = cause; mSuggestedRetryTime = suggestedRetryTime; mId = id; @@ -219,8 +221,11 @@ public final class DataCallResponse implements Parcelable { mHandoverFailureMode = handoverFailureMode; mPduSessionId = pduSessionId; mDefaultQos = defaultQos; - mQosBearerSessions = qosBearerSessions; + mQosBearerSessions = (qosBearerSessions == null) + ? new ArrayList<>() : new ArrayList<>(qosBearerSessions); mSliceInfo = sliceInfo; + mTrafficDescriptors = (trafficDescriptors == null) + ? new ArrayList<>() : new ArrayList<>(trafficDescriptors); } /** @hide */ @@ -249,6 +254,8 @@ public final class DataCallResponse implements Parcelable { mQosBearerSessions = new ArrayList<>(); source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader()); mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader()); + mTrafficDescriptors = new ArrayList<>(); + source.readList(mTrafficDescriptors, TrafficDescriptor.class.getClassLoader()); } /** @@ -381,7 +388,6 @@ public final class DataCallResponse implements Parcelable { * * @hide */ - @Nullable public Qos getDefaultQos() { return mDefaultQos; @@ -406,6 +412,14 @@ public final class DataCallResponse implements Parcelable { return mSliceInfo; } + /** + * @return The traffic descriptors related to this data connection. + */ + @NonNull + public List<TrafficDescriptor> getTrafficDescriptors() { + return mTrafficDescriptors; + } + @NonNull @Override public String toString() { @@ -429,6 +443,7 @@ public final class DataCallResponse implements Parcelable { .append(" defaultQos=").append(mDefaultQos) .append(" qosBearerSessions=").append(mQosBearerSessions) .append(" sliceInfo=").append(mSliceInfo) + .append(" trafficDescriptors=").append(mTrafficDescriptors) .append("}"); return sb.toString(); } @@ -443,15 +458,22 @@ public final class DataCallResponse implements Parcelable { DataCallResponse other = (DataCallResponse) o; - final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null) ? - mDefaultQos == other.mDefaultQos : - mDefaultQos.equals(other.mDefaultQos); + final boolean isQosSame = (mDefaultQos == null || other.mDefaultQos == null) + ? mDefaultQos == other.mDefaultQos + : mDefaultQos.equals(other.mDefaultQos); - final boolean isQosBearerSessionsSame = (mQosBearerSessions == null || mQosBearerSessions == null) ? - mQosBearerSessions == other.mQosBearerSessions : - mQosBearerSessions.size() == other.mQosBearerSessions.size() + final boolean isQosBearerSessionsSame = + (mQosBearerSessions == null || other.mQosBearerSessions == null) + ? mQosBearerSessions == other.mQosBearerSessions + : mQosBearerSessions.size() == other.mQosBearerSessions.size() && mQosBearerSessions.containsAll(other.mQosBearerSessions); + final boolean isTrafficDescriptorsSame = + (mTrafficDescriptors == null || other.mTrafficDescriptors == null) + ? mTrafficDescriptors == other.mTrafficDescriptors + : mTrafficDescriptors.size() == other.mTrafficDescriptors.size() + && mTrafficDescriptors.containsAll(other.mTrafficDescriptors); + return mCause == other.mCause && mSuggestedRetryTime == other.mSuggestedRetryTime && mId == other.mId @@ -473,7 +495,8 @@ public final class DataCallResponse implements Parcelable { && mPduSessionId == other.mPduSessionId && isQosSame && isQosBearerSessionsSame - && Objects.equals(mSliceInfo, other.mSliceInfo); + && Objects.equals(mSliceInfo, other.mSliceInfo) + && isTrafficDescriptorsSame; } @Override @@ -481,7 +504,7 @@ public final class DataCallResponse implements Parcelable { return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos, - mQosBearerSessions, mSliceInfo); + mQosBearerSessions, mSliceInfo, mTrafficDescriptors); } @Override @@ -517,6 +540,7 @@ public final class DataCallResponse implements Parcelable { } dest.writeList(mQosBearerSessions); dest.writeParcelable(mSliceInfo, flags); + dest.writeList(mTrafficDescriptors); } public static final @android.annotation.NonNull Parcelable.Creator<DataCallResponse> CREATOR = @@ -602,6 +626,8 @@ public final class DataCallResponse implements Parcelable { private SliceInfo mSliceInfo; + private List<TrafficDescriptor> mTrafficDescriptors = new ArrayList<>(); + /** * Default constructor for Builder. */ @@ -841,6 +867,24 @@ public final class DataCallResponse implements Parcelable { } /** + * The traffic descriptors for this data connection, as defined in 3GPP TS 24.526 + * Section 5.2. They are used for URSP traffic matching as described in 3GPP TS 24.526 + * Section 4.2.2. They includes an optional DNN, which, if present, must be used for traffic + * matching; it does not specify the end point to be used for the data call. The end point + * is specified by {@link DataProfile}, which must be used as the end point if one is not + * specified through URSP rules. + * + * @param trafficDescriptors the traffic descriptors for the data call. + * + * @return The same instance of the builder. + */ + public @NonNull Builder setTrafficDescriptors( + @NonNull List<TrafficDescriptor> trafficDescriptors) { + mTrafficDescriptors = trafficDescriptors; + return this; + } + + /** * Build the DataCallResponse. * * @return the DataCallResponse object. @@ -849,7 +893,7 @@ public final class DataCallResponse implements Parcelable { return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, - mDefaultQos, mQosBearerSessions, mSliceInfo); + mDefaultQos, mQosBearerSessions, mSliceInfo, mTrafficDescriptors); } } } diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index 484c318c1ac0..f5f29c65b7cd 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -200,6 +200,17 @@ public abstract class DataService extends Service { * handover is occurring from EPDG to 5G. If the slice passed is rejected, then * {@link DataCallResponse#getCause()} is * {@link android.telephony.DataFailCause#SLICE_REJECTED}. + * @param trafficDescriptor {@link TrafficDescriptor} for which data connection needs to be + * established. It is used for URSP traffic matching as described in 3GPP TS 24.526 + * Section 4.2.2. It includes an optional DNN which, if present, must be used for + * traffic matching; it does not specify the end point to be used for the data call. + * @param matchAllRuleAllowed Indicates if using default match-all URSP rule for this + * request is allowed. If false, this request must not use the match-all URSP rule + * and if a non-match-all rule is not found (or if URSP rules are not available) then + * {@link DataCallResponse#getCause()} is + * {@link android.telephony.DataFailCause#MATCH_ALL_RULE_NOT_ALLOWED}. This is needed + * as some requests need to have a hard failure if the intention cannot be met, + * for example, a zero-rating slice. * @param callback The result callback for this request. */ public void setupDataCall(int accessNetworkType, @NonNull DataProfile dataProfile, @@ -207,6 +218,7 @@ public abstract class DataService extends Service { @SetupDataReason int reason, @Nullable LinkProperties linkProperties, @IntRange(from = 0, to = 15) int pduSessionId, @Nullable SliceInfo sliceInfo, + @Nullable TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, @NonNull DataServiceCallback callback) { /* Call the old version since the new version isn't supported */ setupDataCall(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason, @@ -403,10 +415,13 @@ public abstract class DataService extends Service { public final LinkProperties linkProperties; public final int pduSessionId; public final SliceInfo sliceInfo; + public final TrafficDescriptor trafficDescriptor; + public final boolean matchAllRuleAllowed; public final IDataServiceCallback callback; SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming, - boolean allowRoaming, int reason, LinkProperties linkProperties, - int pduSessionId, SliceInfo sliceInfo, IDataServiceCallback callback) { + boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId, + SliceInfo sliceInfo, TrafficDescriptor trafficDescriptor, + boolean matchAllRuleAllowed, IDataServiceCallback callback) { this.accessNetworkType = accessNetworkType; this.dataProfile = dataProfile; this.isRoaming = isRoaming; @@ -415,6 +430,8 @@ public abstract class DataService extends Service { this.reason = reason; this.pduSessionId = pduSessionId; this.sliceInfo = sliceInfo; + this.trafficDescriptor = trafficDescriptor; + this.matchAllRuleAllowed = matchAllRuleAllowed; this.callback = callback; } } @@ -525,7 +542,8 @@ public abstract class DataService extends Service { setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming, setupDataCallRequest.allowRoaming, setupDataCallRequest.reason, setupDataCallRequest.linkProperties, setupDataCallRequest.pduSessionId, - setupDataCallRequest.sliceInfo, + setupDataCallRequest.sliceInfo, setupDataCallRequest.trafficDescriptor, + setupDataCallRequest.matchAllRuleAllowed, (setupDataCallRequest.callback != null) ? new DataServiceCallback(setupDataCallRequest.callback) : null); @@ -690,11 +708,12 @@ public abstract class DataService extends Service { public void setupDataCall(int slotIndex, int accessNetworkType, DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, LinkProperties linkProperties, int pduSessionId, SliceInfo sliceInfo, + TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, IDataServiceCallback callback) { mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, slotIndex, 0, new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming, allowRoaming, reason, linkProperties, pduSessionId, sliceInfo, - callback)) + trafficDescriptor, matchAllRuleAllowed, callback)) .sendToTarget(); } diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl index e0b9a1a9bb5a..81f5fd3b69a9 100644 --- a/telephony/java/android/telephony/data/IDataService.aidl +++ b/telephony/java/android/telephony/data/IDataService.aidl @@ -20,6 +20,7 @@ import android.net.LinkProperties; import android.telephony.data.DataProfile; import android.telephony.data.IDataServiceCallback; import android.telephony.data.SliceInfo; +import android.telephony.data.TrafficDescriptor; /** * {@hide} @@ -30,7 +31,9 @@ oneway interface IDataService void removeDataServiceProvider(int slotId); void setupDataCall(int slotId, int accessNetwork, in DataProfile dataProfile, boolean isRoaming, boolean allowRoaming, int reason, in LinkProperties linkProperties, - int pduSessionId, in SliceInfo sliceInfo, IDataServiceCallback callback); + int pduSessionId, in SliceInfo sliceInfo, + in TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed, + IDataServiceCallback callback); void deactivateDataCall(int slotId, int cid, int reason, IDataServiceCallback callback); void setInitialAttachApn(int slotId, in DataProfile dataProfile, boolean isRoaming, IDataServiceCallback callback); diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.aidl b/telephony/java/android/telephony/data/TrafficDescriptor.aidl new file mode 100644 index 000000000000..a9c7604a91b6 --- /dev/null +++ b/telephony/java/android/telephony/data/TrafficDescriptor.aidl @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** @hide */ +package android.telephony.data; + +parcelable TrafficDescriptor; diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.java b/telephony/java/android/telephony/data/TrafficDescriptor.java new file mode 100644 index 000000000000..480379d641b6 --- /dev/null +++ b/telephony/java/android/telephony/data/TrafficDescriptor.java @@ -0,0 +1,111 @@ +/* + * 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.telephony.data; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for URSP traffic + * matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an optional DNN, which, + * if present, must be used for traffic matching; it does not specify the end point to be used for + * the data call. + * @hide + */ +@SystemApi +public final class TrafficDescriptor implements Parcelable { + private final String mDnn; + private final String mOsAppId; + + private TrafficDescriptor(@NonNull Parcel in) { + mDnn = in.readString(); + mOsAppId = in.readString(); + } + + /** + * Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2 + * @param dnn optional DNN, which must be used for traffic matching, if present + * @param osAppId OsId + osAppId of the traffic descriptor + */ + public TrafficDescriptor(@Nullable String dnn, @Nullable String osAppId) { + mDnn = dnn; + mOsAppId = osAppId; + } + + /** + * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003. + * @return the DNN of this traffic descriptor. + */ + public @Nullable String getDnn() { + return mDnn; + } + + /** + * OsAppId represents the OsId + OsAppId as defined in 3GPP TS 24.526 Section 5.2. + * @return the OS App ID of this traffic descriptor. + */ + public @Nullable String getOsAppId() { + return mOsAppId; + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull @Override + public String toString() { + return "TrafficDescriptor={mDnn=" + mDnn + ", mOsAppId=" + mOsAppId + "}"; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mDnn); + dest.writeString(mOsAppId); + } + + public static final @NonNull Parcelable.Creator<TrafficDescriptor> CREATOR = + new Parcelable.Creator<TrafficDescriptor>() { + @Override + public @NonNull TrafficDescriptor createFromParcel(@NonNull Parcel source) { + return new TrafficDescriptor(source); + } + + @Override + public @NonNull TrafficDescriptor[] newArray(int size) { + return new TrafficDescriptor[size]; + } + }; + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TrafficDescriptor that = (TrafficDescriptor) o; + return Objects.equals(mDnn, that.mDnn) && Objects.equals(mOsAppId, that.mOsAppId); + } + + @Override + public int hashCode() { + return Objects.hash(mDnn, mOsAppId); + } +} diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index a9dae898fc9f..9bb4db8edf79 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -282,6 +282,15 @@ public final class ImsCallProfile implements Parcelable { public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL"; /** + * Boolean extra indicating whether the call is a business call. + * + * This extra will be set to {@code true} if and only if the SIP INVITE headers contain the + * "Organization" header. + */ + public static final String EXTRA_IS_BUSINESS_CALL = + "android.telephony.ims.extra.IS_BUSINESS_CALL"; + + /** * Values for EXTRA_OIR / EXTRA_CNAP */ /** @@ -791,7 +800,9 @@ public final class ImsCallProfile implements Parcelable { + ", emergencyCallTesting=" + mEmergencyCallTesting + ", hasKnownUserIntentEmergency=" + mHasKnownUserIntentEmergency + ", mRestrictCause=" + mRestrictCause - + ", mCallerNumberVerstat= " + mCallerNumberVerificationStatus + " }"; + + ", mCallerNumberVerstat= " + mCallerNumberVerificationStatus + + ", mAcceptedRtpHeaderExtensions= " + mAcceptedRtpHeaderExtensionTypes + + " }"; } @Override diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 31ba8530e623..6fda2e12fffd 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -32,6 +32,7 @@ import android.os.ServiceSpecificException; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; +import android.telephony.TelephonyManager; import android.telephony.ims.aidl.IImsConfigCallback; import android.telephony.ims.aidl.IRcsConfigCallback; import android.telephony.ims.feature.MmTelFeature; @@ -1300,7 +1301,7 @@ public class ProvisioningManager { * provisioning. * <p> * Requires Permission: Manifest.permission.MODIFY_PHONE_STATE or that the calling app has - * carrier privileges (see {@link #hasCarrierPrivileges}). + * carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed. * @param isCompressed The XML file is compressed in gzip format and must be decompressed * before being read. @@ -1330,7 +1331,7 @@ public class ProvisioningManager { * the intent is valid. and {@link #EXTRA_STATUS} to specify RCS VoLTE single registration * status. */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE = "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE"; @@ -1375,7 +1376,7 @@ public class ProvisioningManager { * provisioning status events {@link #registerRcsProvisioningChangedCallback} * @param rcc RCS client configuration {@link RcsClientConfiguration} */ - @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void setRcsClientConfiguration( @NonNull RcsClientConfiguration rcc) throws ImsException { try { @@ -1390,6 +1391,14 @@ public class ProvisioningManager { /** * Returns a flag to indicate whether or not the device supports IMS single registration for * MMTEL and RCS features as well as if the carrier has provisioned the feature. + * + * <p> Requires Permission: + * <ul> + * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li> + * <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li> + * <li>or that the caller has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges()}).</li> + * </ul> * @return true if IMS single registration is capable at this time, or false otherwise * @throws ImsException If the remote ImsService is not available for * any reason or the subscription associated with this instance is no @@ -1398,7 +1407,8 @@ public class ProvisioningManager { * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION for whether or not this * device supports IMS single registration. */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isRcsVolteSingleRegistrationCapable() throws ImsException { try { return getITelephony().isRcsVolteSingleRegistrationCapable(mSubId); @@ -1408,36 +1418,44 @@ public class ProvisioningManager { } /** - * Registers a new {@link RcsProvisioningCallback} to listen to changes to - * RCS provisioning xml. - * - * <p>RCS application must be the default messaging application and must - * have already registered its {@link RcsClientConfiguration} by using - * {@link #setRcsClientConfiguration} before it registers the provisioning - * callback. If ProvisioningManager has a valid RCS configuration at the - * time of callback registration and a reconfiguration is not required - * due to RCS client parameters change, then the callback shall be invoked - * immediately with the xml. - * When the subscription associated with this callback is removed (SIM removed, - * ESIM swap,etc...), this callback will automatically be removed. - * - * @param executor The {@link Executor} to call the callback methods on - * @param callback The rcs provisioning callback to be registered. - * @see #unregisterRcsProvisioningChangedCallback(RcsProvisioningCallback) - * @see SubscriptionManager.OnSubscriptionsChangedListener - * @throws IllegalArgumentException if the subscription associated with this - * callback is not active (SIM is not inserted, ESIM inactive) or the - * subscription is invalid. - * @throws ImsException if the subscription associated with this callback is - * valid, but the {@link ImsService} associated with the subscription is not - * available. This can happen if the service crashed, for example. - * It shall also throw this exception when the RCS client parameters for the - * application are not valid. In that case application must set the client - * params (See {@link #setRcsClientConfiguration}) and re register the - * callback. - * See {@link ImsException#getCode()} for a more detailed reason. - */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + * Registers a new {@link RcsProvisioningCallback} to listen to changes to + * RCS provisioning xml. + * + * <p>RCS application must be the default messaging application and must + * have already registered its {@link RcsClientConfiguration} by using + * {@link #setRcsClientConfiguration} before it registers the provisioning + * callback. If ProvisioningManager has a valid RCS configuration at the + * time of callback registration and a reconfiguration is not required + * due to RCS client parameters change, then the callback shall be invoked + * immediately with the xml. + * When the subscription associated with this callback is removed (SIM removed, + * ESIM swap,etc...), this callback will automatically be removed. + * <p> Requires Permission: + * <ul> + * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li> + * <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li> + * <li>or that the caller has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges()}).</li> + * </ul> + * + * @param executor The {@link Executor} to call the callback methods on + * @param callback The rcs provisioning callback to be registered. + * @see #unregisterRcsProvisioningChangedCallback(RcsProvisioningCallback) + * @see SubscriptionManager.OnSubscriptionsChangedListener + * @throws IllegalArgumentException if the subscription associated with this + * callback is not active (SIM is not inserted, ESIM inactive) or the + * subscription is invalid. + * @throws ImsException if the subscription associated with this callback is + * valid, but the {@link ImsService} associated with the subscription is not + * available. This can happen if the service crashed, for example. + * It shall also throw this exception when the RCS client parameters for the + * application are not valid. In that case application must set the client + * params (See {@link #setRcsClientConfiguration}) and re register the + * callback. + * See {@link ImsException#getCode()} for a more detailed reason. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerRcsProvisioningChangedCallback( @NonNull @CallbackExecutor Executor executor, @NonNull RcsProvisioningCallback callback) throws ImsException { @@ -1459,13 +1477,22 @@ public class ProvisioningManager { * removed, ESIM swap, etc...), this callback will automatically be * removed. If this method is called for an inactive subscription, it * will result in a no-op. + * <p> Requires Permission: + * <ul> + * <li>{@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE},</li> + * <li>{@link android.Manifest.permission#PERFORM_IMS_SINGLE_REGISTRATION},</li> + * <li>or that the caller has carrier privileges (see + * {@link TelephonyManager#hasCarrierPrivileges()}).</li> + * </ul> + * * @param callback The existing {@link RcsProvisioningCallback} to be * removed. * @see #registerRcsProvisioningChangedCallback * @throws IllegalArgumentException if the subscription associated with this callback is * invalid. */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void unregisterRcsProvisioningChangedCallback( @NonNull RcsProvisioningCallback callback) { try { @@ -1480,7 +1507,7 @@ public class ProvisioningManager { * Reconfiguration triggered by the RCS application. Most likely cause * is the 403 forbidden to a HTTP request. */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerRcsReconfiguration() { try { getITelephony().triggerRcsReconfiguration(mSubId); diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java index 5eb75e762fc9..bdf628b4d339 100644 --- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.StringDef; import android.annotation.SystemApi; import android.net.Uri; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -39,6 +40,15 @@ import java.util.List; public final class RcsContactPresenceTuple implements Parcelable { /** + * The service ID used to indicate that service discovery via presence is available. + * <p> + * See RCC.07 v5.0 specification for more information. + * @hide + */ + public static final String SERVICE_ID_PRESENCE = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcse.dp"; + + /** * The service ID used to indicate that MMTEL service is available. * <p> * See the GSMA RCC.07 specification for more information. @@ -329,6 +339,13 @@ public final class RcsContactPresenceTuple implements Parcelable { public @NonNull @DuplexMode List<String> getUnsupportedDuplexModes() { return Collections.unmodifiableList(mUnsupportedDuplexModeList); } + + @Override + public String toString() { + return "servCaps{" + "a=" + mIsAudioCapable + ", v=" + mIsVideoCapable + + ", supported=" + mSupportedDuplexModeList + ", unsupported=" + + mUnsupportedDuplexModeList + '}'; + } } /** @@ -487,4 +504,36 @@ public final class RcsContactPresenceTuple implements Parcelable { public @Nullable ServiceCapabilities getServiceCapabilities() { return mServiceCapabilities; } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("{"); + if (Build.IS_ENG) { + builder.append("u="); + builder.append(mContactUri); + } else { + builder.append("u="); + builder.append(mContactUri != null ? "XXX" : "null"); + } + builder.append(", id="); + builder.append(mServiceId); + builder.append(", v="); + builder.append(mServiceVersion); + builder.append(", s="); + builder.append(mStatus); + if (mTimestamp != null) { + builder.append(", timestamp="); + builder.append(mTimestamp); + } + if (mServiceDescription != null) { + builder.append(", servDesc="); + builder.append(mServiceDescription); + } + if (mServiceCapabilities != null) { + builder.append(", servCaps="); + builder.append(mServiceCapabilities); + } + builder.append("}"); + return builder.toString(); + } } diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index fe855023f5d0..9299fed1e27d 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.Uri; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -344,4 +345,39 @@ public final class RcsContactUceCapability implements Parcelable { public @NonNull Uri getContactUri() { return mContactUri; } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("RcsContactUceCapability"); + if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) { + builder.append("(presence) {"); + } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) { + builder.append("(options) {"); + } else { + builder.append("(?) {"); + } + if (Build.IS_ENG) { + builder.append("uri="); + builder.append(mContactUri); + } else { + builder.append("uri (isNull)="); + builder.append(mContactUri != null ? "XXX" : "null"); + } + builder.append(", sourceType="); + builder.append(mSourceType); + builder.append(", requestResult="); + builder.append(mRequestResult); + + if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) { + builder.append(", presenceTuples={"); + builder.append(mPresenceTuples); + builder.append("}"); + } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) { + builder.append(", featureTags={"); + builder.append(mFeatureTags); + builder.append("}"); + } + + return builder.toString(); + } } diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtension.java b/telephony/java/android/telephony/ims/RtpHeaderExtension.java index f9ab7016facb..8815cefae241 100644 --- a/telephony/java/android/telephony/ims/RtpHeaderExtension.java +++ b/telephony/java/android/telephony/ims/RtpHeaderExtension.java @@ -134,4 +134,19 @@ public final class RtpHeaderExtension implements Parcelable { result = 31 * result + Arrays.hashCode(mExtensionData); return result; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("RtpHeaderExtension{mLocalIdentifier="); + sb.append(mLocalIdentifier); + sb.append(", mData="); + for (byte b : mExtensionData) { + sb.append(Integer.toBinaryString(b)); + sb.append("b_"); + } + sb.append("}"); + + return sb.toString(); + } } diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java index e1d39c217395..af4e23476331 100644 --- a/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java +++ b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java @@ -133,4 +133,16 @@ public final class RtpHeaderExtensionType implements Parcelable { public int hashCode() { return Objects.hash(mLocalIdentifier, mUri); } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("RtpHeaderExtensionType{mLocalIdentifier="); + sb.append(mLocalIdentifier); + sb.append(", mUri="); + sb.append(mUri); + sb.append("}"); + + return sb.toString(); + } } diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java index 04421c9a2449..171842b26cb0 100644 --- a/telephony/java/android/telephony/ims/SipDelegateManager.java +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -275,7 +275,8 @@ public class SipDelegateManager { * @see CarrierConfigManager.Ims#KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL * @see PackageManager#FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isSupported() throws ImsException { try { IImsRcsController controller = mBinderCache.getBinder(); @@ -317,7 +318,7 @@ public class SipDelegateManager { * @throws ImsException Thrown if there was a problem communicating with the ImsService * associated with this SipDelegateManager. See {@link ImsException#getCode()}. */ - @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor executor, @NonNull DelegateConnectionStateCallback dc, @NonNull DelegateConnectionMessageCallback mc) throws ImsException { @@ -351,7 +352,7 @@ public class SipDelegateManager { * @param delegateConnection The SipDelegateConnection to destroy. * @param reason The reason for why this SipDelegateConnection was destroyed. */ - @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull SipDelegateConnection delegateConnection, @SipDelegateDestroyReason int reason) { @@ -392,6 +393,7 @@ public class SipDelegateManager { * this condition. May be {@code null} if there was no reason String provided from the * network. */ + @RequiresPermission(Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerFullNetworkRegistration(@NonNull SipDelegateConnection connection, @IntRange(from = 100, to = 699) int sipCode, @Nullable String sipReason) { if (connection == null) { diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java index 4967e5da7c9a..62955487897f 100644 --- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java +++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java @@ -25,7 +25,6 @@ import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.RcsFeature; -import android.util.Log; import java.util.List; @@ -44,30 +43,12 @@ public interface CapabilityExchangeEventListener { * Respond to a remote capability request from the contact specified with the * capabilities of this device. * @param ownCapabilities The capabilities of this device. - * @hide - */ - default void onRespondToCapabilityRequest( - @NonNull RcsContactUceCapability ownCapabilities) {} - - /** - * Respond to a remote capability request from the contact specified with the - * capabilities of this device. - * @param ownCapabilities The capabilities of this device. * @param isBlocked Whether or not the user has blocked the number requesting the * capabilities of this device. If true, the device should respond to the OPTIONS * request with a 200 OK response and no capabilities. */ - default void onRespondToCapabilityRequest(@NonNull RcsContactUceCapability ownCapabilities, - boolean isBlocked) { - Log.w("CapabilityExchangeEventListener", "implement " - + "onRespondToCapabilityRequest(RcsContactUceCapability, boolean) instead!"); - // Fall back to old implementation - if (isBlocked) { - onRespondToCapabilityRequestWithError(200, "OK"); - } else { - onRespondToCapabilityRequest(ownCapabilities); - } - } + void onRespondToCapabilityRequest(@NonNull RcsContactUceCapability ownCapabilities, + boolean isBlocked); /** * Respond to a remote capability request from the contact specified with the diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index cc050becfb25..34984e05e181 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -37,6 +37,7 @@ import com.android.internal.telephony.util.RemoteCallbackListExt; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; +import java.util.Arrays; import java.util.HashMap; /** @@ -368,7 +369,13 @@ public class ImsConfigImplBase { } private void onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) { - mRcsConfigData = isCompressed ? RcsConfig.decompressGzip(config) : config; + // cache uncompressed config + config = isCompressed ? RcsConfig.decompressGzip(config) : config; + if (Arrays.equals(mRcsConfigData, config)) { + return; + } + mRcsConfigData = config; + // can be null in testing if (mRcsCallbacks != null) { mRcsCallbacks.broadcastAction(c -> { diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java index 5f8e93d02a00..8ad40ed1032c 100644 --- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java @@ -57,10 +57,10 @@ public class ImsEcbmImplBase { } else if (listener != null && mListener == null) { mListener = listener; } else { - // Fail fast here instead of silently overwriting the listener to another - // listener due to another connection connecting. - throw new IllegalStateException("ImsEcbmImplBase: Listener already set by " - + "another connection."); + // Warn that the listener is being replaced while active + Log.w(TAG, "setListener is being called when there is already an active " + + "listener"); + mListener = listener; } } } diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java index 8e961acc7b36..ec1c7b3a92a8 100644 --- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java @@ -62,10 +62,10 @@ public class ImsMultiEndpointImplBase { } else if (listener != null && mListener == null) { mListener = listener; } else { - // Fail fast here instead of silently overwriting the listener to another - // listener due to another connection connecting. - throw new IllegalStateException("ImsMultiEndpointImplBase: Listener already" - + " set by another connection."); + // Warn that the listener is being replaced while active + Log.w(TAG, "setListener is being called when there is already an active " + + "listener"); + mListener = listener; } } } diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java index 83b89aa8e814..eb3e8ed5a8e4 100644 --- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java @@ -224,11 +224,10 @@ public class ImsUtImplBase { } else if (listener != null && mUtListener == null) { mUtListener = new ImsUtListener(listener); } else { - // This is a limitation of the current API surface, there can only be one - // listener connected. Fail fast instead of silently overwriting the other - // listener. - throw new IllegalStateException("ImsUtImplBase#setListener: listener already " - + "set by another connected interface!"); + // Warn that the listener is being replaced while active + Log.w(TAG, "setListener is being called when there is already an active " + + "listener"); + mUtListener = new ImsUtListener(listener); } } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 1d049530ba77..e87f3d9aec76 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2055,6 +2055,11 @@ interface ITelephony { int setImsProvisioningString(int subId, int key, String value); /** + * Start emergency callback mode for testing. + */ + void startEmergencyCallbackMode(); + + /** * Update Emergency Number List for Test Mode. */ void updateEmergencyNumberListTestMode(int action, in EmergencyNumber num); @@ -2320,6 +2325,16 @@ interface ITelephony { void triggerRcsReconfiguration(int subId); /** + * Enables or disables the test mode for RCS VoLTE single registration. + */ + void setRcsSingleRegistrationTestModeEnabled(boolean enabled); + + /** + * Gets the test mode for RCS VoLTE single registration. + */ + boolean getRcsSingleRegistrationTestModeEnabled(); + + /** * Overrides the config of RCS VoLTE single registration enabled for the device. */ void setDeviceSingleRegistrationEnabledOverride(String enabled); @@ -2340,6 +2355,11 @@ interface ITelephony { void sendDeviceToDeviceMessage(int message, int value); /** + * Sets the specified transport active; only for use through shell. + */ + void setActiveDeviceToDeviceTransport(String transport); + + /** * Gets the config of RCS VoLTE single registration enabled for the carrier/subscription. */ boolean getCarrierSingleRegistrationEnabled(int subId); diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index 151187c5071f..3a99f0e010c6 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -126,6 +126,7 @@ public class PhoneConstants { * connections.<br/> * APN_TYPE_ALL is a special type to indicate that this APN entry can * service all data connections. + * TODO: remove these and use the reference to ApnSetting.TYPE_XXX_STRING instead */ public static final String APN_TYPE_ALL = ApnSetting.TYPE_ALL_STRING; /** APN type for default data traffic */ @@ -153,20 +154,8 @@ public class PhoneConstants { public static final String APN_TYPE_MCX = ApnSetting.TYPE_MCX_STRING; /** APN type for XCAP */ public static final String APN_TYPE_XCAP = ApnSetting.TYPE_XCAP_STRING; - /** Array of all APN types */ - public static final String[] APN_TYPES = {APN_TYPE_DEFAULT, - APN_TYPE_MMS, - APN_TYPE_SUPL, - APN_TYPE_DUN, - APN_TYPE_HIPRI, - APN_TYPE_FOTA, - APN_TYPE_IMS, - APN_TYPE_CBS, - APN_TYPE_IA, - APN_TYPE_EMERGENCY, - APN_TYPE_MCX, - APN_TYPE_XCAP, - }; + // /** APN type for enterprise */ + // public static final String APN_TYPE_ENTERPRISE = ApnSetting.TYPE_ENTERPRISE_STRING; public static final int RIL_CARD_MAX_APPS = 8; diff --git a/test-base/Android.bp b/test-base/Android.bp index c7c9fc739189..9bd639b63ae0 100644 --- a/test-base/Android.bp +++ b/test-base/Android.bp @@ -19,6 +19,16 @@ // This contains the junit.framework and android.test classes that were in // Android API level 25 excluding those from android.test.runner. // Also contains the com.android.internal.util.Predicate[s] classes. +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 + // SPDX-license-identifier-CPL-1.0 + default_applicable_licenses: ["frameworks_base_license"], +} + java_sdk_library { name: "android.test.base", @@ -39,6 +49,12 @@ java_sdk_library { compile_dex: true, default_to_stubs: true, + + // Additional hiddenapi annotations are provided in a separate module. + // TODO(b/180295980) - investigate whether this can be removed + hiddenapi_additional_annotations: [ + "android.test.base-hiddenapi-annotations", + ], } // Build the android.test.base_static library @@ -81,8 +97,9 @@ java_library_static { // =============================================== // This contains the android.test classes from android.test.base plus // the com.android.internal.util.Predicate[s] classes. This is only -// intended for inclusion in android.test.legacy and must not be used -// elsewhere. +// intended for inclusion in android.test.legacy and in +// android.test.base-hiddenapi-annotations to avoid a dependency cycle and must +// not be used elsewhere. java_library_static { name: "android.test.base-minus-junit", diff --git a/test-base/hiddenapi/Android.bp b/test-base/hiddenapi/Android.bp index c202467517aa..1466590ef311 100644 --- a/test-base/hiddenapi/Android.bp +++ b/test-base/hiddenapi/Android.bp @@ -14,19 +14,29 @@ // 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"], +} + // Provided solely to contribute information about which hidden parts of the android.test.base // library are used by apps. The source files are stubs of the actual files in ../src which use the // UnsupportedAppUsage annotation to tag those methods that are accessible via the hiddenapi. -// Relies on the convention that modules with name <x>-hiddenapi provide hiddenapi information for -// module <x> that is on the bootclasspath. java_library { - name: "android.test.base-hiddenapi", + name: "android.test.base-hiddenapi-annotations", compile_dex: true, srcs: ["src/**/*.java"], libs: [ - "android.test.base", + // Use this instead of `android.test.base` to avoid a dependency cycle + // as `android.test.base` depends on this. + "android.test.base-minus-junit", + "junit", "unsupportedappusage", ], } diff --git a/test-legacy/Android.mk b/test-legacy/Android.mk index af26c5b80717..ce5e4cf46695 100644 --- a/test-legacy/Android.mk +++ b/test-legacy/Android.mk @@ -27,6 +27,9 @@ ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK))) include $(CLEAR_VARS) LOCAL_MODULE := android.test.legacy +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-Unicode-DFS +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE LOCAL_SDK_VERSION := current diff --git a/test-mock/Android.bp b/test-mock/Android.bp index 7d0f92fac4c7..a2447d71c3bd 100644 --- a/test-mock/Android.bp +++ b/test-mock/Android.bp @@ -16,6 +16,15 @@ // Build the android.test.mock library // =================================== +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_sdk_library { name: "android.test.mock", diff --git a/test-runner/Android.bp b/test-runner/Android.bp index 1f6db8403eee..fe007e39f717 100644 --- a/test-runner/Android.bp +++ b/test-runner/Android.bp @@ -16,6 +16,16 @@ // Build the android.test.runner library // ===================================== +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 + // SPDX-license-identifier-CPL-1.0 + default_applicable_licenses: ["frameworks_base_license"], +} + java_sdk_library { name: "android.test.runner", diff --git a/test-runner/tests/Android.bp b/test-runner/tests/Android.bp index d74cee4937c9..ac21bcb9d124 100644 --- a/test-runner/tests/Android.bp +++ b/test-runner/tests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "FrameworkTestRunnerTests", diff --git a/tests/AccessibilityEventsLogger/Android.bp b/tests/AccessibilityEventsLogger/Android.bp index ead165602254..c403f9bf961e 100644 --- a/tests/AccessibilityEventsLogger/Android.bp +++ b/tests/AccessibilityEventsLogger/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "AccessibilityEventsLogger", srcs: ["**/*.java"], diff --git a/tests/AccessoryDisplay/common/Android.bp b/tests/AccessoryDisplay/common/Android.bp index 3ce4c5718d13..fd3af1ecf1c9 100644 --- a/tests/AccessoryDisplay/common/Android.bp +++ b/tests/AccessoryDisplay/common/Android.bp @@ -13,6 +13,15 @@ // limitations under the License. // Build the application. +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_static { name: "AccessoryDisplayCommon", sdk_version: "current", diff --git a/tests/AccessoryDisplay/sink/Android.bp b/tests/AccessoryDisplay/sink/Android.bp index 4e50a81d8c24..d825c60b437f 100644 --- a/tests/AccessoryDisplay/sink/Android.bp +++ b/tests/AccessoryDisplay/sink/Android.bp @@ -13,6 +13,15 @@ // limitations under the License. // Build the application. +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"], +} + android_test { name: "AccessoryDisplaySink", sdk_version: "current", diff --git a/tests/AccessoryDisplay/source/Android.bp b/tests/AccessoryDisplay/source/Android.bp index 6d8087f5e7dd..6ed752ee2c8f 100644 --- a/tests/AccessoryDisplay/source/Android.bp +++ b/tests/AccessoryDisplay/source/Android.bp @@ -13,6 +13,15 @@ // limitations under the License. // Build the application. +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"], +} + android_test { name: "AccessoryDisplaySource", sdk_version: "current", diff --git a/tests/ActivityManagerPerfTests/stub-app/Android.bp b/tests/ActivityManagerPerfTests/stub-app/Android.bp index a3c1f5b2f17d..19225e4c879e 100644 --- a/tests/ActivityManagerPerfTests/stub-app/Android.bp +++ b/tests/ActivityManagerPerfTests/stub-app/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "ActivityManagerPerfTestsStubApp1", static_libs: ["ActivityManagerPerfTestsUtils"], @@ -65,4 +74,3 @@ android_test_helper_app { "--auto-add-overlay", ], } - diff --git a/tests/ActivityManagerPerfTests/test-app/Android.bp b/tests/ActivityManagerPerfTests/test-app/Android.bp index ef9d587581c4..5fd1d5a0ae69 100644 --- a/tests/ActivityManagerPerfTests/test-app/Android.bp +++ b/tests/ActivityManagerPerfTests/test-app/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ActivityManagerPerfTestsTestApp", srcs: ["src/**/*.java"], diff --git a/tests/ActivityManagerPerfTests/tests/Android.bp b/tests/ActivityManagerPerfTests/tests/Android.bp index 2ae2cc49bb7a..c8dbf811c69b 100644 --- a/tests/ActivityManagerPerfTests/tests/Android.bp +++ b/tests/ActivityManagerPerfTests/tests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ActivityManagerPerfTests", srcs: ["src/**/*.java"], diff --git a/tests/ActivityManagerPerfTests/utils/Android.bp b/tests/ActivityManagerPerfTests/utils/Android.bp index 766c3acf3c09..99c43c8d8fca 100644 --- a/tests/ActivityManagerPerfTests/utils/Android.bp +++ b/tests/ActivityManagerPerfTests/utils/Android.bp @@ -12,6 +12,15 @@ // 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_test { name: "ActivityManagerPerfTestsUtils", sdk_version: "current", diff --git a/tests/ActivityTests/Android.bp b/tests/ActivityTests/Android.bp index 01828624fa4a..be6096fb31cf 100644 --- a/tests/ActivityTests/Android.bp +++ b/tests/ActivityTests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "ActivityTest", srcs: ["**/*.java"], diff --git a/tests/ActivityViewTest/Android.bp b/tests/ActivityViewTest/Android.bp index e7b8c8e1d058..95178a0fb9a5 100644 --- a/tests/ActivityViewTest/Android.bp +++ b/tests/ActivityViewTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "ActivityViewTest", srcs: ["src/**/*.java"], diff --git a/tests/AmSlam/Android.bp b/tests/AmSlam/Android.bp index a8e575a39da4..cc33d88ba363 100644 --- a/tests/AmSlam/Android.bp +++ b/tests/AmSlam/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "AmSlam", srcs: ["**/*.java"], diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp index e2d2ecaf1684..4e98f4264e11 100644 --- a/tests/ApkVerityTest/Android.bp +++ b/tests/ApkVerityTest/Android.bp @@ -12,6 +12,15 @@ // 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_test_host { name: "ApkVerityTest", srcs: ["src/**/*.java"], diff --git a/tests/ApkVerityTest/ApkVerityTestApp/Android.bp b/tests/ApkVerityTest/ApkVerityTestApp/Android.bp index 69632b215822..adf8f9f9d321 100644 --- a/tests/ApkVerityTest/ApkVerityTestApp/Android.bp +++ b/tests/ApkVerityTest/ApkVerityTestApp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "ApkVerityTestApp", manifest: "AndroidManifest.xml", diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp index 8f2d4bc70fa0..0b5f0f611916 100644 --- a/tests/ApkVerityTest/block_device_writer/Android.bp +++ b/tests/ApkVerityTest/block_device_writer/Android.bp @@ -14,6 +14,15 @@ // This is a cc_test just because it supports test_suites. This should be converted to something // like cc_binary_test_helper once supported, thus auto_gen_config:false below. +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"], +} + cc_test { // Depending on how the test runs, the executable may be uploaded to different location. // Before the bug in the file pusher is fixed, workaround by making the name unique. diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java index ab3572ba2173..d96005b8a71a 100644 --- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java +++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java @@ -95,10 +95,13 @@ public class ApkVerityTest extends BaseHostJUnit4Test { new AddFsVerityCertRule(this, CERT_PATH); private ITestDevice mDevice; + private boolean mDmRequireFsVerity; @Before public void setUp() throws DeviceNotAvailableException { mDevice = getDevice(); + mDmRequireFsVerity = "true".equals( + mDevice.getProperty("pm.dexopt.dm.require_fsverity")); uninstallPackage(TARGET_PACKAGE); } @@ -124,7 +127,7 @@ public class ApkVerityTest extends BaseHostJUnit4Test { verifyInstalledFiles( INSTALLED_BASE_APK, INSTALLED_BASE_APK_FSV_SIG); - verifyInstalledFilesHaveFsverity(); + verifyInstalledFilesHaveFsverity(INSTALLED_BASE_APK); } @Test @@ -151,7 +154,9 @@ public class ApkVerityTest extends BaseHostJUnit4Test { INSTALLED_BASE_APK_FSV_SIG, INSTALLED_SPLIT_APK, INSTALLED_SPLIT_APK_FSV_SIG); - verifyInstalledFilesHaveFsverity(); + verifyInstalledFilesHaveFsverity( + INSTALLED_BASE_APK, + INSTALLED_SPLIT_APK); } @Test @@ -167,7 +172,9 @@ public class ApkVerityTest extends BaseHostJUnit4Test { INSTALLED_BASE_APK_FSV_SIG, INSTALLED_BASE_DM, INSTALLED_BASE_DM_FSV_SIG); - verifyInstalledFilesHaveFsverity(); + verifyInstalledFilesHaveFsverity( + INSTALLED_BASE_APK, + INSTALLED_BASE_DM); } @Test @@ -189,7 +196,11 @@ public class ApkVerityTest extends BaseHostJUnit4Test { INSTALLED_SPLIT_APK_FSV_SIG, INSTALLED_SPLIT_DM, INSTALLED_SPLIT_DM_FSV_SIG); - verifyInstalledFilesHaveFsverity(); + verifyInstalledFilesHaveFsverity( + INSTALLED_BASE_APK, + INSTALLED_BASE_DM, + INSTALLED_SPLIT_APK, + INSTALLED_SPLIT_DM); } @Test @@ -213,7 +224,9 @@ public class ApkVerityTest extends BaseHostJUnit4Test { INSTALLED_BASE_APK_FSV_SIG, INSTALLED_SPLIT_APK, INSTALLED_SPLIT_APK_FSV_SIG); - verifyInstalledFilesHaveFsverity(); + verifyInstalledFilesHaveFsverity( + INSTALLED_BASE_APK, + INSTALLED_SPLIT_APK); } @Test @@ -250,39 +263,92 @@ public class ApkVerityTest extends BaseHostJUnit4Test { INSTALLED_BASE_APK, INSTALLED_SPLIT_APK, INSTALLED_SPLIT_APK_FSV_SIG); - } @Test - public void testInstallOnlyBaseHasFsvSig() + public void testInstallOnlyDmHasFsvSig() throws DeviceNotAvailableException, FileNotFoundException { new InstallMultiple() - .addFileAndSignature(BASE_APK) + .addFile(BASE_APK) + .addFileAndSignature(BASE_APK_DM) + .addFile(SPLIT_APK) + .addFileAndSignature(SPLIT_APK_DM) + .run(); + verifyInstalledFiles( + INSTALLED_BASE_APK, + INSTALLED_BASE_DM, + INSTALLED_BASE_DM_FSV_SIG, + INSTALLED_SPLIT_APK, + INSTALLED_SPLIT_DM, + INSTALLED_SPLIT_DM_FSV_SIG); + verifyInstalledFilesHaveFsverity( + INSTALLED_BASE_DM, + INSTALLED_SPLIT_DM); + } + + @Test + public void testInstallDmWithoutFsvSig_Base() + throws DeviceNotAvailableException, FileNotFoundException { + InstallMultiple installer = new InstallMultiple() + .addFile(BASE_APK) .addFile(BASE_APK_DM) .addFile(SPLIT_APK) - .addFile(SPLIT_APK_DM) - .runExpectingFailure(); + .addFileAndSignature(SPLIT_APK_DM); + if (mDmRequireFsVerity) { + installer.runExpectingFailure(); + } else { + installer.run(); + verifyInstalledFiles( + INSTALLED_BASE_APK, + INSTALLED_BASE_DM, + INSTALLED_SPLIT_APK, + INSTALLED_SPLIT_DM, + INSTALLED_SPLIT_DM_FSV_SIG); + verifyInstalledFilesHaveFsverity(INSTALLED_SPLIT_DM); + } } @Test - public void testInstallOnlyDmHasFsvSig() + public void testInstallDmWithoutFsvSig_Split() throws DeviceNotAvailableException, FileNotFoundException { - new InstallMultiple() + InstallMultiple installer = new InstallMultiple() .addFile(BASE_APK) .addFileAndSignature(BASE_APK_DM) .addFile(SPLIT_APK) - .addFile(SPLIT_APK_DM) + .addFile(SPLIT_APK_DM); + if (mDmRequireFsVerity) { + installer.runExpectingFailure(); + } else { + installer.run(); + verifyInstalledFiles( + INSTALLED_BASE_APK, + INSTALLED_BASE_DM, + INSTALLED_BASE_DM_FSV_SIG, + INSTALLED_SPLIT_APK, + INSTALLED_SPLIT_DM); + verifyInstalledFilesHaveFsverity(INSTALLED_BASE_DM); + } + } + + @Test + public void testInstallSomeApkIsMissingFsvSig_Base() + throws DeviceNotAvailableException, FileNotFoundException { + new InstallMultiple() + .addFileAndSignature(BASE_APK) + .addFileAndSignature(BASE_APK_DM) + .addFile(SPLIT_APK) + .addFileAndSignature(SPLIT_APK_DM) .runExpectingFailure(); } @Test - public void testInstallOnlySplitHasFsvSig() + public void testInstallSomeApkIsMissingFsvSig_Split() throws DeviceNotAvailableException, FileNotFoundException { new InstallMultiple() .addFile(BASE_APK) - .addFile(BASE_APK_DM) + .addFileAndSignature(BASE_APK_DM) .addFileAndSignature(SPLIT_APK) - .addFile(SPLIT_APK_DM) + .addFileAndSignature(SPLIT_APK_DM) .runExpectingFailure(); } @@ -383,37 +449,36 @@ public class ApkVerityTest extends BaseHostJUnit4Test { } } - private void verifyInstalledFilesHaveFsverity() throws DeviceNotAvailableException { + private void verifyInstalledFilesHaveFsverity(String... filenames) + throws DeviceNotAvailableException { // Verify that all files are protected by fs-verity String apkPath = getApkPath(TARGET_PACKAGE); String appDir = apkPath.substring(0, apkPath.lastIndexOf("/")); long kTargetOffset = 0; - for (String basename : expectRemoteCommandToSucceed("ls " + appDir).split("\n")) { - if (basename.endsWith(".apk") || basename.endsWith(".dm")) { - String path = appDir + "/" + basename; - damageFileAgainstBlockDevice(path, kTargetOffset); - - // Retry is sometimes needed to pass the test. Package manager may have FD leaks - // (see b/122744005 as example) that prevents the file in question to be evicted - // from filesystem cache. Forcing GC workarounds the problem. - int retry = 5; - for (; retry > 0; retry--) { - BlockDeviceWriter.dropCaches(mDevice); - if (!BlockDeviceWriter.canReadByte(mDevice, path, kTargetOffset)) { - break; - } - try { - CLog.d("lsof: " + expectRemoteCommandToSucceed("lsof " + apkPath)); - Thread.sleep(1000); - String pid = expectRemoteCommandToSucceed("pidof system_server"); - mDevice.executeShellV2Command("kill -10 " + pid); // force GC - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; - } + for (String basename : filenames) { + String path = appDir + "/" + basename; + damageFileAgainstBlockDevice(path, kTargetOffset); + + // Retry is sometimes needed to pass the test. Package manager may have FD leaks + // (see b/122744005 as example) that prevents the file in question to be evicted + // from filesystem cache. Forcing GC workarounds the problem. + int retry = 5; + for (; retry > 0; retry--) { + BlockDeviceWriter.dropCaches(mDevice); + if (!BlockDeviceWriter.canReadByte(mDevice, path, kTargetOffset)) { + break; + } + try { + CLog.d("lsof: " + expectRemoteCommandToSucceed("lsof " + apkPath)); + Thread.sleep(1000); + String pid = expectRemoteCommandToSucceed("pidof system_server"); + mDevice.executeShellV2Command("kill -10 " + pid); // force GC + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; } - assertTrue("Read from " + path + " should fail", retry > 0); } + assertTrue("Read from " + path + " should fail", retry > 0); } } diff --git a/tests/ApkVerityTest/testdata/Android.bp b/tests/ApkVerityTest/testdata/Android.bp index c10b0cef21d7..ccfc4c99a347 100644 --- a/tests/ApkVerityTest/testdata/Android.bp +++ b/tests/ApkVerityTest/testdata/Android.bp @@ -12,6 +12,17 @@ // 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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], +} + filegroup { name: "ApkVerityTestKeyPem", srcs: ["ApkVerityTestKey.pem"], @@ -74,4 +85,3 @@ genrule { srcs: [":ApkVerityTestAppSplitDm"], out: ["ApkVerityTestAppSplit.dm.fsv_sig"], } - diff --git a/tests/AppLaunch/Android.bp b/tests/AppLaunch/Android.bp index 75db55122553..f838c5a80c28 100644 --- a/tests/AppLaunch/Android.bp +++ b/tests/AppLaunch/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "AppLaunch", // Only compile source java files in this apk. diff --git a/tests/AppLaunchWear/Android.bp b/tests/AppLaunchWear/Android.bp index 8d34b6eb9c0f..e2fc4735a7c2 100644 --- a/tests/AppLaunchWear/Android.bp +++ b/tests/AppLaunchWear/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "AppLaunchWear", // Only compile source java files in this apk. diff --git a/tests/AppResourcesLoaders/Android.bp b/tests/AppResourcesLoaders/Android.bp index e5739dbf181c..d882db8b18a2 100644 --- a/tests/AppResourcesLoaders/Android.bp +++ b/tests/AppResourcesLoaders/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "AppResourcesLoaders", srcs: ["**/*.java"], diff --git a/tests/AppResourcesLoaders/Overlay/Android.bp b/tests/AppResourcesLoaders/Overlay/Android.bp index 80443f6c3c00..b063023f7d19 100644 --- a/tests/AppResourcesLoaders/Overlay/Android.bp +++ b/tests/AppResourcesLoaders/Overlay/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test_helper_app { name: "AppResourcesLoaders_Overlay", } diff --git a/tests/Assist/Android.bp b/tests/Assist/Android.bp index 216e75109dde..033c14017442 100644 --- a/tests/Assist/Android.bp +++ b/tests/Assist/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "Assist", srcs: ["**/*.java"], diff --git a/tests/BackgroundDexOptServiceIntegrationTests/Android.bp b/tests/BackgroundDexOptServiceIntegrationTests/Android.bp index a85d129b013a..0a45d5381f84 100644 --- a/tests/BackgroundDexOptServiceIntegrationTests/Android.bp +++ b/tests/BackgroundDexOptServiceIntegrationTests/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "BackgroundDexOptServiceIntegrationTests", srcs: ["src/**/*.java"], diff --git a/tests/BandwidthTests/Android.bp b/tests/BandwidthTests/Android.bp index 523f5226cd2c..a7fc89db32e1 100644 --- a/tests/BandwidthTests/Android.bp +++ b/tests/BandwidthTests/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "BandwidthEnforcementTest", platform_apis: true, diff --git a/tests/BatteryStatsPerfTest/Android.bp b/tests/BatteryStatsPerfTest/Android.bp index 58ccec705419..5233a5b8654e 100644 --- a/tests/BatteryStatsPerfTest/Android.bp +++ b/tests/BatteryStatsPerfTest/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "BatteryStatsPerfTests", srcs: ["src/**/*.java"], diff --git a/tests/BatteryWaster/Android.bp b/tests/BatteryWaster/Android.bp index 4698910adb40..1fa4f82cd9f4 100644 --- a/tests/BatteryWaster/Android.bp +++ b/tests/BatteryWaster/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "BatteryWaster", srcs: ["**/*.java"], diff --git a/tests/BiDiTests/Android.bp b/tests/BiDiTests/Android.bp index c659e8c1257e..79ae41fb0c82 100644 --- a/tests/BiDiTests/Android.bp +++ b/tests/BiDiTests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "BiDiTests", // Only compile source java files in this apk. diff --git a/tests/BlobStoreTestUtils/Android.bp b/tests/BlobStoreTestUtils/Android.bp index 53d36389a52a..c4faf7f4fb11 100644 --- a/tests/BlobStoreTestUtils/Android.bp +++ b/tests/BlobStoreTestUtils/Android.bp @@ -12,6 +12,15 @@ // 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: "BlobStoreTestUtils", srcs: ["src/**/*.java"], @@ -21,4 +30,4 @@ java_library { "androidx.test.ext.junit", ], sdk_version: "test_current", -}
\ No newline at end of file +} diff --git a/tests/BootImageProfileTest/Android.bp b/tests/BootImageProfileTest/Android.bp index 1b097a8af0f9..9fb5aa21f985 100644 --- a/tests/BootImageProfileTest/Android.bp +++ b/tests/BootImageProfileTest/Android.bp @@ -12,6 +12,15 @@ // 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_test_host { name: "BootImageProfileTest", srcs: ["src/**/*.java"], diff --git a/tests/BrowserPowerTest/Android.bp b/tests/BrowserPowerTest/Android.bp index 1d358cbe6e75..a8a9897c0e86 100644 --- a/tests/BrowserPowerTest/Android.bp +++ b/tests/BrowserPowerTest/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "BrowserPowerTests", libs: [ diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp index 125deb521ddc..b889b0d29a8b 100644 --- a/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp +++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/jni/Android.bp @@ -13,6 +13,15 @@ // 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"], +} + cc_test_library { name: "libsmartcamera_jni", sdk_version: "14", diff --git a/tests/CameraPrewarmTest/Android.bp b/tests/CameraPrewarmTest/Android.bp index eaf453b6f3ae..07c49232efc5 100644 --- a/tests/CameraPrewarmTest/Android.bp +++ b/tests/CameraPrewarmTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "CameraPrewarmTest", srcs: ["**/*.java"], diff --git a/tests/Codegen/Android.bp b/tests/Codegen/Android.bp index 966c5602959c..ddbf16817b94 100644 --- a/tests/Codegen/Android.bp +++ b/tests/Codegen/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "CodegenTests", srcs: [ diff --git a/tests/Compatibility/Android.bp b/tests/Compatibility/Android.bp index c14e705b09ab..c58c99e1c4f0 100644 --- a/tests/Compatibility/Android.bp +++ b/tests/Compatibility/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "AppCompatibilityTest", static_libs: ["androidx.test.rules"], diff --git a/tests/CoreTests/android/Android.bp b/tests/CoreTests/android/Android.bp index 24134e8fb7f0..e2f194b04437 100644 --- a/tests/CoreTests/android/Android.bp +++ b/tests/CoreTests/android/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "LegacyCoreTests", srcs: ["**/*.java"], diff --git a/tests/DataIdleTest/Android.bp b/tests/DataIdleTest/Android.bp index 19656ce32b29..f9509cc9a4bf 100644 --- a/tests/DataIdleTest/Android.bp +++ b/tests/DataIdleTest/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "DataIdleTest", platform_apis: true, diff --git a/tests/DozeTest/Android.bp b/tests/DozeTest/Android.bp index f1be029f58d5..36ea91a84a0d 100644 --- a/tests/DozeTest/Android.bp +++ b/tests/DozeTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "DozeTest", // Only compile source java files in this apk. diff --git a/tests/DpiTest/Android.bp b/tests/DpiTest/Android.bp index 7d6a78ba1581..52bb08bbc8ad 100644 --- a/tests/DpiTest/Android.bp +++ b/tests/DpiTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "DensityTest", srcs: ["**/*.java"], diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.mk b/tests/DynamicCodeLoggerIntegrationTests/Android.mk index 2d58ce8baddc..bfb5b076237a 100644 --- a/tests/DynamicCodeLoggerIntegrationTests/Android.mk +++ b/tests/DynamicCodeLoggerIntegrationTests/Android.mk @@ -22,6 +22,9 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_MODULE := DynamicCodeLoggerTestLibrary +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/dcl) include $(BUILD_JAVA_LIBRARY) @@ -35,6 +38,9 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_MODULE := DynamicCodeLoggerNativeTestLibrary +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE LOCAL_SRC_FILES := src/cpp/com_android_dcl_Jni.cpp LOCAL_HEADER_LIBRARIES := jni_headers LOCAL_SDK_VERSION := 28 @@ -48,6 +54,9 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_MODULE := DynamicCodeLoggerNativeExecutable +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE LOCAL_SRC_FILES := src/cpp/test_executable.cpp include $(BUILD_EXECUTABLE) diff --git a/tests/FeatureSplit/base/Android.bp b/tests/FeatureSplit/base/Android.bp index ab25464a82fc..89c26d2c2256 100644 --- a/tests/FeatureSplit/base/Android.bp +++ b/tests/FeatureSplit/base/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test_helper_app { name: "FeatureSplitBase", srcs: ["**/*.java"], diff --git a/tests/FeatureSplit/feature1/Android.bp b/tests/FeatureSplit/feature1/Android.bp index 706a4f544393..2f8aa8b6eb28 100644 --- a/tests/FeatureSplit/feature1/Android.bp +++ b/tests/FeatureSplit/feature1/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "FeatureSplit1", srcs: ["**/*.java"], diff --git a/tests/FeatureSplit/feature2/Android.bp b/tests/FeatureSplit/feature2/Android.bp index a3634821f6fd..2a5c432d1499 100644 --- a/tests/FeatureSplit/feature2/Android.bp +++ b/tests/FeatureSplit/feature2/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "FeatureSplit2", srcs: ["**/*.java"], diff --git a/tests/FixVibrateSetting/Android.bp b/tests/FixVibrateSetting/Android.bp index 5608a2b5e15d..bd7c701026ba 100644 --- a/tests/FixVibrateSetting/Android.bp +++ b/tests/FixVibrateSetting/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "FixVibrateSetting", srcs: ["**/*.java"], diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index 0792e8b272a8..217a72b90fd4 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "FlickerTests", srcs: ["src/**/*.java", "src/**/*.kt"], diff --git a/tests/FlickerTests/OWNERS b/tests/FlickerTests/OWNERS index f35a318acbf7..b5561010e7f9 100644 --- a/tests/FlickerTests/OWNERS +++ b/tests/FlickerTests/OWNERS @@ -1,2 +1,3 @@ +# Bug component: 909476 include /services/core/java/com/android/server/wm/OWNERS natanieljr@google.com
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt index c5447c1ccf71..a8b7b057fe24 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt @@ -17,579 +17,230 @@ package com.android.server.wm.flicker import android.platform.helpers.IAppHelper -import com.android.server.wm.flicker.dsl.EventLogAssertionBuilder -import com.android.server.wm.flicker.dsl.EventLogAssertionBuilderLegacy -import com.android.server.wm.flicker.dsl.LayersAssertionBuilder -import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy -import com.android.server.wm.flicker.dsl.WmAssertionBuilder -import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_WINDOW_NAME -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_LAYER_NAME import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_WINDOW_NAME -const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider" -const val DOCKED_STACK_DIVIDER = "DockedStackDivider" const val WALLPAPER_TITLE = "Wallpaper" -@JvmOverloads -fun WmAssertionBuilder.statusBarWindowIsAlwaysVisible(bugId: Int = 0) { - all("statusBarWindowIsAlwaysVisible", bugId) { +fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() { + assertWm { this.showsAboveAppWindow(NAV_BAR_LAYER_NAME) } } -@JvmOverloads -fun WmAssertionBuilder.navBarWindowIsAlwaysVisible(bugId: Int = 0) { - all("navBarWindowIsAlwaysVisible", bugId) { +fun FlickerTestParameter.navBarWindowIsAlwaysVisible() { + assertWm { this.showsAboveAppWindow(NAV_BAR_LAYER_NAME) } } -fun WmAssertionBuilder.visibleWindowsShownMoreThanOneConsecutiveEntry( - ignoreWindows: List<String> = emptyList(), - bugId: Int = 0 -) { - all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId) { - this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows) - } -} - -fun WmAssertionBuilder.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper, bugId: Int = 0) { - all("launcherReplacesAppWindowAsTopWindow", bugId) { - this.showsAppWindowOnTop(testApp.getPackage()) - .then() - .showsAppWindowOnTop("Launcher") - } -} - -fun WmAssertionBuilder.wallpaperWindowBecomesVisible(bugId: Int = 0) { - all("wallpaperWindowBecomesVisible", bugId) { - this.hidesBelowAppWindow(WALLPAPER_TITLE) - .then() - .showsBelowAppWindow(WALLPAPER_TITLE) - } -} - -fun WmAssertionBuilder.wallpaperWindowBecomesInvisible(bugId: Int = 0) { - all("wallpaperWindowBecomesInvisible", bugId) { - this.showsBelowAppWindow("Wallpaper") - .then() - .hidesBelowAppWindow("Wallpaper") - } -} - -fun WmAssertionBuilder.appWindowAlwaysVisibleOnTop( - packageName: String, - bugId: Int = 0 -) { - all("appWindowAlwaysVisibleOnTop", bugId) { - this.showsAppWindowOnTop(packageName) - } -} - -fun WmAssertionBuilder.appWindowBecomesVisible(appName: String, bugId: Int = 0) { - all("appWindowBecomesVisible", bugId) { - this.hidesAppWindow(appName) - .then() - .showsAppWindow(appName) - } -} - -fun WmAssertionBuilder.appWindowBecomesInVisible(appName: String, bugId: Int = 0) { - all("appWindowBecomesInVisible", bugId) { - this.showsAppWindow(appName) - .then() - .hidesAppWindow(appName) - } -} - -@JvmOverloads -fun LayersAssertionBuilder.noUncoveredRegions( - beginRotation: Int, - endRotation: Int = beginRotation, - allStates: Boolean = true, - bugId: Int = 0 -) { - val startingBounds = WindowUtils.getDisplayBounds(beginRotation) - val endingBounds = WindowUtils.getDisplayBounds(endRotation) - if (allStates) { - all("noUncoveredRegions", bugId) { - if (startingBounds == endingBounds) { - this.coversAtLeastRegion(startingBounds) - } else { - this.coversAtLeastRegion(startingBounds) - .then() - .coversAtLeastRegion(endingBounds) - } - } - } else { - start("noUncoveredRegions_StartingPos") { - this.coversAtLeastRegion(startingBounds) - } - end("noUncoveredRegions_EndingPos") { - this.coversAtLeastRegion(endingBounds) - } - } -} - -@JvmOverloads -fun LayersAssertionBuilder.navBarLayerIsAlwaysVisible( - rotatesScreen: Boolean = false, - bugId: Int = 0 -) { - if (rotatesScreen) { - all("navBarLayerIsAlwaysVisible", bugId) { - this.showsLayer(NAV_BAR_LAYER_NAME) - .then() - .hidesLayer(NAV_BAR_LAYER_NAME) - .then() - .showsLayer(NAV_BAR_LAYER_NAME) - } - } else { - all("navBarLayerIsAlwaysVisible", bugId) { - this.showsLayer(NAV_BAR_LAYER_NAME) - } - } -} - -@JvmOverloads -fun LayersAssertionBuilder.statusBarLayerIsAlwaysVisible( - rotatesScreen: Boolean = false, - bugId: Int = 0 -) { - if (rotatesScreen) { - all("statusBarLayerIsAlwaysVisible", bugId) { - this.showsLayer(STATUS_BAR_WINDOW_NAME) - .then() - hidesLayer(STATUS_BAR_WINDOW_NAME) - .then() - .showsLayer(STATUS_BAR_WINDOW_NAME) - } - } else { - all("statusBarLayerIsAlwaysVisible", bugId) { - this.showsLayer(STATUS_BAR_WINDOW_NAME) - } - } -} - -@JvmOverloads -fun LayersAssertionBuilder.navBarLayerRotatesAndScales( - beginRotation: Int, - endRotation: Int = beginRotation, - bugId: Int = 0 -) { - val startingPos = WindowUtils.getNavigationBarPosition(beginRotation) - val endingPos = WindowUtils.getNavigationBarPosition(endRotation) - - start("navBarLayerRotatesAndScales_StartingPos", bugId) { - this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos) - } - end("navBarLayerRotatesAndScales_EndingPost", bugId) { - this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos) - } - - /*if (startingPos == endingPos) { - all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) { - this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos) - } - }*/ -} - @JvmOverloads -fun LayersAssertionBuilder.statusBarLayerRotatesScales( - beginRotation: Int, - endRotation: Int = beginRotation, - bugId: Int = 0 +fun FlickerTestParameter.visibleWindowsShownMoreThanOneConsecutiveEntry( + ignoreWindows: List<String> = emptyList() ) { - val startingPos = WindowUtils.getStatusBarPosition(beginRotation) - val endingPos = WindowUtils.getStatusBarPosition(endRotation) - - start("statusBarLayerRotatesScales_StartingPos", bugId) { - this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, startingPos) - } - end("statusBarLayerRotatesScales_EndingPos", bugId) { - this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, endingPos) - } -} - -fun LayersAssertionBuilder.visibleLayersShownMoreThanOneConsecutiveEntry( - ignoreLayers: List<String> = emptyList(), - bugId: Int = 0 -) { - all("visibleLayersShownMoreThanOneConsecutiveEntry", bugId) { - this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers) - } -} - -fun LayersAssertionBuilder.appLayerReplacesWallpaperLayer(appName: String, bugId: Int = 0) { - all("appLayerReplacesWallpaperLayer", bugId) { - this.showsLayer("Wallpaper") - .then() - .replaceVisibleLayer("Wallpaper", appName) - } -} - -fun LayersAssertionBuilder.wallpaperLayerReplacesAppLayer(testApp: IAppHelper, bugId: Int = 0) { - all("appLayerReplacesWallpaperLayer", bugId) { - this.showsLayer(testApp.getPackage()) - .then() - .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE) - } -} - -fun LayersAssertionBuilder.layerAlwaysVisible(packageName: String, bugId: Int = 0) { - all("layerAlwaysVisible", bugId) { - this.showsLayer(packageName) - } -} - -fun LayersAssertionBuilder.layerBecomesVisible(packageName: String, bugId: Int = 0) { - all("layerBecomesVisible", bugId) { - this.hidesLayer(packageName) - .then() - .showsLayer(packageName) - } -} - -fun LayersAssertionBuilder.layerBecomesInvisible(packageName: String, bugId: Int = 0) { - all("layerBecomesInvisible", bugId) { - this.showsLayer(packageName) - .then() - .hidesLayer(packageName) - } -} - -fun EventLogAssertionBuilder.focusChanges(vararg windows: String, bugId: Int = 0) { - all("focusChanges", bugId) { - this.focusChanges(windows) - } -} - -fun EventLogAssertionBuilder.focusDoesNotChange(bugId: Int = 0) { - all("focusDoesNotChange", bugId) { - this.focusDoesNotChange() - } -} - -@JvmOverloads -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("statusBarWindowIsAlwaysVisible", bugId, enabled) { - this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME) - } -} - -@JvmOverloads -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("navBarWindowIsAlwaysVisible", bugId, enabled) { - this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME) - } -} - -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry( - ignoreWindows: List<String> = emptyList(), - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId, enabled) { + assertWm { this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow( - testApp: IAppHelper, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("launcherReplacesAppWindowAsTopWindow", bugId, enabled) { +fun FlickerTestParameter.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper) { + assertWm { this.showsAppWindowOnTop(testApp.getPackage()) .then() .showsAppWindowOnTop("Launcher") } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.wallpaperWindowBecomesVisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("wallpaperWindowBecomesVisible", bugId, enabled) { +fun FlickerTestParameter.wallpaperWindowBecomesVisible() { + assertWm { this.hidesBelowAppWindow(WALLPAPER_TITLE) .then() .showsBelowAppWindow(WALLPAPER_TITLE) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.wallpaperWindowBecomesInvisible( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("wallpaperWindowBecomesInvisible", bugId, enabled) { - this.showsBelowAppWindow("Wallpaper") +fun FlickerTestParameter.wallpaperWindowBecomesInvisible() { + assertWm { + this.showsBelowAppWindow(WALLPAPER_TITLE) .then() - .hidesBelowAppWindow("Wallpaper") + .hidesBelowAppWindow(WALLPAPER_TITLE) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop( - packageName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("appWindowAlwaysVisibleOnTop", bugId, enabled) { +fun FlickerTestParameter.appWindowAlwaysVisibleOnTop(packageName: String) { + assertWm { this.showsAppWindowOnTop(packageName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.appWindowBecomesVisible( - appName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("appWindowBecomesVisible", bugId, enabled) { +fun FlickerTestParameter.appWindowBecomesVisible(appName: String) { + assertWm { this.hidesAppWindow(appName) .then() .showsAppWindow(appName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun WmAssertionBuilderLegacy.appWindowBecomesInVisible( - appName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("appWindowBecomesInVisible", bugId, enabled) { +fun FlickerTestParameter.appWindowBecomesInVisible(appName: String) { + assertWm { this.showsAppWindow(appName) .then() .hidesAppWindow(appName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") @JvmOverloads -fun LayersAssertionBuilderLegacy.noUncoveredRegions( +fun FlickerTestParameter.noUncoveredRegions( beginRotation: Int, endRotation: Int = beginRotation, - allStates: Boolean = true, - bugId: Int = 0, - enabled: Boolean = bugId == 0 + allStates: Boolean = true ) { val startingBounds = WindowUtils.getDisplayBounds(beginRotation) val endingBounds = WindowUtils.getDisplayBounds(endRotation) if (allStates) { - all("noUncoveredRegions", bugId, enabled) { + assertLayers { if (startingBounds == endingBounds) { - this.coversAtLeastRegion(startingBounds) + this.coversAtLeast(startingBounds) } else { - this.coversAtLeastRegion(startingBounds) + this.coversAtLeast(startingBounds) .then() - .coversAtLeastRegion(endingBounds) + .coversAtLeast(endingBounds) } } } else { - start("noUncoveredRegions_StartingPos") { - this.coversAtLeastRegion(startingBounds) + assertLayersStart { + this.coversAtLeast(startingBounds) } - end("noUncoveredRegions_EndingPos") { - this.coversAtLeastRegion(endingBounds) + assertLayersEnd { + this.coversAtLeast(endingBounds) } } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") @JvmOverloads -fun LayersAssertionBuilderLegacy.navBarLayerIsAlwaysVisible( - rotatesScreen: Boolean = false, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { +fun FlickerTestParameter.navBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) { if (rotatesScreen) { - all("navBarLayerIsAlwaysVisible", bugId, enabled) { - this.showsLayer(NAV_BAR_LAYER_NAME) + assertLayers { + this.isVisible(NAV_BAR_LAYER_NAME) .then() - .hidesLayer(NAV_BAR_LAYER_NAME) + .isInvisible(NAV_BAR_LAYER_NAME) .then() - .showsLayer(NAV_BAR_LAYER_NAME) + .isVisible(NAV_BAR_LAYER_NAME) } } else { - all("navBarLayerIsAlwaysVisible", bugId, enabled) { - this.showsLayer(NAV_BAR_LAYER_NAME) + assertLayers { + this.isVisible(NAV_BAR_LAYER_NAME) } } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") @JvmOverloads -fun LayersAssertionBuilderLegacy.statusBarLayerIsAlwaysVisible( - rotatesScreen: Boolean = false, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { +fun FlickerTestParameter.statusBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) { if (rotatesScreen) { - all("statusBarLayerIsAlwaysVisible", bugId, enabled) { - this.showsLayer(STATUS_BAR_LAYER_NAME) + assertLayers { + this.isVisible(STATUS_BAR_WINDOW_NAME) .then() - .hidesLayer(STATUS_BAR_LAYER_NAME) + .isInvisible(STATUS_BAR_WINDOW_NAME) .then() - .showsLayer(STATUS_BAR_LAYER_NAME) + .isVisible(STATUS_BAR_WINDOW_NAME) } } else { - all("statusBarLayerIsAlwaysVisible", bugId, enabled) { - this.showsLayer(STATUS_BAR_LAYER_NAME) + assertLayers { + this.isVisible(STATUS_BAR_WINDOW_NAME) } } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") @JvmOverloads -fun LayersAssertionBuilderLegacy.navBarLayerRotatesAndScales( +fun FlickerTestParameter.navBarLayerRotatesAndScales( beginRotation: Int, - endRotation: Int = beginRotation, - bugId: Int = 0, - enabled: Boolean = bugId == 0 + endRotation: Int = beginRotation ) { val startingPos = WindowUtils.getNavigationBarPosition(beginRotation) val endingPos = WindowUtils.getNavigationBarPosition(endRotation) - start("navBarLayerRotatesAndScales_StartingPos", bugId, enabled) { - this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos) + assertLayersStart { + this.coversExactly(startingPos, NAV_BAR_LAYER_NAME) } - end("navBarLayerRotatesAndScales_EndingPost", bugId, enabled) { - this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos) - } - - if (startingPos == endingPos) { - all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) { - this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos) - } + assertLayersEnd { + this.coversExactly(endingPos, NAV_BAR_LAYER_NAME) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") @JvmOverloads -fun LayersAssertionBuilderLegacy.statusBarLayerRotatesScales( +fun FlickerTestParameter.statusBarLayerRotatesScales( beginRotation: Int, - endRotation: Int = beginRotation, - bugId: Int = 0, - enabled: Boolean = bugId == 0 + endRotation: Int = beginRotation ) { val startingPos = WindowUtils.getStatusBarPosition(beginRotation) val endingPos = WindowUtils.getStatusBarPosition(endRotation) - start("statusBarLayerRotatesScales_StartingPos", bugId, enabled) { - this.hasVisibleRegion(STATUS_BAR_LAYER_NAME, startingPos) + assertLayersStart { + this.coversExactly(startingPos, STATUS_BAR_WINDOW_NAME) } - end("statusBarLayerRotatesScales_EndingPos", bugId, enabled) { - this.hasVisibleRegion(STATUS_BAR_LAYER_NAME, endingPos) + assertLayersEnd { + this.coversExactly(endingPos, STATUS_BAR_WINDOW_NAME) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun LayersAssertionBuilderLegacy.visibleLayersShownMoreThanOneConsecutiveEntry( - ignoreLayers: List<String> = kotlin.collections.emptyList(), - bugId: Int = 0, - enabled: Boolean = bugId == 0 +@JvmOverloads +fun FlickerTestParameter.visibleLayersShownMoreThanOneConsecutiveEntry( + ignoreLayers: List<String> = emptyList() ) { - all("visibleLayersShownMoreThanOneConsecutiveEntry", bugId, enabled) { + assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun LayersAssertionBuilderLegacy.appLayerReplacesWallpaperLayer( - appName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("appLayerReplacesWallpaperLayer", bugId, enabled) { - this.showsLayer("Wallpaper") +fun FlickerTestParameter.appLayerReplacesWallpaperLayer(appName: String) { + assertLayers { + this.isVisible(WALLPAPER_TITLE) .then() - .replaceVisibleLayer("Wallpaper", appName) + .isInvisible(WALLPAPER_TITLE) + .isVisible(appName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun LayersAssertionBuilderLegacy.wallpaperLayerReplacesAppLayer( - testApp: IAppHelper, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("appLayerReplacesWallpaperLayer", bugId, enabled) { - this.showsLayer(testApp.getPackage()) +fun FlickerTestParameter.wallpaperLayerReplacesAppLayer(testApp: IAppHelper) { + assertLayers { + this.isVisible(testApp.getPackage()) .then() - .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE) + .isInvisible(testApp.getPackage()) + .isVisible(WALLPAPER_TITLE) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun LayersAssertionBuilderLegacy.layerAlwaysVisible( - packageName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("layerAlwaysVisible", bugId, enabled) { - this.showsLayer(packageName) +fun FlickerTestParameter.layerAlwaysVisible(packageName: String) { + assertLayers { + this.isVisible(packageName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun LayersAssertionBuilderLegacy.layerBecomesVisible( - packageName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("layerBecomesVisible", bugId, enabled) { - this.hidesLayer(packageName) +fun FlickerTestParameter.layerBecomesVisible(packageName: String) { + assertLayers { + this.isInvisible(packageName) .then() - .showsLayer(packageName) + .isVisible(packageName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun LayersAssertionBuilderLegacy.layerBecomesInvisible( - packageName: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("layerBecomesInvisible", bugId, enabled) { - this.showsLayer(packageName) +fun FlickerTestParameter.layerBecomesInvisible(packageName: String) { + assertLayers { + this.isVisible(packageName) .then() - .hidesLayer(packageName) + .isInvisible(packageName) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun EventLogAssertionBuilderLegacy.focusChanges( - vararg windows: String, - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("focusChanges", bugId, enabled) { +fun FlickerTestParameter.focusChanges(vararg windows: String) { + assertEventLog { this.focusChanges(windows) } } -@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)") -fun EventLogAssertionBuilderLegacy.focusDoesNotChange( - bugId: Int = 0, - enabled: Boolean = bugId == 0 -) { - all("focusDoesNotChange", bugId, enabled) { +fun FlickerTestParameter.focusDoesNotChange() { + assertEventLog { this.focusDoesNotChange() } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt index c507841ffb71..fbf18d45afd8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt @@ -16,11 +16,17 @@ package com.android.server.wm.flicker.close +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible @@ -35,12 +41,12 @@ import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -51,86 +57,119 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseAppBackButtonTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class CloseAppBackButtonTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + this.setRotation(testSpec.config.startRotation) + testApp.launchViaIntent(wmHelper) + } + } + transitions { + device.pressBack() + wmHelper.waitForHomeActivityVisible() + } + teardown { + eachRun { + this.setRotation(Surface.ROTATION_0) + } + test { + testApp.exit() + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun launcherReplacesAppWindowAsTopWindow() = + testSpec.launcherReplacesAppWindowAsTopWindow(testApp) + + @Presubmit + @Test + fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible() + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + Surface.ROTATION_0) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest(bugId = 173684672) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @FlakyTest(bugId = 173684672) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = SimpleAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - this.setRotation(configuration.startRotation) - testApp.launchViaIntent(wmHelper) - } - } - transitions { - device.pressBack() - wmHelper.waitForHomeActivityVisible() - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } - test { - testApp.exit() - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - launcherReplacesAppWindowAsTopWindow(testApp) - wallpaperWindowBecomesVisible() - } - - layersTrace { - noUncoveredRegions(configuration.startRotation, - Surface.ROTATION_0) - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - wallpaperLayerReplacesAppLayer(testApp) - - if (!isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - } - - flaky { - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173684672) - } - - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173684672) - - if (isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - } - } - } + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt index d1c3efe35c54..08d2b7c206bf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt @@ -16,12 +16,17 @@ package com.android.server.wm.flicker.close +import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible @@ -36,12 +41,12 @@ import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -50,88 +55,121 @@ import org.junit.runners.Parameterized * Test app closes by pressing home button. * To run this test: `atest FlickerTests:CloseAppHomeButtonTest` */ -@Presubmit @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseAppHomeButtonTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class CloseAppHomeButtonTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + testApp.launchViaIntent(wmHelper) + this.setRotation(testSpec.config.startRotation) + } + } + transitions { + device.pressHome() + wmHelper.waitForHomeActivityVisible() + } + teardown { + eachRun { + this.setRotation(Surface.ROTATION_0) + } + test { + testApp.exit() + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun launcherReplacesAppWindowAsTopWindow() = + testSpec.launcherReplacesAppWindowAsTopWindow(testApp) + + @Presubmit + @Test + fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible() + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions( + testSpec.config.startRotation, Surface.ROTATION_0) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest(bugId = 173689015) + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @FlakyTest(bugId = 173689015) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = SimpleAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - testApp.launchViaIntent(wmHelper) - this.setRotation(configuration.startRotation) - } - } - transitions { - device.pressHome() - wmHelper.waitForHomeActivityVisible() - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } - test { - testApp.exit() - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - launcherReplacesAppWindowAsTopWindow(testApp) - wallpaperWindowBecomesVisible() - } - - layersTrace { - noUncoveredRegions(configuration.startRotation, - Surface.ROTATION_0) - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - wallpaperLayerReplacesAppLayer(testApp) - - if (!isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - } - - flaky { - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173689015) - } - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173689015) - - if (isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt index 323236ed9962..f7e749311442 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt @@ -16,13 +16,9 @@ package com.android.server.wm.flicker.helpers -import android.os.Bundle import android.os.RemoteException -import android.platform.helpers.IAppHelper import android.view.Surface import com.android.server.wm.flicker.Flicker -import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.startRotation /** * Changes the device [rotation] and wait for the rotation animation to complete @@ -47,128 +43,4 @@ fun Flicker.setRotation(rotation: Int) { } catch (e: RemoteException) { throw RuntimeException(e) } -} - -/** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param beginRotation Initial screen rotation - * @param endRotation End screen rotation (if any, otherwise use same as initial) - * - * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION> -</END_ROTATION></BEGIN_ROTATION></APP></NAME> */ -fun buildTestTag( - testName: String, - app: IAppHelper, - beginRotation: Int, - endRotation: Int -): String { - return buildTestTag( - testName, app.launcherName, beginRotation, endRotation, app2 = null, extraInfo = "") -} - -/** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param configuration Configuration for the test - * @param extraInfo Additional information to append to the tag - * - * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION> -</END_ROTATION></BEGIN_ROTATION></APP></NAME> */ -@JvmOverloads -fun buildTestTag( - testName: String, - configuration: Bundle, - extraInfo: String = "" -): String { - return buildTestTag(testName, - app = null, - beginRotation = configuration.startRotation, - endRotation = configuration.endRotation, - app2 = null, - extraInfo = extraInfo) -} - -/** - * Build a test tag for the test - * @param configuration Configuration for the test - * @param extraInfo Additional information to append to the tag - * - * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION> -</END_ROTATION></BEGIN_ROTATION></APP></NAME> */ -@JvmOverloads -fun buildTestTag( - configuration: Bundle, - extraInfo: String = "" -): String { - return buildTestTag(testName = null, - app = null, - beginRotation = configuration.startRotation, - endRotation = configuration.endRotation, - app2 = null, - extraInfo = extraInfo) -} - -/** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param configuration Configuration for the test - * @param extraInfo Additional information to append to the tag - * - * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION> -</END_ROTATION></BEGIN_ROTATION></APP></NAME> */ -@JvmOverloads -fun buildTestTag( - testName: String, - app: IAppHelper?, - configuration: Bundle, - extraInfo: String = "" -): String { - return buildTestTag(testName, app?.launcherName ?: "", configuration.startRotation, - configuration.endRotation, app2 = null, extraInfo = extraInfo) -} - -/** - * Build a test tag for the test - * @param testName Name of the transition(s) being tested - * @param app App being launcher - * @param app2 Second app being launched (if any) - * @param beginRotation Initial screen rotation - * @param endRotation End screen rotation (if any, otherwise use same as initial) - * @param extraInfo Additional information to append to the tag - * - * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>] -</EXTRA></NAME> */ -fun buildTestTag( - testName: String?, - app: String?, - beginRotation: Int, - endRotation: Int, - app2: String?, - extraInfo: String -): String { - var testTag = "" - if (testName != null) { - testTag += testName - } - if (app != null) { - testTag += "__$app" - } - if (app2 != null) { - testTag += "-$app2" - } - testTag += "__${Surface.rotationToString(beginRotation)}" - if (endRotation != beginRotation) { - testTag += "-${Surface.rotationToString(endRotation)}" - } - if (extraInfo.isNotEmpty()) { - testTag += "__$extraInfo" - } - - if (testTag.startsWith("__")) { - testTag = testTag.drop(2) - } - return testTag -} +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt index c7736f825e27..47eaddfa1f3a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt @@ -16,14 +16,19 @@ package com.android.server.wm.flicker.ime +import android.app.Instrumentation +import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry @@ -37,7 +42,9 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -48,79 +55,116 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeAutoOpenWindowToAppTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + testApp.launchViaIntent(wmHelper) + testApp.openIME(device, wmHelper) + this.setRotation(testSpec.config.startRotation) + } + } + teardown { + test { + testApp.exit() + wmHelper.waitForAppTransitionIdle() + this.setRotation(Surface.ROTATION_0) + } + } + transitions { + testApp.closeIME(device, wmHelper) + } + } + } + + @Postsubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + + @Postsubmit + @Test + fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp) + + @Postsubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) + + @Postsubmit + @Test + fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + + @Postsubmit + @Test + fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - val testApp = ImeAppAutoFocusHelper(instrumentation, - configuration.startRotation) - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - testApp.launchViaIntent(wmHelper) - testApp.openIME(device, wmHelper) - this.setRotation(configuration.startRotation) - } - } - teardown { - test { - testApp.exit() - wmHelper.waitForAppTransitionIdle() - this.setRotation(Surface.ROTATION_0) - } - } - transitions { - testApp.closeIME(device, wmHelper) - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - postsubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - imeAppWindowIsAlwaysVisible(testApp) - } - - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.startRotation) - imeLayerBecomesInvisible() - imeAppLayerIsAlwaysVisible(testApp) - if (!isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation) - statusBarLayerRotatesScales(configuration.startRotation) - } - } - } - - flaky { - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry() - - if (isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation) - statusBarLayerRotatesScales(configuration.startRotation) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } -}
\ No newline at end of file +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt index aa24456c652f..26afb794bb06 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt @@ -16,14 +16,18 @@ package com.android.server.wm.flicker.ime +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry @@ -37,7 +41,9 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -48,93 +54,131 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeAutoOpenWindowToHomeTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + testApp.launchViaIntent(wmHelper) + testApp.openIME(device, wmHelper) + this.setRotation(testSpec.config.startRotation) + } + } + teardown { + test { + testApp.exit() + this.setRotation(Surface.ROTATION_0) + } + } + transitions { + device.pressHome() + wmHelper.waitForHomeActivityVisible() + wmHelper.waitImeWindowGone() + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + + @Presubmit + @Test + fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible() + + @Presubmit + @Test + fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp) + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + Surface.ROTATION_0) + + @Presubmit + @Test + fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + + @Presubmit + @Test + fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + } + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + } companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - val testApp = ImeAppAutoFocusHelper(instrumentation, - configuration.startRotation) - withTestName { - buildTestTag(configuration) - } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - testApp.launchViaIntent(wmHelper) - testApp.openIME(device, wmHelper) - this.setRotation(configuration.startRotation) - } - } - teardown { - test { - testApp.exit() - this.setRotation(Surface.ROTATION_0) - } - } - transitions { - device.pressHome() - wmHelper.waitForHomeActivityVisible() - wmHelper.waitImeWindowGone() - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - - imeWindowBecomesInvisible() - imeAppWindowBecomesInvisible(testApp) - } - - layersTrace { - noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0) - imeLayerBecomesInvisible() - imeAppLayerBecomesInvisible(testApp) - - if (!isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - } - } - } - - flaky { - layersTrace { - if (isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt index 2bd5abb640e5..2c4c627a444d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt @@ -16,13 +16,17 @@ package com.android.server.wm.flicker.ime +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -33,10 +37,10 @@ import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEn import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -47,63 +51,97 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeWindowToAppTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.launchViaIntent() + this.setRotation(testSpec.config.startRotation) + } + eachRun { + testApp.openIME(device, wmHelper) + } + } + teardown { + test { + testApp.exit() + this.setRotation(Surface.ROTATION_0) + } + } + transitions { + testApp.closeIME(device, wmHelper) + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + + @Presubmit + @Test + fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + + @Presubmit + @Test + fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) + + @Presubmit + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + + @Presubmit + @Test + fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp) companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = ImeAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - testApp.launchViaIntent() - this.setRotation(configuration.startRotation) - } - eachRun { - testApp.openIME(device, wmHelper) - } - } - teardown { - test { - testApp.exit() - this.setRotation(Surface.ROTATION_0) - } - } - transitions { - testApp.closeIME(device, wmHelper) - } - assertions { - postsubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - imeAppWindowIsAlwaysVisible(testApp) - } - - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.startRotation) - navBarLayerRotatesAndScales(configuration.startRotation) - statusBarLayerRotatesScales(configuration.startRotation) - visibleLayersShownMoreThanOneConsecutiveEntry() - imeLayerBecomesInvisible() - imeAppLayerIsAlwaysVisible(testApp) - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt index 7b61bb58446c..2bcdcd9c8219 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt @@ -16,28 +16,33 @@ package com.android.server.wm.flicker.ime +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry +import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -47,90 +52,118 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeWindowToHomeTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + testApp.launchViaIntent(wmHelper) + this.setRotation(testSpec.config.startRotation) + testApp.openIME(device, wmHelper) + } + } + transitions { + device.pressHome() + wmHelper.waitForHomeActivityVisible() + wmHelper.waitImeWindowGone() + } + teardown { + eachRun { + device.pressHome() + wmHelper.waitForHomeActivityVisible() + } + test { + testApp.exit() + this.setRotation(Surface.ROTATION_0) + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + + @Presubmit + @Test + fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible() + + @Presubmit + @Test + fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + Surface.ROTATION_0) + + @Presubmit + @Test + fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + + @Presubmit + @Test + fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = ImeAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - testApp.launchViaIntent(wmHelper) - this.setRotation(configuration.startRotation) - testApp.openIME(device, wmHelper) - } - } - transitions { - device.pressHome() - wmHelper.waitForHomeActivityVisible() - wmHelper.waitImeWindowGone() - } - teardown { - eachRun { - device.pressHome() - wmHelper.waitForHomeActivityVisible() - } - test { - testApp.exit() - this.setRotation(Surface.ROTATION_0) - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - postsubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - imeWindowBecomesInvisible() - imeAppWindowBecomesInvisible(testApp) - } - - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - imeLayerBecomesInvisible() - imeAppLayerBecomesInvisible(testApp) - noUncoveredRegions(configuration.startRotation, - Surface.ROTATION_0) - - if (!isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - } - - flaky { - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(IME_WINDOW_TITLE)) - - if (isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation, - Surface.ROTATION_0) - statusBarLayerRotatesScales(configuration.startRotation, - Surface.ROTATION_0) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5, + supportedRotations = listOf(Surface.ROTATION_0)) } } -}
\ No newline at end of file +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt index cfdd8564128f..3dfa31d64f8c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt @@ -17,85 +17,74 @@ package com.android.server.wm.flicker.ime import android.platform.helpers.IAppHelper -import com.android.server.wm.flicker.dsl.LayersAssertionBuilder -import com.android.server.wm.flicker.dsl.WmAssertionBuilder +import com.android.server.wm.flicker.FlickerTestParameter const val IME_WINDOW_TITLE = "InputMethod" -@JvmOverloads -fun LayersAssertionBuilder.imeLayerBecomesVisible(bugId: Int = 0) { - all("imeLayerBecomesVisible", bugId) { - this.hidesLayer(IME_WINDOW_TITLE) - .then() - .showsLayer(IME_WINDOW_TITLE) +fun FlickerTestParameter.imeLayerBecomesVisible() { + assertLayers { + this.isInvisible(IME_WINDOW_TITLE) + .then() + .isVisible(IME_WINDOW_TITLE) } } -@JvmOverloads -fun LayersAssertionBuilder.imeLayerBecomesInvisible(bugId: Int = 0) { - all("imeLayerBecomesInvisible", bugId) { - this.showsLayer(IME_WINDOW_TITLE) - .then() - .hidesLayer(IME_WINDOW_TITLE) +fun FlickerTestParameter.imeLayerBecomesInvisible() { + assertLayers { + this.isVisible(IME_WINDOW_TITLE) + .then() + .isInvisible(IME_WINDOW_TITLE) } } -@JvmOverloads -fun LayersAssertionBuilder.imeAppLayerIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) { - all("imeAppLayerIsAlwaysVisible", bugId) { - this.showsLayer(testApp.getPackage()) +fun FlickerTestParameter.imeAppLayerIsAlwaysVisible(testApp: IAppHelper) { + assertLayers { + this.isVisible(testApp.getPackage()) } } -@JvmOverloads -fun WmAssertionBuilder.imeAppWindowIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) { - all("imeAppWindowIsAlwaysVisible", bugId) { +fun FlickerTestParameter.imeAppWindowIsAlwaysVisible(testApp: IAppHelper) { + assertWm { this.showsAppWindowOnTop(testApp.getPackage()) } } -@JvmOverloads -fun WmAssertionBuilder.imeWindowBecomesVisible(bugId: Int = 0) { - all("imeWindowBecomesVisible", bugId) { +fun FlickerTestParameter.imeWindowBecomesVisible() { + assertWm { this.hidesNonAppWindow(IME_WINDOW_TITLE) - .then() - .showsNonAppWindow(IME_WINDOW_TITLE) + .then() + .showsNonAppWindow(IME_WINDOW_TITLE) } } -@JvmOverloads -fun WmAssertionBuilder.imeWindowBecomesInvisible(bugId: Int = 0) { - all("imeWindowBecomesInvisible", bugId) { +fun FlickerTestParameter.imeWindowBecomesInvisible() { + assertWm { this.showsNonAppWindow(IME_WINDOW_TITLE) - .then() - .hidesNonAppWindow(IME_WINDOW_TITLE) + .then() + .hidesNonAppWindow(IME_WINDOW_TITLE) } } -@JvmOverloads -fun WmAssertionBuilder.imeAppWindowBecomesVisible(windowName: String, bugId: Int = 0) { - all("imeAppWindowBecomesVisible", bugId) { +fun FlickerTestParameter.imeAppWindowBecomesVisible(windowName: String) { + assertWm { this.hidesAppWindow(windowName) - .then() - .showsAppWindow(windowName) + .then() + .showsAppWindow(windowName) } } -@JvmOverloads -fun WmAssertionBuilder.imeAppWindowBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) { - all("imeAppWindowBecomesInvisible", bugId) { +fun FlickerTestParameter.imeAppWindowBecomesInvisible(testApp: IAppHelper) { + assertWm { this.showsAppWindowOnTop(testApp.getPackage()) - .then() - .appWindowNotOnTop(testApp.getPackage()) + .then() + .appWindowNotOnTop(testApp.getPackage()) } } -@JvmOverloads -fun LayersAssertionBuilder.imeAppLayerBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) { - all("imeAppLayerBecomesInvisible", bugId) { - this.skipUntilFirstAssertion() - .showsLayer(testApp.getPackage()) - .then() - .hidesLayer(testApp.getPackage()) +fun FlickerTestParameter.imeAppLayerBecomesInvisible(testApp: IAppHelper) { + assertLayers { + this.isVisible(testApp.getPackage()) + .then() + .isInvisible(testApp.getPackage()) } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt index 9e94d6e49527..6b2b930128fa 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt @@ -16,13 +16,17 @@ package com.android.server.wm.flicker.ime +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -32,14 +36,16 @@ import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEn import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop -import com.android.server.wm.flicker.helpers.isRotated +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.layerAlwaysVisible import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -50,80 +56,120 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenImeWindowTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class OpenImeWindowTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.launchViaIntent(wmHelper) + this.setRotation(testSpec.config.startRotation) + } + } + transitions { + testApp.openIME(device, wmHelper) + } + teardown { + eachRun { + testApp.closeIME(device, wmHelper) + } + test { + testApp.exit() + this.setRotation(Surface.ROTATION_0) + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible() + + @Presubmit + @Test + fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) + + @Presubmit + @Test + fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible() + + @Presubmit + @Test + fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) + } + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + + @FlakyTest + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = ImeAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - testApp.launchViaIntent(wmHelper) - this.setRotation(configuration.startRotation) - } - } - transitions { - testApp.openIME(device, wmHelper) - } - teardown { - eachRun { - testApp.closeIME(device, wmHelper) - } - test { - testApp.exit() - this.setRotation(Surface.ROTATION_0) - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - postsubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - - imeWindowBecomesVisible() - appWindowAlwaysVisibleOnTop(testApp.`package`) - } - - layersTrace { - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - noUncoveredRegions(configuration.startRotation) - imeLayerBecomesVisible() - layerAlwaysVisible(testApp.`package`) - - if (!isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation) - statusBarLayerRotatesScales(configuration.startRotation) - } - } - } - - flaky { - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry() - } - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry() - - if (isRotated) { - navBarLayerRotatesAndScales(configuration.startRotation) - statusBarLayerRotatesScales(configuration.startRotation) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5, + supportedRotations = listOf(Surface.ROTATION_0)) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt index 2fe49af26cc1..0cd5d7999a58 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt @@ -16,14 +16,18 @@ package com.android.server.wm.flicker.ime +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper import com.android.server.wm.flicker.helpers.reopenAppFromOverview -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -31,18 +35,20 @@ import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.testapp.ActivityOptions +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -53,89 +59,132 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ReOpenImeWindowTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation) + private val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.launchViaIntent(wmHelper) + testApp.openIME(device, wmHelper) + } + eachRun { + device.pressRecentApps() + wmHelper.waitImeWindowGone() + wmHelper.waitForAppTransitionIdle() + this.setRotation(testSpec.config.startRotation) + } + } + transitions { + device.reopenAppFromOverview(wmHelper) + wmHelper.waitImeWindowShown() + } + teardown { + test { + this.setRotation(Surface.ROTATION_0) + testApp.exit() + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible() + + @Presubmit + @Test + fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible() + + @Presubmit + @Test + fun imeAppWindowBecomesVisible() = + testSpec.imeAppWindowBecomesVisible(testAppComponentName.className) + + @Presubmit + @Test + // During testing the launcher is always in portrait mode + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + testSpec.config.endRotation) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible() + + @Presubmit + @Test + fun appLayerReplacesWallpaperLayer() = + testSpec.appLayerReplacesWallpaperLayer(testAppComponentName.className) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 1) { configuration -> - val testApp = ImeAppAutoFocusHelper(instrumentation, - configuration.startRotation) - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - testApp.launchViaIntent(wmHelper) - testApp.openIME(device, wmHelper) - } - eachRun { - device.pressRecentApps() - wmHelper.waitImeWindowGone() - wmHelper.waitForAppTransitionIdle() - this.setRotation(configuration.startRotation) - } - } - transitions { - device.reopenAppFromOverview() - wmHelper.waitImeWindowShown() - } - teardown { - test { - this.setRotation(Surface.ROTATION_0) - testApp.exit() - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - - imeWindowBecomesVisible() - imeAppWindowBecomesVisible(testAppComponentName.className) - wallpaperWindowBecomesInvisible() - } - - layersTrace { - noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation) - statusBarLayerIsAlwaysVisible() - navBarLayerIsAlwaysVisible() - imeLayerBecomesVisible() - appLayerReplacesWallpaperLayer(testAppComponentName.className) - - if (!isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - } - - flaky { - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry() - - if (isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 1) } } -}
\ No newline at end of file +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt index be3fa5fa3cdf..130860d31ac1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt @@ -17,12 +17,12 @@ package com.android.server.wm.flicker.launch import android.platform.helpers.IAppHelper -import com.android.server.wm.flicker.dsl.WmAssertionBuilder +import com.android.server.wm.flicker.FlickerTestParameter -fun WmAssertionBuilder.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper, bugId: Int = 0) { - all("appWindowReplacesLauncherAsTopWindow", bugId) { +fun FlickerTestParameter.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper) { + assertWm { this.showsAppWindowOnTop("Launcher") - .then() - .showsAppWindowOnTop("Snapshot", testApp.getPackage()) + .then() + .showsAppWindowOnTop("Snapshot", testApp.getPackage()) } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt index 0ec0b04339cd..74f002d67229 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt @@ -16,14 +16,17 @@ package com.android.server.wm.flicker.launch +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.endRotation +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -39,9 +42,11 @@ import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEn import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.isRotated +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -52,85 +57,122 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppColdTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class OpenAppColdTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + eachRun { + this.setRotation(testSpec.config.startRotation) + } + } + transitions { + testApp.launchViaIntent(wmHelper) + // wmHelper.waitForFullScreenApp(testApp.component) + } + teardown { + eachRun { + testApp.exit() + wmHelper.waitForAppTransitionIdle() + this.setRotation(Surface.ROTATION_0) + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun appWindowReplacesLauncherAsTopWindow() = + testSpec.appWindowReplacesLauncherAsTopWindow(testApp) + + @Presubmit + @Test + fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible() + + @Presubmit + @Test + // During testing the launcher is always in portrait mode + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + Surface.ROTATION_0) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun appLayerReplacesWallpaperLayer() = + testSpec.appLayerReplacesWallpaperLayer(testApp.`package`) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`) + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = SimpleAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - this.setRotation(configuration.startRotation) - } - } - transitions { - testApp.open() - wmHelper.waitForFullScreenApp(testApp.component) - } - teardown { - eachRun { - testApp.exit() - wmHelper.waitForAppTransitionIdle() - this.setRotation(Surface.ROTATION_0) - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - appWindowReplacesLauncherAsTopWindow(testApp) - wallpaperWindowBecomesInvisible() - } - - layersTrace { - // During testing the launcher is always in portrait mode - noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation) - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - appLayerReplacesWallpaperLayer(testApp.`package`) - - if (!isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - - eventLog { - focusChanges("NexusLauncherActivity", testApp.`package`) - } - } - - flaky { - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry() - - if (isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt index 84cc8e3ab058..18fac6a82de7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt @@ -16,17 +16,21 @@ package com.android.server.wm.flicker.launch +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.focusChanges import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.helpers.reopenAppFromOverview -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -40,9 +44,11 @@ import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.isRotated +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -53,97 +59,133 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromOverviewTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { +class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.launchViaIntent(wmHelper) + } + eachRun { + device.pressHome() + wmHelper.waitForAppTransitionIdle() + device.pressRecentApps() + wmHelper.waitForAppTransitionIdle() + this.setRotation(testSpec.config.startRotation) + } + } + transitions { + device.reopenAppFromOverview(wmHelper) + wmHelper.waitForFullScreenApp(testApp.component) + } + teardown { + test { + testApp.exit() + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Test + fun appWindowReplacesLauncherAsTopWindow() = + testSpec.appWindowReplacesLauncherAsTopWindow(testApp) + + @Test + fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible() + + @Presubmit + @Test + fun appLayerReplacesWallpaperLayer() = + testSpec.appLayerReplacesWallpaperLayer(testApp.`package`) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerIsAlwaysVisible() + } + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerIsAlwaysVisible() + } + + @Presubmit + @Test + fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`) + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + } + + @FlakyTest + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + } + + @Presubmit + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + } + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + } + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(Surface.ROTATION_0, + testSpec.config.endRotation) + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = SimpleAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation, repetitions = 5) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - testApp.launchViaIntent(wmHelper) - } - eachRun { - device.pressHome() - wmHelper.waitForAppTransitionIdle() - device.pressRecentApps() - wmHelper.waitForAppTransitionIdle() - this.setRotation(configuration.startRotation) - } - } - transitions { - device.reopenAppFromOverview() - wmHelper.waitForFullScreenApp(testApp.component) - } - teardown { - test { - testApp.exit() - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - appWindowReplacesLauncherAsTopWindow(testApp) - wallpaperWindowBecomesInvisible() - } - - layersTrace { - appLayerReplacesWallpaperLayer(testApp.`package`) - - if (!isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } else { - statusBarLayerIsAlwaysVisible() - navBarLayerIsAlwaysVisible() - } - } - - eventLog { - focusChanges("NexusLauncherActivity", testApp.`package`) - } - } - - postsubmit { - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry() - } - } - - flaky { - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry() - noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation, - bugId = 141361128) - - if (isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } else { - statusBarLayerIsAlwaysVisible() - navBarLayerIsAlwaysVisible() - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt index 1f375a5cdea8..386dafc590af 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -16,14 +16,17 @@ package com.android.server.wm.flicker.launch +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.endRotation +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.focusChanges -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible @@ -36,12 +39,13 @@ import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer +import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.isRotated +import org.junit.Assume import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -52,89 +56,122 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppWarmTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTestRunner(testSpec) { +class OpenAppWarmTest(private val testSpec: FlickerTestParameter) { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.launchViaIntent(wmHelper) + // wmHelper.waitForFullScreenApp(testApp.component) + } + eachRun { + device.pressHome() + wmHelper.waitForHomeActivityVisible() + this.setRotation(testSpec.config.startRotation) + } + } + transitions { + testApp.launchViaIntent(wmHelper) + wmHelper.waitForFullScreenApp(testApp.component) + } + teardown { + eachRun { + this.setRotation(Surface.ROTATION_0) + } + test { + testApp.exit() + } + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun appWindowReplacesLauncherAsTopWindow() = + testSpec.appWindowReplacesLauncherAsTopWindow(testApp) + + @Presubmit + @Test + fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible() + + @Presubmit + @Test + // During testing the launcher is always in portrait mode + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + Surface.ROTATION_0) + + @Presubmit + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + fun appLayerReplacesWallpaperLayer() = + testSpec.appLayerReplacesWallpaperLayer(testApp.`package`) + + @Presubmit + @Test + fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`) + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): List<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = SimpleAppHelper(instrumentation) - return FlickerTestRunnerFactory.getInstance() - .buildTest(instrumentation) { configuration -> - withTestName { buildTestTag(configuration) } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - testApp.open() - wmHelper.waitForFullScreenApp(testApp.component) - } - eachRun { - device.pressHome() - wmHelper.waitForHomeActivityVisible() - this.setRotation(configuration.startRotation) - } - } - transitions { - testApp.open() - wmHelper.waitForFullScreenApp(testApp.component) - } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } - test { - testApp.exit() - } - } - assertions { - val isRotated = configuration.startRotation.isRotated() - - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - - appWindowReplacesLauncherAsTopWindow(testApp) - wallpaperWindowBecomesInvisible() - } - - layersTrace { - // During testing the launcher is always in portrait mode - noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation) - navBarLayerIsAlwaysVisible() - statusBarLayerIsAlwaysVisible() - appLayerReplacesWallpaperLayer(testApp.`package`) - - if (!isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - - eventLog { - focusChanges("NexusLauncherActivity", testApp.`package`) - } - } - - flaky { - layersTrace { - visibleLayersShownMoreThanOneConsecutiveEntry() - - if (isRotated) { - navBarLayerRotatesAndScales(Surface.ROTATION_0, - configuration.endRotation) - statusBarLayerRotatesScales(Surface.ROTATION_0, - configuration.endRotation) - } - } - } - } - } + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } 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 7bfdd96b9af8..20e4b88ff13b 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,29 +16,28 @@ package com.android.server.wm.flicker.rotation -import android.os.Bundle +import android.platform.test.annotations.Presubmit +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.focusDoesNotChange import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.StandardAppHelper -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry +import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -49,78 +48,103 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class ChangeAppRotationTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : RotationTransition(InstrumentationRegistry.getInstrumentation()) { - override val testApp: StandardAppHelper - get() = SimpleAppHelper(instrumentation) + testSpec: FlickerTestParameter +) : RotationTransition(testSpec) { + override val testApp = SimpleAppHelper(instrumentation) + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + setup { + test { + testApp.launchViaIntent(wmHelper) + } + } + } + + @Presubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + testSpec.config.endRotation, allStates = false) + + @Presubmit + @Test + fun screenshotLayerBecomesInvisible() { + testSpec.assertLayers { + this.isVisible(testApp.getPackage()) + .then() + .isVisible(SCREENSHOT_LAYER) + .then() + .isVisible(testApp.getPackage()) + } + } - override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = emptyMap() + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + @FlakyTest(bugId = 140855415) + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @FlakyTest(bugId = 140855415) + @Test + fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @FlakyTest(bugId = 140855415) + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + + @FlakyTest(bugId = 140855415) + @Test + fun appLayerRotates_StartingPos() { + testSpec.assertLayersStart { + this.coversExactly(startingPos, testApp.getPackage()) + } + } + + @FlakyTest(bugId = 140855415) + @Test + fun appLayerRotates_EndingPos() { + testSpec.assertLayersEnd { + this.coversExactly(endingPos, testApp.getPackage()) + } + } + + @FlakyTest(bugId = 151179149) + @Test + fun focusDoesNotChange() = testSpec.focusDoesNotChange() + + companion object { private const val SCREENSHOT_LAYER = "RotationLayer" - @Parameterized.Parameters(name = "{0}1}") + @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { buildTestTag(configuration) } - assertions { - presubmit { - windowManagerTrace { - navBarWindowIsAlwaysVisible() - statusBarWindowIsAlwaysVisible() - visibleWindowsShownMoreThanOneConsecutiveEntry() - } - - layersTrace { - noUncoveredRegions(configuration.startRotation, - configuration.endRotation, allStates = false) - - all("screenshotLayerBecomesInvisible") { - this.showsLayer(testApp.getPackage()) - .then() - .showsLayer(SCREENSHOT_LAYER) - .then() - .showsLayer(testApp.getPackage()) - } - } - } - - flaky { - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - navBarLayerRotatesAndScales(configuration.startRotation, - configuration.endRotation, bugId = 140855415) - statusBarLayerRotatesScales(configuration.startRotation, - configuration.endRotation, bugId = 140855415) - visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 140855415) - - val startingPos = WindowUtils.getDisplayBounds( - configuration.startRotation) - val endingPos = WindowUtils.getDisplayBounds( - configuration.endRotation) - - start("appLayerRotates_StartingPos", bugId = 140855415) { - this.hasVisibleRegion(testApp.getPackage(), startingPos) - } - - end("appLayerRotates_EndingPos", bugId = 140855415) { - this.hasVisibleRegion(testApp.getPackage(), endingPos) - } - } - - eventLog { - focusDoesNotChange(bugId = 151179149) - } - } - } - } - - return FlickerTestRunnerFactory.getInstance() - .buildRotationTest(instrumentation, transition, testSpec, repetitions = 5) + fun getParams(): Collection<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance() + .getConfigRotationTests(repetitions = 5) } } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt index b871e949cb19..c391112a53ec 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt @@ -17,39 +17,50 @@ package com.android.server.wm.flicker.rotation import android.app.Instrumentation -import android.os.Bundle +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.StandardAppHelper +import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation -abstract class RotationTransition(protected val instrumentation: Instrumentation) { - abstract val testApp: StandardAppHelper - abstract fun getAppLaunchParams(configuration: Bundle): Map<String, String> +abstract class RotationTransition(protected val testSpec: FlickerTestParameter) { + protected abstract val testApp: StandardAppHelper - protected open val transition: FlickerBuilder.(Bundle) -> Unit - get() = { configuration -> - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - val extras = getAppLaunchParams(configuration) - testApp.launchViaIntent(wmHelper, stringExtras = extras) - } - eachRun { - this.setRotation(configuration.startRotation) - } + protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + protected val startingPos get() = WindowUtils.getDisplayBounds(testSpec.config.startRotation) + protected val endingPos get() = WindowUtils.getDisplayBounds(testSpec.config.endRotation) + + protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() } - teardown { - test { - testApp.exit() - } + eachRun { + this.setRotation(testSpec.config.startRotation) } - transitions { - this.setRotation(configuration.endRotation) + } + teardown { + test { + testApp.exit() } } + transitions { + this.setRotation(testSpec.config.endRotation) + } + } + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + transition(testSpec.config) + } + } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt index 78614640a372..fc5bcc7eef1b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt @@ -16,32 +16,31 @@ package com.android.server.wm.flicker.rotation -import android.os.Bundle +import android.platform.test.annotations.Presubmit +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.focusDoesNotChange +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.endRotation +import com.android.server.wm.flicker.focusDoesNotChange import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper -import com.android.server.wm.flicker.helpers.StandardAppHelper import com.android.server.wm.flicker.layerAlwaysVisible -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.buildTestTag import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry +import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -52,116 +51,119 @@ import org.junit.runners.Parameterized */ @RequiresDevice @RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class SeamlessAppRotationTest( - testSpec: FlickerTestRunnerFactory.TestSpec -) : FlickerTestRunner(testSpec) { - companion object : RotationTransition(InstrumentationRegistry.getInstrumentation()) { - override val testApp: StandardAppHelper - get() = SeamlessRotationAppHelper(instrumentation) + testSpec: FlickerTestParameter +) : RotationTransition(testSpec) { + override val testApp = SeamlessRotationAppHelper(instrumentation) + + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + setup { + test { + testApp.launchViaIntent(wmHelper, + stringExtras = mapOf( + ActivityOptions.EXTRA_STARVE_UI_THREAD to it.starveUiThread.toString()) + ) + } + } + } + + @Presubmit + @Test + fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + + @Presubmit + @Test + fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`) + + @Presubmit + @Test + fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`) + + @FlakyTest(bugId = 140855415) + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @FlakyTest(bugId = 140855415) + @Test + fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @FlakyTest(bugId = 147659548) + @Test + fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + testSpec.config.endRotation, allStates = false) + + @FlakyTest + @Test + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @FlakyTest + @Test + fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales( + testSpec.config.startRotation, testSpec.config.endRotation) + + @FlakyTest + @Test + fun visibleLayersShownMoreThanOneConsecutiveEntry() = + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + + @FlakyTest(bugId = 147659548) + @Test + fun appLayerRotates() { + testSpec.assertLayers { + this.coversExactly(startingPos, testApp.`package`) + } + } - override fun getAppLaunchParams(configuration: Bundle): Map<String, String> = mapOf( - ActivityOptions.EXTRA_STARVE_UI_THREAD to configuration.starveUiThread.toString() - ) + @FlakyTest(bugId = 151179149) + @Test + fun focusDoesNotChange() = testSpec.focusDoesNotChange() - private val testFactory = FlickerTestRunnerFactory.getInstance() + companion object { + private val testFactory = FlickerTestParameterFactory.getInstance() - private val Bundle.starveUiThread - get() = this.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) + private val Map<String, Any?>.starveUiThread + get() = this.getOrDefault(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) as Boolean - private fun Bundle.createConfig(starveUiThread: Boolean): Bundle { - val config = this.deepCopy() - config.putBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD, starveUiThread) + private fun FlickerTestParameter.createConfig( + starveUiThread: Boolean + ): MutableMap<String, Any?> { + val config = this.config.toMutableMap() + config[ActivityOptions.EXTRA_STARVE_UI_THREAD] = starveUiThread return config } @JvmStatic - private fun getConfigurations(): List<Bundle> { - return testFactory.getConfigRotationTests().flatMap { + private fun getConfigurations(): List<FlickerTestParameter> { + return testFactory.getConfigRotationTests(repetitions = 2).flatMap { val defaultRun = it.createConfig(starveUiThread = false) val busyUiRun = it.createConfig(starveUiThread = true) - listOf(defaultRun, busyUiRun) + listOf( + FlickerTestParameter(defaultRun), + FlickerTestParameter(busyUiRun, + name = "${FlickerTestParameter.defaultName(busyUiRun)}_BUSY_UI_THREAD" + ) + ) } } @Parameterized.Parameters(name = "{0}") @JvmStatic - fun getParams(): Collection<Array<Any>> { - val configurations = getConfigurations() - val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration -> - withTestName { - val extra = if (configuration.starveUiThread) { - "BUSY_UI_THREAD" - } else { - "" - } - buildTestTag(configuration, extraInfo = extra) - } - assertions { - val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation) - val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation) - - presubmit { - windowManagerTrace { - visibleWindowsShownMoreThanOneConsecutiveEntry() - appWindowAlwaysVisibleOnTop(testApp.`package`) - } - - layersTrace { - layerAlwaysVisible(testApp.`package`) - } - } - - flaky { - windowManagerTrace { - navBarWindowIsAlwaysVisible(bugId = 140855415) - statusBarWindowIsAlwaysVisible(bugId = 140855415) - } - - layersTrace { - navBarLayerIsAlwaysVisible(bugId = 140855415) - statusBarLayerIsAlwaysVisible(bugId = 140855415) - noUncoveredRegions(configuration.startRotation, - configuration.endRotation, allStates = false, bugId = 147659548) - navBarLayerRotatesAndScales(configuration.startRotation, - configuration.endRotation) - statusBarLayerRotatesScales(configuration.startRotation, - configuration.endRotation) - visibleLayersShownMoreThanOneConsecutiveEntry() - - all("appLayerRotates", bugId = 147659548) { - if (startingBounds == endingBounds) { - this.hasVisibleRegion( - testApp.`package`, startingBounds) - } else { - this.hasVisibleRegion(testApp.`package`, - startingBounds) - .then() - .hasVisibleRegion(testApp.`package`, - endingBounds) - } - } - - all("noUncoveredRegions", bugId = 147659548) { - if (startingBounds == endingBounds) { - this.coversAtLeastRegion(startingBounds) - } else { - this.coversAtLeastRegion(startingBounds) - .then() - .coversAtLeastRegion(endingBounds) - } - } - } - - eventLog { - focusDoesNotChange(bugId = 151179149) - } - } - } - } - - return testFactory.buildRotationTest(instrumentation, transition, testSpec, - deviceConfigurations = configurations, repetitions = 2) + fun getParams(): Collection<FlickerTestParameter> { + return getConfigurations() } } -}
\ No newline at end of file +} diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.bp b/tests/FlickerTests/test-apps/flickerapp/Android.bp index 502779723c36..78660c04d8d4 100644 --- a/tests/FlickerTests/test-apps/flickerapp/Android.bp +++ b/tests/FlickerTests/test-apps/flickerapp/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "FlickerTestApp", srcs: ["**/*.java"], diff --git a/tests/FrameworkPerf/Android.bp b/tests/FrameworkPerf/Android.bp index a259ebd05fa8..9be3ab795b86 100644 --- a/tests/FrameworkPerf/Android.bp +++ b/tests/FrameworkPerf/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "FrameworkPerf", srcs: ["**/*.java"], diff --git a/tests/GamePerformance/Android.bp b/tests/GamePerformance/Android.bp index 02908d3a4cd4..f250a1bbdaca 100644 --- a/tests/GamePerformance/Android.bp +++ b/tests/GamePerformance/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "GamePerformance", // Don't include this package in any target diff --git a/tests/GridLayoutTest/Android.bp b/tests/GridLayoutTest/Android.bp index b4b5ba561c3f..71d884c4111f 100644 --- a/tests/GridLayoutTest/Android.bp +++ b/tests/GridLayoutTest/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "GridLayoutTest", srcs: ["**/*.java"], diff --git a/tests/HierarchyViewerTest/Android.bp b/tests/HierarchyViewerTest/Android.bp index 814c88328118..9c5d1c09cedd 100644 --- a/tests/HierarchyViewerTest/Android.bp +++ b/tests/HierarchyViewerTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "HierarchyViewerTest", srcs: ["**/*.java"], diff --git a/tests/HugeBackup/Android.bp b/tests/HugeBackup/Android.bp index b44c4578a853..7d4e52acb9b2 100644 --- a/tests/HugeBackup/Android.bp +++ b/tests/HugeBackup/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "HugeBackup", // Only compile source java files in this apk. diff --git a/tests/HwAccelerationTest/Android.bp b/tests/HwAccelerationTest/Android.bp index 37d3f5d4d97f..76063227eac1 100644 --- a/tests/HwAccelerationTest/Android.bp +++ b/tests/HwAccelerationTest/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "HwAccelerationTest", srcs: ["**/*.java"], diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index c6c67feeed72..62ccb1a0b9db 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -400,6 +400,15 @@ </intent-filter> </activity> + <activity android:name="StretchySurfaceViewActivity" + android:label="SurfaceView/Stretchy Movement" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="com.android.test.hwui.TEST"/> + </intent-filter> + </activity> + <activity android:name="GetBitmapSurfaceViewActivity" android:label="SurfaceView/GetBitmap with Camera source" android:exported="true"> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java index 818d899413de..65d7363ad628 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java @@ -19,8 +19,13 @@ package com.android.test.hwui; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; +import android.graphics.PointF; +import android.graphics.RecordingCanvas; +import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.RenderNode; import android.os.Bundle; +import android.view.MotionEvent; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.ScrollView; @@ -38,7 +43,46 @@ public class PositionListenerActivity extends Activity { ProgressBar spinner = new ProgressBar(this, null, android.R.attr.progressBarStyleLarge); layout.addView(spinner); - ScrollView scrollingThing = new ScrollView(this); + ScrollView scrollingThing = new ScrollView(this) { + int setting = 0; + PointF opts[] = new PointF[] { + new PointF(0, 0), + new PointF(0, -1f), + new PointF(1f, 0), + new PointF(0, 1f), + new PointF(-1f, 0), + new PointF(-1f, 1f), + }; + { + setWillNotDraw(false); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (ev.getActionMasked() == MotionEvent.ACTION_UP) { + setting = (setting + 1) % opts.length; + invalidate(); + } + return super.onTouchEvent(ev); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + RenderNode node = ((RecordingCanvas) canvas).mNode; + PointF dir = opts[setting]; + float maxStretchAmount = 100f; + // Although we could do this in a single call, the real one won't be - so mimic that + if (dir.x != 0f) { + node.stretch(0f, 0f, (float) getWidth(), (float) getHeight(), + dir.x, 0f, maxStretchAmount); + } + if (dir.y != 0f) { + node.stretch(0f, 0f, (float) getWidth(), (float) getHeight(), + 0f, dir.y, maxStretchAmount); + } + } + }; scrollingThing.addView(new MyPositionReporter(this)); layout.addView(scrollingThing); @@ -49,6 +93,11 @@ public class PositionListenerActivity extends Activity { RenderNode mNode; int mCurrentCount = 0; int mTranslateY = 0; + Rect mPosition = new Rect(); + RectF mStretchArea = new RectF(); + float mStretchX = 0.0f; + float mStretchY = 0.0f; + float mStretchMax = 0.0f; MyPositionReporter(Context c) { super(c); @@ -78,18 +127,36 @@ public class PositionListenerActivity extends Activity { canvas.drawRenderNode(mNode); } + void updateText() { + setText(String.format("%d: Position %s, stretch area %s, vec %f,%f, amount %f", + mCurrentCount, mPosition.toShortString(), mStretchArea.toShortString(), + mStretchX, mStretchY, mStretchMax)); + } + @Override public void positionChanged(long frameNumber, int left, int top, int right, int bottom) { - post(() -> { + getHandler().postAtFrontOfQueue(() -> { mCurrentCount++; - setText(String.format("%d: Position [%d, %d, %d, %d]", mCurrentCount, - left, top, right, bottom)); + mPosition.set(left, top, right, bottom); + updateText(); + }); + } + + @Override + public void applyStretch(long frameNumber, float left, float top, float right, float bottom, + float vecX, float vecY, float maxStretch) { + getHandler().postAtFrontOfQueue(() -> { + mStretchArea.set(left, top, right, bottom); + mStretchX = vecX; + mStretchY = vecY; + mStretchMax = maxStretch; + updateText(); }); } @Override public void positionLost(long frameNumber) { - post(() -> { + getHandler().postAtFrontOfQueue(() -> { mCurrentCount++; setText(mCurrentCount + " No position"); }); diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java new file mode 100644 index 000000000000..d6042445c2d8 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java @@ -0,0 +1,157 @@ +/* + * 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.test.hwui; + +import android.animation.ObjectAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RecordingCanvas; +import android.graphics.RenderNode; +import android.os.Bundle; +import android.view.Gravity; +import android.view.SurfaceHolder; +import android.view.SurfaceHolder.Callback; +import android.view.SurfaceView; +import android.view.animation.LinearInterpolator; +import android.widget.FrameLayout; + +public class StretchySurfaceViewActivity extends Activity implements Callback { + SurfaceView mSurfaceView; + ObjectAnimator mAnimator; + + class MySurfaceView extends SurfaceView { + boolean mSlow; + boolean mScaled; + int mToggle = 0; + + public MySurfaceView(Context context) { + super(context); + setOnClickListener(v -> { + mToggle = (mToggle + 1) % 4; + mSlow = (mToggle & 0x2) != 0; + mScaled = (mToggle & 0x1) != 0; + + mSurfaceView.setScaleX(mScaled ? 1.6f : 1f); + mSurfaceView.setScaleY(mScaled ? 0.8f : 1f); + + setTitle("Slow=" + mSlow + ", scaled=" + mScaled); + invalidate(); + }); + setWillNotDraw(false); + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + if (mSlow) { + try { + Thread.sleep(16); + } catch (InterruptedException e) {} + } + } + + public void setMyTranslationY(float ty) { + setTranslationY(ty); + if (mSlow) { + invalidate(); + } + } + + public float getMyTranslationY() { + return getTranslationY(); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FrameLayout content = new FrameLayout(this) { + { + setWillNotDraw(false); + } + + @Override + protected void onDraw(Canvas canvas) { + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setColor(Color.RED); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(10f); + canvas.drawLine(0f, 0f, getWidth(), getHeight(), paint); + super.onDraw(canvas); + + RenderNode node = ((RecordingCanvas) canvas).mNode; + node.stretch(0f, 0f, getWidth(), getHeight() / 2f, 0f, 1f, 400f); + } + }; + + mSurfaceView = new MySurfaceView(this); + mSurfaceView.getHolder().addCallback(this); + + final float density = getResources().getDisplayMetrics().density; + int size = (int) (200 * density); + + content.addView(mSurfaceView, new FrameLayout.LayoutParams( + size, size, Gravity.CENTER_HORIZONTAL | Gravity.TOP)); + mAnimator = ObjectAnimator.ofFloat(mSurfaceView, "myTranslationY", + 0, size); + mAnimator.setRepeatMode(ObjectAnimator.REVERSE); + mAnimator.setRepeatCount(ObjectAnimator.INFINITE); + mAnimator.setDuration(1000); + mAnimator.setInterpolator(new LinearInterpolator()); + setContentView(content); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Canvas canvas = holder.lockCanvas(); + canvas.drawColor(Color.WHITE); + + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setColor(Color.GREEN); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(10f); + canvas.drawLine(0, 0, width, height, paint); + + holder.unlockCanvasAndPost(canvas); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + } + + @Override + protected void onResume() { + super.onResume(); + mAnimator.start(); + } + + @Override + protected void onPause() { + mAnimator.pause(); + super.onPause(); + } +} diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index a72b07c45dd8..335c8d0127eb 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "InputTests", srcs: ["src/**/*.kt"], diff --git a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt index f919a3eaf271..c19e5cc34611 100644 --- a/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt +++ b/tests/Input/src/com/android/test/input/ViewFrameInfoTest.kt @@ -62,15 +62,12 @@ class ViewFrameInfoTest { fun testUpdateFrameInfoFromViewFrameInfo() { val frameInfo = FrameInfo() // By default, all values should be zero - assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(0) - assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(0) + // TODO(b/169866723): Use InputEventAssigner and assert INPUT_EVENT_ID assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo(0) assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isEqualTo(0) // The values inside FrameInfo should match those from ViewFrameInfo after we update them mViewFrameInfo.populateFrameInfo(frameInfo) - assertThat(frameInfo.frameInfo[FrameInfo.OLDEST_INPUT_EVENT]).isEqualTo(10) - assertThat(frameInfo.frameInfo[FrameInfo.NEWEST_INPUT_EVENT]).isEqualTo(20) assertThat(frameInfo.frameInfo[FrameInfo.FLAGS]).isEqualTo( FrameInfo.FLAG_WINDOW_LAYOUT_CHANGED) assertThat(frameInfo.frameInfo[FrameInfo.DRAW_START]).isGreaterThan(mTimeStarted) diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp index 9da17db6a573..ef45864dd93b 100644 --- a/tests/Internal/Android.bp +++ b/tests/Internal/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "InternalTests", proto: { diff --git a/tests/JankBench/Android.bp b/tests/JankBench/Android.bp index 166639d2e7db..39dd197bf681 100644 --- a/tests/JankBench/Android.bp +++ b/tests/JankBench/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "JankBench", manifest: "app/src/main/AndroidManifest.xml", diff --git a/tests/JobSchedulerPerfTests/Android.bp b/tests/JobSchedulerPerfTests/Android.bp index 2ae8c33b60a7..eb9b2f636191 100644 --- a/tests/JobSchedulerPerfTests/Android.bp +++ b/tests/JobSchedulerPerfTests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "JobSchedulerPerfTests", srcs: ["src/**/*.java"], diff --git a/tests/JobSchedulerTestApp/Android.bp b/tests/JobSchedulerTestApp/Android.bp index bac0220e5591..893a983ca9d9 100644 --- a/tests/JobSchedulerTestApp/Android.bp +++ b/tests/JobSchedulerTestApp/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "JobSchedulerTestApp", srcs: ["src/**/*.java"], diff --git a/tests/LargeAssetTest/Android.bp b/tests/LargeAssetTest/Android.bp index 499e6a0721a1..2a6de77fb170 100644 --- a/tests/LargeAssetTest/Android.bp +++ b/tests/LargeAssetTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "LargeAssetTest", srcs: ["**/*.java"], diff --git a/tests/LegacyAssistant/Android.bp b/tests/LegacyAssistant/Android.bp index fef924d1cd89..ab8ef8885c45 100644 --- a/tests/LegacyAssistant/Android.bp +++ b/tests/LegacyAssistant/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "LegacyAssistant", srcs: ["**/*.java"], diff --git a/tests/LocalizationTest/Android.bp b/tests/LocalizationTest/Android.bp index c4bfcb1d2261..4e0b0a89d972 100644 --- a/tests/LocalizationTest/Android.bp +++ b/tests/LocalizationTest/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "LocalizationTest", srcs: ["java/**/*.kt"], diff --git a/tests/LocationTracker/Android.bp b/tests/LocationTracker/Android.bp index f0075a9c37bd..538687c7db45 100644 --- a/tests/LocationTracker/Android.bp +++ b/tests/LocationTracker/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "LocationTracker", srcs: ["**/*.java"], diff --git a/tests/LotsOfApps/Android.bp b/tests/LotsOfApps/Android.bp index 68b9f88ecfd7..5f6c089188c7 100644 --- a/tests/LotsOfApps/Android.bp +++ b/tests/LotsOfApps/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "LotsOfApps", srcs: ["**/*.java"], diff --git a/tests/LowStorageTest/Android.bp b/tests/LowStorageTest/Android.bp index e72e4a5e2559..6dcf39d606dd 100644 --- a/tests/LowStorageTest/Android.bp +++ b/tests/LowStorageTest/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "lowstoragetest", certificate: "platform", diff --git a/tests/ManagedProfileLifecycleStressTest/Android.bp b/tests/ManagedProfileLifecycleStressTest/Android.bp index 639ce3cfe935..3ef6322a2984 100644 --- a/tests/ManagedProfileLifecycleStressTest/Android.bp +++ b/tests/ManagedProfileLifecycleStressTest/Android.bp @@ -12,6 +12,15 @@ // 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_test_host { name: "ManagedProfileLifecycleStressTest", srcs: ["src/**/*.java"], diff --git a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp index 1f47b03d0074..7a9b6cfb134f 100644 --- a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp +++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "DummyDPC", defaults: ["cts_defaults"], diff --git a/tests/MemoryUsage/Android.bp b/tests/MemoryUsage/Android.bp index aeb533882d4f..e30a0a7cd8b5 100644 --- a/tests/MemoryUsage/Android.bp +++ b/tests/MemoryUsage/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "MemoryUsage", // Only compile source java files in this apk. diff --git a/tests/MirrorSurfaceTest/Android.bp b/tests/MirrorSurfaceTest/Android.bp index e359c64cc982..1368f260725f 100644 --- a/tests/MirrorSurfaceTest/Android.bp +++ b/tests/MirrorSurfaceTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "MirrorSurfaceTest", srcs: ["src/**/*.java"], diff --git a/tests/NativeProcessesMemoryTest/Android.bp b/tests/NativeProcessesMemoryTest/Android.bp index f2625bf2db11..b7160e95dbb6 100644 --- a/tests/NativeProcessesMemoryTest/Android.bp +++ b/tests/NativeProcessesMemoryTest/Android.bp @@ -12,6 +12,15 @@ // 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_test_host { name: "native-processes-memory-test", srcs: ["src/**/*.java"], diff --git a/tests/NetworkSecurityConfigTest/Android.bp b/tests/NetworkSecurityConfigTest/Android.bp index cf8ca57862b4..473eadbcad73 100644 --- a/tests/NetworkSecurityConfigTest/Android.bp +++ b/tests/NetworkSecurityConfigTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "NetworkSecurityConfigTests", certificate: "platform", diff --git a/tests/NullHomeTest/Android.bp b/tests/NullHomeTest/Android.bp index fc71d0deaac6..d8799a87e8c9 100644 --- a/tests/NullHomeTest/Android.bp +++ b/tests/NullHomeTest/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "NullHomeTest", srcs: ["src/**/*.java"], diff --git a/tests/OdmApps/Android.bp b/tests/OdmApps/Android.bp index d86f9cc81a5f..de86498afd27 100644 --- a/tests/OdmApps/Android.bp +++ b/tests/OdmApps/Android.bp @@ -12,6 +12,15 @@ // 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_test_host { name: "OdmAppsTest", srcs: ["src/**/*.java"], diff --git a/tests/OdmApps/app/Android.bp b/tests/OdmApps/app/Android.bp index 5eb8590b6e06..a33a1cf4690e 100644 --- a/tests/OdmApps/app/Android.bp +++ b/tests/OdmApps/app/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "TestOdmApp", test_suites: ["device-tests"], diff --git a/tests/OdmApps/priv-app/Android.bp b/tests/OdmApps/priv-app/Android.bp index 9dd477cf6ad3..7527729c8267 100644 --- a/tests/OdmApps/priv-app/Android.bp +++ b/tests/OdmApps/priv-app/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "TestOdmPrivApp", test_suites: ["device-tests"], diff --git a/tests/OneMedia/Android.bp b/tests/OneMedia/Android.bp index 11e12f35741c..5c7317735bc7 100644 --- a/tests/OneMedia/Android.bp +++ b/tests/OneMedia/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "OneMedia", srcs: [ diff --git a/tests/PackageWatchdog/Android.bp b/tests/PackageWatchdog/Android.bp index 0b75039cf69f..1e1dc8458560 100644 --- a/tests/PackageWatchdog/Android.bp +++ b/tests/PackageWatchdog/Android.bp @@ -13,6 +13,15 @@ // limitations under the License. // PackageWatchdogTest +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"], +} + android_test { name: "PackageWatchdogTest", srcs: ["src/**/*.java"], diff --git a/tests/PackageWatchdog/TEST_MAPPING b/tests/PackageWatchdog/TEST_MAPPING new file mode 100644 index 000000000000..6494a273eced --- /dev/null +++ b/tests/PackageWatchdog/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "PackageWatchdogTest" + } + ] +} diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp index 7d918cc4c18b..f0f9c4bdd721 100644 --- a/tests/PlatformCompatGating/Android.bp +++ b/tests/PlatformCompatGating/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "PlatformCompatGating", // Only compile source java files in this apk. diff --git a/tests/PlatformCompatGating/test-rules/Android.bp b/tests/PlatformCompatGating/test-rules/Android.bp index 10fa2dc0d7c6..5f91f9d0e505 100644 --- a/tests/PlatformCompatGating/test-rules/Android.bp +++ b/tests/PlatformCompatGating/test-rules/Android.bp @@ -14,6 +14,15 @@ // 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: "platform-compat-test-rules", srcs: ["src/**/*.java"], @@ -23,4 +32,4 @@ java_library { "truth-prebuilt", "core-compat-test-rules" ], -}
\ No newline at end of file +} diff --git a/tests/ProtoInputStreamTests/Android.bp b/tests/ProtoInputStreamTests/Android.bp index ecc405664128..0029080b5a89 100644 --- a/tests/ProtoInputStreamTests/Android.bp +++ b/tests/ProtoInputStreamTests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "ProtoInputStreamTests", proto: { diff --git a/tests/RemoteDisplayProvider/Android.bp b/tests/RemoteDisplayProvider/Android.bp index 6c7798fb3faf..55732d14af46 100644 --- a/tests/RemoteDisplayProvider/Android.bp +++ b/tests/RemoteDisplayProvider/Android.bp @@ -13,6 +13,15 @@ // limitations under the License. // Build the application. +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"], +} + android_test { name: "RemoteDisplayProviderTest", sdk_version: "system_current", diff --git a/tests/RenderThreadTest/Android.bp b/tests/RenderThreadTest/Android.bp index 165977607219..b18b04edb4c4 100644 --- a/tests/RenderThreadTest/Android.bp +++ b/tests/RenderThreadTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "RenderThreadTest", // Only compile source java files in this apk. diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 7dd003eb9755..6e1cef496f40 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "RollbackTest", manifest: "RollbackTest/AndroidManifest.xml", @@ -96,6 +105,7 @@ apex { key: "com.android.apex.apkrollback.test.key", apps: ["TestAppAv1"], installable: false, + updatable: false, } apex { @@ -106,6 +116,7 @@ apex { key: "com.android.apex.apkrollback.test.key", apps: ["TestAppAv2"], installable: false, + updatable: false, } apex { @@ -116,4 +127,5 @@ apex { key: "com.android.apex.apkrollback.test.key", apps: ["TestAppACrashingV2"], installable: false, -}
\ No newline at end of file + updatable: false, +} diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index 0508125edfc8..0bb0337b3b09 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -35,7 +35,6 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.cts.install.lib.Install; import com.android.cts.install.lib.InstallUtils; import com.android.cts.install.lib.TestApp; -import com.android.cts.install.lib.Uninstall; import com.android.cts.rollback.lib.Rollback; import com.android.cts.rollback.lib.RollbackUtils; import com.android.internal.R; @@ -89,7 +88,6 @@ public class StagedRollbackTest { */ @Test public void testBadApkOnly_Phase1_Install() throws Exception { - Uninstall.packages(TestApp.A); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1); Install.single(TestApp.A1).commit(); @@ -149,7 +147,6 @@ public class StagedRollbackTest { */ @Test public void testNativeWatchdogTriggersRollback_Phase1_Install() throws Exception { - Uninstall.packages(TestApp.A); Install.single(TestApp.A1).commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); @@ -183,7 +180,6 @@ public class StagedRollbackTest { */ @Test public void testNativeWatchdogTriggersRollbackForAll_Phase1_InstallA() throws Exception { - Uninstall.packages(TestApp.A); Install.single(TestApp.A1).commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); @@ -201,7 +197,6 @@ public class StagedRollbackTest { TestApp.A)).isNotNull(); // Install another package with rollback - Uninstall.packages(TestApp.B); Install.single(TestApp.B1).commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1); @@ -238,7 +233,6 @@ public class StagedRollbackTest { @Test public void testPreviouslyAbandonedRollbacks_Phase1_InstallAndAbandon() throws Exception { - Uninstall.packages(TestApp.A); Install.single(TestApp.A1).commit(); assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); @@ -265,7 +259,6 @@ public class StagedRollbackTest { public void testPreviouslyAbandonedRollbacks_Phase3_VerifyRollback() throws Exception { assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); InstallUtils.processUserData(TestApp.A); - Uninstall.packages(TestApp.A); } private static String getModuleMetadataPackageName() { @@ -301,7 +294,6 @@ public class StagedRollbackTest { @Test public void testRollbackDataPolicy_Phase1_Install() throws Exception { - Uninstall.packages(TestApp.A, TestApp.B, TestApp.C); Install.multi(TestApp.A1, TestApp.B1, TestApp.C1).commit(); // Write user data version = 1 InstallUtils.processUserData(TestApp.A); diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 65fb7b6c8cc6..304567a34ed3 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -99,10 +99,16 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex"); runPhase("expireRollbacks"); mLogger.start(getDevice()); + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A"); + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.B"); + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.C"); } @After public void tearDown() throws Exception { + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A"); + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.B"); + getDevice().uninstallPackage("com.android.cts.install.lib.testapp.C"); mLogger.stop(); runPhase("expireRollbacks"); deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex", @@ -283,7 +289,6 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { */ @Test public void testRollbackApexWithApk() throws Exception { - getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A"); pushTestApex(); runPhase("testRollbackApexWithApk_Phase1_Install"); getDevice().reboot(); @@ -297,7 +302,6 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { */ @Test public void testRollbackApexWithApkCrashing() throws Exception { - getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A"); pushTestApex(); // Install an apex with apk that crashes diff --git a/tests/SerialChat/Android.bp b/tests/SerialChat/Android.bp index 3c18035a4aab..8719e01031d5 100644 --- a/tests/SerialChat/Android.bp +++ b/tests/SerialChat/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "SerialChat", srcs: ["**/*.java"], diff --git a/tests/ServiceCrashTest/Android.bp b/tests/ServiceCrashTest/Android.bp index 40a377de852f..fb98b7631b7e 100644 --- a/tests/ServiceCrashTest/Android.bp +++ b/tests/ServiceCrashTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "ServiceCrashTest", // Only compile source java files in this apk. diff --git a/tests/SharedLibrary/client/Android.bp b/tests/SharedLibrary/client/Android.bp index dbf6dc94eb8d..6eab7c2e2dce 100644 --- a/tests/SharedLibrary/client/Android.bp +++ b/tests/SharedLibrary/client/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "SharedLibraryClient", srcs: ["**/*.java"], diff --git a/tests/SharedLibrary/lib/Android.bp b/tests/SharedLibrary/lib/Android.bp index f69d388ef0f1..0595cb1e116a 100644 --- a/tests/SharedLibrary/lib/Android.bp +++ b/tests/SharedLibrary/lib/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "SharedLibrary", srcs: ["**/*.java"], diff --git a/tests/ShowWhenLockedApp/Android.bp b/tests/ShowWhenLockedApp/Android.bp index dba564c91059..f24834a53a36 100644 --- a/tests/ShowWhenLockedApp/Android.bp +++ b/tests/ShowWhenLockedApp/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "ShowWhenLocked", srcs: ["**/*.java"], diff --git a/tests/SilkFX/Android.bp b/tests/SilkFX/Android.bp index 92e3efa7fd55..088d9a2d7f41 100644 --- a/tests/SilkFX/Android.bp +++ b/tests/SilkFX/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "SilkFX", srcs: ["**/*.java", "**/*.kt"], diff --git a/tests/SmokeTest/Android.bp b/tests/SmokeTest/Android.bp index bc45ee6799b4..4c853e3c5267 100644 --- a/tests/SmokeTest/Android.bp +++ b/tests/SmokeTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "SmokeTestApp", // This builds "SmokeTestApp" diff --git a/tests/SmokeTest/tests/Android.bp b/tests/SmokeTest/tests/Android.bp index ceb2d193de79..5542dd0119ea 100644 --- a/tests/SmokeTest/tests/Android.bp +++ b/tests/SmokeTest/tests/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "SmokeTest", // Include all test java files. diff --git a/tests/SmokeTestApps/Android.bp b/tests/SmokeTestApps/Android.bp index 0feb00040eac..3505fe1c4afb 100644 --- a/tests/SmokeTestApps/Android.bp +++ b/tests/SmokeTestApps/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "SmokeTestTriggerApps", srcs: ["src/**/*.java"], diff --git a/tests/SoundTriggerTestApp/Android.bp b/tests/SoundTriggerTestApp/Android.bp index d3a1300b8e12..09f1e10561e3 100644 --- a/tests/SoundTriggerTestApp/Android.bp +++ b/tests/SoundTriggerTestApp/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "SoundTriggerTestApp", srcs: ["**/*.java"], diff --git a/tests/Split/Android.bp b/tests/Split/Android.bp index d8c89bab2857..727b2026461c 100644 --- a/tests/Split/Android.bp +++ b/tests/Split/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "Split", srcs: ["**/*.java"], diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index 3a40696d1add..2e57467e55fe 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "StagedInstallInternalTestApp", manifest: "app/AndroidManifest.xml", @@ -41,4 +50,3 @@ java_test_host { test_suites: ["general-tests"], test_config: "StagedInstallInternalTest.xml", } - diff --git a/tests/StatusBar/Android.bp b/tests/StatusBar/Android.bp index 0b650ed3afc8..2fad051300c7 100644 --- a/tests/StatusBar/Android.bp +++ b/tests/StatusBar/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "StatusBarTest", srcs: ["**/*.java"], diff --git a/tests/SurfaceComposition/Android.bp b/tests/SurfaceComposition/Android.bp index 53e4d52b2efd..f5aba8f5a2f2 100644 --- a/tests/SurfaceComposition/Android.bp +++ b/tests/SurfaceComposition/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "SurfaceComposition", // Don't include this package in any target diff --git a/tests/SurfaceControlViewHostTest/Android.bp b/tests/SurfaceControlViewHostTest/Android.bp index e4e060010eea..0127ba559500 100644 --- a/tests/SurfaceControlViewHostTest/Android.bp +++ b/tests/SurfaceControlViewHostTest/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "SurfaceControlViewHostTest", srcs: ["**/*.java"], diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp index 48031de15f54..dc75f00e7cdc 100644 --- a/tests/SurfaceViewBufferTests/Android.bp +++ b/tests/SurfaceViewBufferTests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "SurfaceViewBufferTests", srcs: ["**/*.java","**/*.kt"], diff --git a/tests/SystemMemoryTest/device/Android.bp b/tests/SystemMemoryTest/device/Android.bp index 2bf0fec0fd1f..d7cec1aedaed 100644 --- a/tests/SystemMemoryTest/device/Android.bp +++ b/tests/SystemMemoryTest/device/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test_helper_app { name: "SystemMemoryTestDevice", sdk_version: "current", diff --git a/tests/SystemMemoryTest/host/Android.bp b/tests/SystemMemoryTest/host/Android.bp index 3bb5489dab6c..79744625b752 100644 --- a/tests/SystemMemoryTest/host/Android.bp +++ b/tests/SystemMemoryTest/host/Android.bp @@ -12,6 +12,15 @@ // 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_test_host { name: "system-memory-test", srcs: ["src/**/*.java"], diff --git a/tests/SystemUIDemoModeController/Android.bp b/tests/SystemUIDemoModeController/Android.bp index 1e4c43792d70..d952cf6a4b3e 100644 --- a/tests/SystemUIDemoModeController/Android.bp +++ b/tests/SystemUIDemoModeController/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "DemoModeController", srcs: ["**/*.java"], diff --git a/tests/TaskOrganizerTest/Android.bp b/tests/TaskOrganizerTest/Android.bp index 91900626a9cc..9b72d359aae6 100644 --- a/tests/TaskOrganizerTest/Android.bp +++ b/tests/TaskOrganizerTest/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "TaskOrganizerTest", srcs: ["**/*.java","**/*.kt"], diff --git a/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp index 4f7569d4f451..a9fbfd97225d 100644 --- a/tests/TelephonyCommonTests/Android.bp +++ b/tests/TelephonyCommonTests/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "TelephonyCommonTests", srcs: [ diff --git a/tests/TouchLatency/Android.bp b/tests/TouchLatency/Android.bp index 1174bcb0d4d6..3a9e240d9746 100644 --- a/tests/TouchLatency/Android.bp +++ b/tests/TouchLatency/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "TouchLatency", manifest: "app/src/main/AndroidManifest.xml", diff --git a/tests/TransformTest/Android.bp b/tests/TransformTest/Android.bp index fd7aaeb35feb..f58fe8f13bda 100644 --- a/tests/TransformTest/Android.bp +++ b/tests/TransformTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "TransformTest", srcs: ["**/*.java"], diff --git a/tests/TransitionTests/Android.bp b/tests/TransitionTests/Android.bp index 57f19e38330d..4daa5b84f71e 100644 --- a/tests/TransitionTests/Android.bp +++ b/tests/TransitionTests/Android.bp @@ -1,3 +1,14 @@ +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 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_base_license"], +} + android_app { name: "TransitionTests", // Only compile source java files in this apk. diff --git a/tests/TtsTests/Android.bp b/tests/TtsTests/Android.bp index b137523803a6..b7aa5d4a38aa 100644 --- a/tests/TtsTests/Android.bp +++ b/tests/TtsTests/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "TtsTests", srcs: ["**/*.java"], diff --git a/tests/UiBench/Android.bp b/tests/UiBench/Android.bp index e0608e288459..0d2f2ef46cab 100644 --- a/tests/UiBench/Android.bp +++ b/tests/UiBench/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "UiBench", sdk_version: "current", diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp index 43a5078c3c24..ee24d48f0ed5 100644 --- a/tests/UpdatableSystemFontTest/Android.bp +++ b/tests/UpdatableSystemFontTest/Android.bp @@ -12,6 +12,15 @@ // 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_test_host { name: "UpdatableSystemFontTest", srcs: ["src/**/*.java"], diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp index 1296699e3c9f..f744d5dd2b51 100644 --- a/tests/UpdatableSystemFontTest/testdata/Android.bp +++ b/tests/UpdatableSystemFontTest/testdata/Android.bp @@ -12,6 +12,15 @@ // 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: "UpdatableSystemFontTestKeyPem", srcs: ["UpdatableSystemFontTestKey.pem"], diff --git a/tests/UsageReportingTest/Android.bp b/tests/UsageReportingTest/Android.bp index 0bac5a224b26..dfce0705df59 100644 --- a/tests/UsageReportingTest/Android.bp +++ b/tests/UsageReportingTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "UsageReportingTest", // Only compile source java files in this apk. diff --git a/tests/UsageStatsPerfTests/Android.bp b/tests/UsageStatsPerfTests/Android.bp index 3991fb8366ac..0e372a3ef95a 100644 --- a/tests/UsageStatsPerfTests/Android.bp +++ b/tests/UsageStatsPerfTests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "UsageStatsPerfTests", srcs: ["src/**/*.java"], diff --git a/tests/UsageStatsTest/Android.bp b/tests/UsageStatsTest/Android.bp index 0808b05ec053..afb266bf326d 100644 --- a/tests/UsageStatsTest/Android.bp +++ b/tests/UsageStatsTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "UsageStatsTest", // Only compile source java files in this apk. diff --git a/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.bp b/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.bp index c7e9df0fe9cf..9133baed7179 100644 --- a/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.bp +++ b/tests/UsbHostExternalManagmentTest/AoapTestDevice/Android.bp @@ -16,6 +16,15 @@ //################################################# +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"], +} + android_test { name: "AoapTestDeviceApp", srcs: ["src/**/*.java"], diff --git a/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.bp b/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.bp index 6fa58cb5c682..68930023de96 100644 --- a/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.bp +++ b/tests/UsbHostExternalManagmentTest/AoapTestHost/Android.bp @@ -16,6 +16,15 @@ //################################################# +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"], +} + android_test { name: "AoapTestHostApp", srcs: ["src/**/*.java"], diff --git a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.bp b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.bp index edd4205968b3..2fca4d35fe01 100644 --- a/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.bp +++ b/tests/UsbHostExternalManagmentTest/UsbHostExternalManagmentTestApp/Android.bp @@ -17,6 +17,15 @@ //################################################# // TODO: should this be android_helper_test_app? +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"], +} + android_app { name: "UsbHostExternalManagementTestApp", srcs: ["src/**/*.java"], diff --git a/tests/UsbManagerTests/Android.bp b/tests/UsbManagerTests/Android.bp index a03c6e223b74..97fbf5b32035 100644 --- a/tests/UsbManagerTests/Android.bp +++ b/tests/UsbManagerTests/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "UsbManagerTests", srcs: ["src/**/*.java"], diff --git a/tests/UsbManagerTests/lib/Android.bp b/tests/UsbManagerTests/lib/Android.bp index 3c5d91b326d0..994484cd63bf 100644 --- a/tests/UsbManagerTests/lib/Android.bp +++ b/tests/UsbManagerTests/lib/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_library { name: "UsbManagerTestLib", srcs: ["src/**/*.java"], diff --git a/tests/UsbTests/Android.bp b/tests/UsbTests/Android.bp index 7c2be9b63ac3..9328b67795cb 100644 --- a/tests/UsbTests/Android.bp +++ b/tests/UsbTests/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "UsbTests", srcs: ["**/*.java"], diff --git a/tests/UsesFeature2Test/Android.bp b/tests/UsesFeature2Test/Android.bp index a1b77d07ccdc..624e4ec7ebd8 100644 --- a/tests/UsesFeature2Test/Android.bp +++ b/tests/UsesFeature2Test/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "UsesFeature2Test", srcs: ["**/*.java"], diff --git a/tests/VectorDrawableTest/Android.bp b/tests/VectorDrawableTest/Android.bp index 13f318ef7c42..9da7c5fdbb17 100644 --- a/tests/VectorDrawableTest/Android.bp +++ b/tests/VectorDrawableTest/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "VectorDrawableTest", srcs: ["**/*.java"], diff --git a/tests/VoiceEnrollment/Android.bp b/tests/VoiceEnrollment/Android.bp index e43b38c5a034..b5d62bbd68a8 100644 --- a/tests/VoiceEnrollment/Android.bp +++ b/tests/VoiceEnrollment/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "VoiceEnrollment", srcs: ["**/*.java"], diff --git a/tests/VoiceInteraction/Android.bp b/tests/VoiceInteraction/Android.bp index 7059473fb63f..1aa7fafcd37c 100644 --- a/tests/VoiceInteraction/Android.bp +++ b/tests/VoiceInteraction/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "VoiceInteraction", srcs: ["**/*.java"], diff --git a/tests/WallpaperTest/Android.bp b/tests/WallpaperTest/Android.bp index f68b6ec2452d..b009af25f9da 100644 --- a/tests/WallpaperTest/Android.bp +++ b/tests/WallpaperTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "WallpaperTest", srcs: ["src/**/*.java"], diff --git a/tests/WindowAnimationJank/Android.bp b/tests/WindowAnimationJank/Android.bp index 50b2297386cc..ed86aa5f90ea 100644 --- a/tests/WindowAnimationJank/Android.bp +++ b/tests/WindowAnimationJank/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "WindowAnimationJank", srcs: ["src/**/*.java"], diff --git a/tests/WindowInsetsTests/Android.bp b/tests/WindowInsetsTests/Android.bp index 7272152dc257..b1f4819fc0bb 100644 --- a/tests/WindowInsetsTests/Android.bp +++ b/tests/WindowInsetsTests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "WindowInsetsTests", srcs: ["src/**/*.java"], @@ -24,4 +33,3 @@ android_test { "com.google.android.material_material", ], } - diff --git a/tests/appwidgets/AppWidgetHostTest/Android.bp b/tests/appwidgets/AppWidgetHostTest/Android.bp index 24b76136c045..a3838e5405d1 100644 --- a/tests/appwidgets/AppWidgetHostTest/Android.bp +++ b/tests/appwidgets/AppWidgetHostTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "AppWidgetHostTest", srcs: ["**/*.java"], diff --git a/tests/appwidgets/AppWidgetProviderTest/Android.bp b/tests/appwidgets/AppWidgetProviderTest/Android.bp index a1a599177a01..a9ee7ad93663 100644 --- a/tests/appwidgets/AppWidgetProviderTest/Android.bp +++ b/tests/appwidgets/AppWidgetProviderTest/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "AppWidgetProvider", srcs: ["**/*.java"], diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk index e9618300fc48..9b155c930871 100644 --- a/tests/backup/Android.mk +++ b/tests/backup/Android.mk @@ -24,6 +24,9 @@ LOCAL_SRC_FILES := \ LOCAL_CFLAGS := -Wall -Werror LOCAL_MODULE_TAGS := optional LOCAL_MODULE := backup_helper_test +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) LOCAL_SHARED_LIBRARIES := libandroidfw libutils diff --git a/tests/benchmarks/Android.bp b/tests/benchmarks/Android.bp index f16ddb9bc68e..f87ca2ef928b 100644 --- a/tests/benchmarks/Android.bp +++ b/tests/benchmarks/Android.bp @@ -15,6 +15,15 @@ // build framework base core benchmarks // ============================================================ +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: "networkStatsFactory-benchmarks", installable: true, diff --git a/tests/benchmarks/internal/Android.bp b/tests/benchmarks/internal/Android.bp index 9c34eaf2af01..74ed7a34f626 100644 --- a/tests/benchmarks/internal/Android.bp +++ b/tests/benchmarks/internal/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "InternalBenchTests", srcs: ["src/**/*.java"], @@ -23,4 +32,3 @@ android_test { platform_apis: true, certificate: "platform" } - diff --git a/tests/libs-permissions/Android.bp b/tests/libs-permissions/Android.bp index 66a1f83dc308..a8ce8a4cd956 100644 --- a/tests/libs-permissions/Android.bp +++ b/tests/libs-permissions/Android.bp @@ -1,3 +1,12 @@ +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: "com.android.test.libs.product", installable: true, diff --git a/tests/net/Android.bp b/tests/net/Android.bp index ffde68eab578..81224957b2c7 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -1,6 +1,15 @@ //######################################################################## // Build FrameworksNetTests package //######################################################################## +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_defaults { name: "FrameworksNetTests-jni-defaults", jni_libs: [ diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp index c271f49ee537..babb81c5fa34 100644 --- a/tests/net/common/Android.bp +++ b/tests/net/common/Android.bp @@ -16,6 +16,15 @@ // Tests in this folder are included both in unit tests and CTS. // They must be fast and stable, and exercise public or test APIs. +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: "FrameworksNetCommonTests", srcs: ["java/**/*.java", "java/**/*.kt"], diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt index ad5bbf220d57..18a93319b271 100644 --- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt +++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt @@ -55,14 +55,14 @@ class CaptivePortalDataTest { .build() private val dataFromPasspoint = CaptivePortalData.Builder() - .setUserPortalUrl(Uri.parse("https://tc.example.com/passpoint"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) - .setVenueInfoUrl(Uri.parse("https://venue.example.com/passpoint"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) .setCaptive(true) .apply { if (SdkLevel.isAtLeastS()) { setVenueFriendlyName("venue friendly name") + setUserPortalUrl(Uri.parse("https://tc.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + setVenueInfoUrl(Uri.parse("https://venue.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } } .build() @@ -96,28 +96,28 @@ class CaptivePortalDataTest { if (SdkLevel.isAtLeastS()) { assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") } assertNotEqualsAfterChange { it.setVenueFriendlyName(null) } - } - assertEquals(dataFromPasspoint, CaptivePortalData.Builder(dataFromPasspoint).build()) - assertNotEqualsAfterChange { it.setUserPortalUrl( - Uri.parse("https://tc.example.com/passpoint")) } - assertNotEqualsAfterChange { it.setUserPortalUrl( - Uri.parse("https://tc.example.com/passpoint"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } - assertNotEqualsAfterChange { it.setUserPortalUrl( - Uri.parse("https://tc.example.com/other"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } - assertNotEqualsAfterChange { it.setUserPortalUrl( - Uri.parse("https://tc.example.com/passpoint"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } - assertNotEqualsAfterChange { it.setVenueInfoUrl( - Uri.parse("https://venue.example.com/passpoint")) } - assertNotEqualsAfterChange { it.setVenueInfoUrl( - Uri.parse("https://venue.example.com/other"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } - assertNotEqualsAfterChange { it.setVenueInfoUrl( - Uri.parse("https://venue.example.com/passpoint"), - CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } + assertEquals(dataFromPasspoint, CaptivePortalData.Builder(dataFromPasspoint).build()) + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/passpoint")) } + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/other"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } + assertNotEqualsAfterChange { it.setUserPortalUrl( + Uri.parse("https://tc.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } + assertNotEqualsAfterChange { it.setVenueInfoUrl( + Uri.parse("https://venue.example.com/passpoint")) } + assertNotEqualsAfterChange { it.setVenueInfoUrl( + Uri.parse("https://venue.example.com/other"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) } + assertNotEqualsAfterChange { it.setVenueInfoUrl( + Uri.parse("https://venue.example.com/passpoint"), + CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) } + } } @Test diff --git a/tests/net/common/java/android/net/NetworkStackTest.java b/tests/net/common/java/android/net/NetworkStackTest.java index a99aa0106655..f8f9c72374ad 100644 --- a/tests/net/common/java/android/net/NetworkStackTest.java +++ b/tests/net/common/java/android/net/NetworkStackTest.java @@ -15,20 +15,8 @@ */ package android.net; -import static android.Manifest.permission.NETWORK_STACK; -import static android.content.pm.PackageManager.PERMISSION_DENIED; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; -import static android.net.NetworkStack.checkNetworkStackPermission; -import static android.net.NetworkStack.checkNetworkStackPermissionOr; - import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.when; -import android.content.Context; import android.os.Build; import android.os.IBinder; @@ -46,44 +34,15 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class NetworkStackTest { - private static final String [] OTHER_PERMISSION = {"otherpermission1", "otherpermission2"}; - @Rule public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule(); - @Mock Context mCtx; @Mock private IBinder mConnectorBinder; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); } - @Test - public void testCheckNetworkStackPermission() throws Exception { - when(mCtx.checkCallingOrSelfPermission(eq(NETWORK_STACK))).thenReturn(PERMISSION_GRANTED); - when(mCtx.checkCallingOrSelfPermission(eq(PERMISSION_MAINLINE_NETWORK_STACK))) - .thenReturn(PERMISSION_DENIED); - checkNetworkStackPermission(mCtx); - checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION); - - when(mCtx.checkCallingOrSelfPermission(eq(NETWORK_STACK))).thenReturn(PERMISSION_DENIED); - when(mCtx.checkCallingOrSelfPermission(eq(PERMISSION_MAINLINE_NETWORK_STACK))) - .thenReturn(PERMISSION_GRANTED); - checkNetworkStackPermission(mCtx); - checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION); - - when(mCtx.checkCallingOrSelfPermission(any())).thenReturn(PERMISSION_DENIED); - - try { - checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION); - } catch (SecurityException e) { - // Expect to get a SecurityException - return; - } - - fail("Expect fail but permission granted."); - } - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) public void testGetService() { NetworkStack.setServiceForTest(mConnectorBinder); diff --git a/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt b/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt new file mode 100644 index 000000000000..56b56efd501b --- /dev/null +++ b/tests/net/common/java/android/net/NetworkStateSnapshotTest.kt @@ -0,0 +1,73 @@ +/* + * 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.net + +import android.net.ConnectivityManager.TYPE_NONE +import android.net.ConnectivityManager.TYPE_WIFI +import android.net.InetAddresses.parseNumericAddress +import android.net.NetworkCapabilities.TRANSPORT_WIFI +import android.os.Build +import androidx.test.filters.SmallTest +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRunner +import com.android.testutils.assertParcelSane +import org.junit.Test +import org.junit.runner.RunWith +import java.net.Inet4Address +import java.net.Inet6Address + +private const val TEST_IMSI = "imsi1" +private const val TEST_SSID = "SSID1" +private const val TEST_NETID = 123 + +private val TEST_IPV4_GATEWAY = parseNumericAddress("192.168.222.3") as Inet4Address +private val TEST_IPV6_GATEWAY = parseNumericAddress("2001:db8::1") as Inet6Address +private val TEST_IPV4_LINKADDR = LinkAddress("192.168.222.123/24") +private val TEST_IPV6_LINKADDR = LinkAddress("2001:db8::123/64") +private val TEST_IFACE = "fake0" +private val TEST_LINK_PROPERTIES = LinkProperties().apply { + interfaceName = TEST_IFACE + addLinkAddress(TEST_IPV4_LINKADDR) + addLinkAddress(TEST_IPV6_LINKADDR) + + // Add default routes + addRoute(RouteInfo(IpPrefix(parseNumericAddress("0.0.0.0"), 0), TEST_IPV4_GATEWAY)) + addRoute(RouteInfo(IpPrefix(parseNumericAddress("::"), 0), TEST_IPV6_GATEWAY)) +} + +private val TEST_CAPABILITIES = NetworkCapabilities().apply { + addTransportType(TRANSPORT_WIFI) + setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false) + setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true) + setSSID(TEST_SSID) +} + +@SmallTest +@RunWith(DevSdkIgnoreRunner::class) +@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R) +class NetworkStateSnapshotTest { + + @Test + fun testParcelUnparcel() { + val emptySnapshot = NetworkStateSnapshot(LinkProperties(), NetworkCapabilities(), + Network(TEST_NETID), null, TYPE_NONE) + val snapshot = NetworkStateSnapshot( + TEST_LINK_PROPERTIES, TEST_CAPABILITIES, Network(TEST_NETID), TEST_IMSI, TYPE_WIFI) + assertParcelSane(emptySnapshot, 5) + assertParcelSane(snapshot, 5) + } +} diff --git a/tests/net/deflake/Android.bp b/tests/net/deflake/Android.bp index b1b017131c64..58ece37ef647 100644 --- a/tests/net/deflake/Android.bp +++ b/tests/net/deflake/Android.bp @@ -14,6 +14,15 @@ // 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_test_host { name: "FrameworksNetDeflakeTest", srcs: ["src/**/*.kt"], diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp index 69742b9bf7b2..56f9df78c83e 100644 --- a/tests/net/integration/Android.bp +++ b/tests/net/integration/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "FrameworksNetIntegrationTests", platform_apis: true, @@ -26,7 +35,7 @@ android_test { "android.test.mock", ], static_libs: [ - "TestNetworkStackLib", + "NetworkStackApiStableLib", "androidx.test.ext.junit", "frameworks-net-integration-testutils", "kotlin-reflect", diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt index 083c8c8741da..9ed55f098a16 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt +++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt @@ -38,6 +38,7 @@ import android.net.metrics.IpConnectivityLog import android.os.ConditionVariable import android.os.IBinder import android.os.INetworkManagementService +import android.os.SystemConfigManager import android.os.UserHandle import android.testing.TestableContext import android.util.Log @@ -57,6 +58,7 @@ import org.junit.BeforeClass import org.junit.Test import org.junit.runner.RunWith import org.mockito.AdditionalAnswers +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.anyInt @@ -94,6 +96,8 @@ class ConnectivityServiceIntegrationTest { private lateinit var netd: INetd @Mock private lateinit var dnsResolver: IDnsResolver + @Mock + private lateinit var systemConfigManager: SystemConfigManager @Spy private var context = TestableContext(realContext) @@ -151,6 +155,11 @@ class ConnectivityServiceIntegrationTest { doReturn(UserHandle.ALL).`when`(asUserCtx).user doReturn(asUserCtx).`when`(context).createContextAsUser(eq(UserHandle.ALL), anyInt()) doNothing().`when`(context).sendStickyBroadcast(any(), any()) + doReturn(Context.SYSTEM_CONFIG_SERVICE).`when`(context) + .getSystemServiceName(SystemConfigManager::class.java) + doReturn(systemConfigManager).`when`(context) + .getSystemService(Context.SYSTEM_CONFIG_SERVICE) + doReturn(IntArray(0)).`when`(systemConfigManager).getSystemPermissionUids(anyString()) networkStackClient = TestNetworkStackClient(realContext) networkStackClient.init() diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java index dc9e587332cb..e1da3d0ae2b3 100644 --- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java +++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java @@ -17,6 +17,7 @@ package com.android.server; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; @@ -84,6 +85,7 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { final String typeName = ConnectivityManager.getNetworkTypeName(type); mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities(); mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); mNetworkCapabilities.addTransportType(transport); switch (transport) { case TRANSPORT_ETHERNET: diff --git a/tests/net/java/android/net/NetworkIdentityTest.kt b/tests/net/java/android/net/NetworkIdentityTest.kt new file mode 100644 index 000000000000..eb2b85c14578 --- /dev/null +++ b/tests/net/java/android/net/NetworkIdentityTest.kt @@ -0,0 +1,54 @@ +/* + * 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.net + +import android.net.NetworkIdentity.OEM_NONE +import android.net.NetworkIdentity.OEM_PAID +import android.net.NetworkIdentity.OEM_PRIVATE +import android.net.NetworkIdentity.getOemBitfield +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import kotlin.test.assertEquals + +@RunWith(JUnit4::class) +class NetworkIdentityTest { + @Test + fun testGetOemBitfield() { + val oemNone = NetworkCapabilities().apply { + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, false) + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, false) + } + val oemPaid = NetworkCapabilities().apply { + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, true) + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, false) + } + val oemPrivate = NetworkCapabilities().apply { + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, false) + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, true) + } + val oemAll = NetworkCapabilities().apply { + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, true) + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, true) + } + + assertEquals(getOemBitfield(oemNone), OEM_NONE) + assertEquals(getOemBitfield(oemPaid), OEM_PAID) + assertEquals(getOemBitfield(oemPrivate), OEM_PRIVATE) + assertEquals(getOemBitfield(oemAll), OEM_PAID or OEM_PRIVATE) + } +} diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt index 1f8f6f311069..27224c216db3 100644 --- a/tests/net/java/android/net/NetworkTemplateTest.kt +++ b/tests/net/java/android/net/NetworkTemplateTest.kt @@ -20,14 +20,23 @@ import android.content.Context import android.net.ConnectivityManager.TYPE_MOBILE import android.net.ConnectivityManager.TYPE_WIFI import android.net.NetworkIdentity.SUBTYPE_COMBINED +import android.net.NetworkIdentity.OEM_NONE; +import android.net.NetworkIdentity.OEM_PAID; +import android.net.NetworkIdentity.OEM_PRIVATE; import android.net.NetworkIdentity.buildNetworkIdentity import android.net.NetworkStats.DEFAULT_NETWORK_ALL import android.net.NetworkStats.METERED_ALL import android.net.NetworkStats.ROAMING_ALL +import android.net.NetworkTemplate.MATCH_ETHERNET import android.net.NetworkTemplate.MATCH_MOBILE +import android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD import android.net.NetworkTemplate.MATCH_WIFI +import android.net.NetworkTemplate.MATCH_WIFI_WILDCARD import android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA import android.net.NetworkTemplate.NETWORK_TYPE_ALL +import android.net.NetworkTemplate.OEM_MANAGED_ALL +import android.net.NetworkTemplate.OEM_MANAGED_NO +import android.net.NetworkTemplate.OEM_MANAGED_YES import android.net.NetworkTemplate.buildTemplateMobileWithRatType import android.telephony.TelephonyManager import com.android.testutils.assertParcelSane @@ -37,9 +46,11 @@ import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations +import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotEquals import kotlin.test.assertTrue +import kotlin.test.fail private const val TEST_IMSI1 = "imsi1" private const val TEST_IMSI2 = "imsi2" @@ -57,15 +68,20 @@ class NetworkTemplateTest { private fun buildNetworkState( type: Int, subscriberId: String? = null, - ssid: String? = null + ssid: String? = null, + oemManaged: Int = OEM_NONE, ): NetworkState { val lp = LinkProperties() val caps = NetworkCapabilities().apply { setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false) setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true) setSSID(ssid) + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, + (oemManaged and OEM_PAID) == OEM_PAID) + setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, + (oemManaged and OEM_PRIVATE) == OEM_PRIVATE) } - return NetworkState(type, lp, caps, mock(Network::class.java), subscriberId, ssid) + return NetworkState(type, lp, caps, mock(Network::class.java), subscriberId) } private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) = @@ -136,11 +152,15 @@ class NetworkTemplateTest { @Test fun testParcelUnparcel() { val templateMobile = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1, null, null, METERED_ALL, - ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_LTE) + ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_LTE, + OEM_MANAGED_ALL) val templateWifi = NetworkTemplate(MATCH_WIFI, null, null, TEST_SSID1, METERED_ALL, - ROAMING_ALL, DEFAULT_NETWORK_ALL, 0) - assertParcelSane(templateMobile, 8) - assertParcelSane(templateWifi, 8) + ROAMING_ALL, DEFAULT_NETWORK_ALL, 0, OEM_MANAGED_ALL) + val templateOem = NetworkTemplate(MATCH_MOBILE, null, null, null, METERED_ALL, + ROAMING_ALL, DEFAULT_NETWORK_ALL, 0, OEM_MANAGED_YES) + assertParcelSane(templateMobile, 9) + assertParcelSane(templateWifi, 9) + assertParcelSane(templateOem, 9) } // Verify NETWORK_TYPE_* constants in NetworkTemplate do not conflict with @@ -152,4 +172,81 @@ class NetworkTemplateTest { assertNotEquals(NETWORK_TYPE_5G_NSA, ratType) } } + + @Test + fun testOemNetworkConstants() { + val constantValues = arrayOf(OEM_MANAGED_YES, OEM_MANAGED_ALL, OEM_MANAGED_NO, + OEM_PAID, OEM_PRIVATE, OEM_PAID or OEM_PRIVATE) + + // Verify that "not OEM managed network" constants are equal. + assertEquals(OEM_MANAGED_NO, OEM_NONE); + + // Verify the constants don't conflict. + assertEquals(constantValues.size, constantValues.distinct().count()) + } + + /** + * Helper to enumerate and assert OEM managed wifi and mobile {@code NetworkTemplate}s match + * their the appropriate OEM managed {@code NetworkIdentity}s. + * + * @param networkType {@code TYPE_MOBILE} or {@code TYPE_WIFI} + * @param matchType A match rule from {@code NetworkTemplate.MATCH_*} corresponding to the + * networkType. + * @param subscriberId To be populated with {@code TEST_IMSI*} only if networkType is + * {@code TYPE_MOBILE}. May be left as null when matchType is + * {@link NetworkTemplate.MATCH_MOBILE_WILDCARD}. + * @param templateSsid Top be populated with {@code TEST_SSID*} only if networkType is + * {@code TYPE_WIFI}. May be left as null when matchType is + * {@link NetworkTemplate.MATCH_WIFI_WILDCARD}. + * @param identSsid If networkType is {@code TYPE_WIFI}, this value must *NOT* be null. Provide + * one of {@code TEST_SSID*}. + */ + private fun matchOemManagedIdent(networkType: Int, matchType:Int, subscriberId: String? = null, + templateSsid: String? = null, identSsid: String? = null) { + val oemManagedStates = arrayOf(OEM_NONE, OEM_PAID, OEM_PRIVATE, OEM_PAID or OEM_PRIVATE) + // A null subscriberId needs a null matchSubscriberIds argument as well. + val matchSubscriberIds = if (subscriberId == null) null else arrayOf(subscriberId) + + val templateOemYes = NetworkTemplate(matchType, subscriberId, matchSubscriberIds, + templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, + OEM_MANAGED_YES) + val templateOemAll = NetworkTemplate(matchType, subscriberId, matchSubscriberIds, + templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, + OEM_MANAGED_ALL) + + for (identityOemManagedState in oemManagedStates) { + val ident = buildNetworkIdentity(mockContext, buildNetworkState(networkType, + subscriberId, identSsid, identityOemManagedState), /*defaultNetwork=*/false, + /*subType=*/0) + + // Create a template with each OEM managed type and match it against the NetworkIdentity + for (templateOemManagedState in oemManagedStates) { + val template = NetworkTemplate(matchType, subscriberId, matchSubscriberIds, + templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, + NETWORK_TYPE_ALL, templateOemManagedState) + if (identityOemManagedState == templateOemManagedState) { + template.assertMatches(ident) + } else { + template.assertDoesNotMatch(ident) + } + } + // OEM_MANAGED_ALL ignores OEM state. + templateOemAll.assertMatches(ident) + if (identityOemManagedState == OEM_NONE) { + // OEM_MANAGED_YES matches everything except OEM_NONE. + templateOemYes.assertDoesNotMatch(ident) + } else { + templateOemYes.assertMatches(ident) + } + } + } + + @Test + fun testOemManagedMatchesIdent() { + matchOemManagedIdent(TYPE_MOBILE, MATCH_MOBILE, subscriberId = TEST_IMSI1) + matchOemManagedIdent(TYPE_MOBILE, MATCH_MOBILE_WILDCARD) + matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI, templateSsid = TEST_SSID1, + identSsid = TEST_SSID1) + matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI_WILDCARD, identSsid = TEST_SSID1) + } } diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java index 866f38c84333..d04c87b29c25 100644 --- a/tests/net/java/android/net/VpnTransportInfoTest.java +++ b/tests/net/java/android/net/VpnTransportInfoTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 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. diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt new file mode 100644 index 000000000000..9b0cfa9db30f --- /dev/null +++ b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt @@ -0,0 +1,137 @@ +/* + * 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.net.util + +import android.content.Context +import android.content.res.Resources +import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER +import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE +import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY +import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdChangedListener +import android.provider.Settings +import android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI +import android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE +import android.telephony.SubscriptionInfo +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import android.test.mock.MockContentResolver +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.internal.R +import com.android.internal.util.test.FakeSettingsProvider +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.argThat +import org.mockito.Mockito.any +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import org.mockito.Mockito.times +import org.mockito.Mockito.verify + +/** + * Tests for [MultinetworkPolicyTracker]. + * + * Build, install and run with: + * atest android.net.util.MultinetworkPolicyTrackerTest + */ +@RunWith(AndroidJUnit4::class) +@SmallTest +class MultinetworkPolicyTrackerTest { + private val resources = mock(Resources::class.java).also { + doReturn(0).`when`(it).getInteger(R.integer.config_networkAvoidBadWifi) + } + private val telephonyManager = mock(TelephonyManager::class.java) + private val subscriptionManager = mock(SubscriptionManager::class.java).also { + doReturn(null).`when`(it).getActiveSubscriptionInfo(anyInt()) + } + private val resolver = MockContentResolver().apply { + addProvider(Settings.AUTHORITY, FakeSettingsProvider()) } + private val context = mock(Context::class.java).also { + doReturn(Context.TELEPHONY_SERVICE).`when`(it) + .getSystemServiceName(TelephonyManager::class.java) + doReturn(telephonyManager).`when`(it).getSystemService(Context.TELEPHONY_SERVICE) + doReturn(subscriptionManager).`when`(it) + .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) + doReturn(resolver).`when`(it).contentResolver + doReturn(resources).`when`(it).resources + doReturn(it).`when`(it).createConfigurationContext(any()) + Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "1") + } + private val tracker = MultinetworkPolicyTracker(context, null /* handler */) + + private fun assertMultipathPreference(preference: Int) { + Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE, + preference.toString()) + tracker.updateMeteredMultipathPreference() + assertEquals(preference, tracker.meteredMultipathPreference) + } + + @Test + fun testUpdateMeteredMultipathPreference() { + assertMultipathPreference(MULTIPATH_PREFERENCE_HANDOVER) + assertMultipathPreference(MULTIPATH_PREFERENCE_RELIABILITY) + assertMultipathPreference(MULTIPATH_PREFERENCE_PERFORMANCE) + } + + @Test + fun testUpdateAvoidBadWifi() { + Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0") + assertTrue(tracker.updateAvoidBadWifi()) + assertFalse(tracker.avoidBadWifi) + + doReturn(1).`when`(resources).getInteger(R.integer.config_networkAvoidBadWifi) + assertTrue(tracker.updateAvoidBadWifi()) + assertTrue(tracker.avoidBadWifi) + } + + @Test + fun testOnActiveDataSubscriptionIdChanged() { + val testSubId = 1000 + val subscriptionInfo = SubscriptionInfo(testSubId, ""/* iccId */, 1/* iccId */, + "TMO"/* displayName */, "TMO"/* carrierName */, 1/* nameSource */, 1/* iconTint */, + "123"/* number */, 1/* roaming */, null/* icon */, "310"/* mcc */, "210"/* mnc */, + ""/* countryIso */, false/* isEmbedded */, null/* nativeAccessRules */, + "1"/* cardString */) + doReturn(subscriptionInfo).`when`(subscriptionManager).getActiveSubscriptionInfo(testSubId) + + // Modify avoidBadWifi and meteredMultipathPreference settings value and local variables in + // MultinetworkPolicyTracker should be also updated after subId changed. + Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0") + Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE, + MULTIPATH_PREFERENCE_PERFORMANCE.toString()) + + val listenerCaptor = ArgumentCaptor.forClass( + ActiveDataSubscriptionIdChangedListener::class.java) + verify(telephonyManager, times(1)) + .registerPhoneStateListener(any(), listenerCaptor.capture()) + val listener = listenerCaptor.value + listener.onActiveDataSubscriptionIdChanged(testSubId) + + // Check it get resource value with test sub id. + verify(subscriptionManager, times(1)).getActiveSubscriptionInfo(testSubId) + verify(context).createConfigurationContext(argThat { it.mcc == 310 && it.mnc == 210 }) + + // Check if avoidBadWifi and meteredMultipathPreference values have been updated. + assertFalse(tracker.avoidBadWifi) + assertEquals(MULTIPATH_PREFERENCE_PERFORMANCE, tracker.meteredMultipathPreference) + } +} diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 13584109b093..1cfc3f9f9e5c 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -53,6 +53,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; import static android.net.NetworkCapabilities.NET_CAPABILITY_IA; @@ -84,6 +85,11 @@ import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED; import static android.net.NetworkPolicyManager.RULE_NONE; import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; +import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; +import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; +import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; +import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; +import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED; import static android.net.RouteInfo.RTN_UNREACHABLE; import static android.os.Process.INVALID_UID; import static android.system.OsConstants.IPPROTO_TCP; @@ -233,6 +239,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.SystemClock; +import android.os.SystemConfigManager; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -355,6 +362,7 @@ public class ConnectivityServiceTest { private static final long TIMESTAMP = 1234L; private static final int NET_ID = 110; + private static final int OEM_PREF_ANY_NET_ID = -1; // Set a non-zero value to verify the flow to set tcp init rwnd value. private static final int TEST_TCP_INIT_RWND = 60; @@ -424,7 +432,7 @@ public class ConnectivityServiceTest { @Mock EthernetManager mEthernetManager; @Mock NetworkPolicyManager mNetworkPolicyManager; @Mock KeyStore mKeyStore; - @Mock IOnSetOemNetworkPreferenceListener mOnSetOemNetworkPreferenceListener; + @Mock SystemConfigManager mSystemConfigManager; private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor = ArgumentCaptor.forClass(ResolverParamsParcel.class); @@ -521,6 +529,7 @@ public class ConnectivityServiceTest { if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager; if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager; + if (Context.SYSTEM_CONFIG_SERVICE.equals(name)) return mSystemConfigManager; return super.getSystemService(name); } @@ -994,10 +1003,12 @@ public class ConnectivityServiceTest { // Used to collect the networks requests managed by this factory. This is a duplicate of // the internal information stored in the NetworkFactory (which is private). private SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>(); + private final HandlerThread mHandlerSendingRequests; public MockNetworkFactory(Looper looper, Context context, String logTag, - NetworkCapabilities filter) { + NetworkCapabilities filter, HandlerThread threadSendingRequests) { super(looper, context, logTag, filter); + mHandlerSendingRequests = threadSendingRequests; } public int getMyRequestCount() { @@ -1051,7 +1062,8 @@ public class ConnectivityServiceTest { public void terminate() { super.terminate(); // Make sure there are no remaining requests unaccounted for. - assertNull(mRequestHistory.poll(TIMEOUT_MS, r -> true)); + HandlerUtils.waitForIdle(mHandlerSendingRequests, TIMEOUT_MS); + assertNull(mRequestHistory.poll(0, r -> true)); } // Trigger releasing the request as unfulfillable @@ -1424,6 +1436,7 @@ public class ConnectivityServiceTest { applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any())) .thenReturn(applicationInfo); + when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]); // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not. // http://b/25897652 . @@ -1609,10 +1622,13 @@ public class ConnectivityServiceTest { } switch (transport) { case TRANSPORT_WIFI: - assertEquals(mCm.getActiveNetwork(), mWiFiNetworkAgent.getNetwork()); + assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); break; case TRANSPORT_CELLULAR: - assertEquals(mCm.getActiveNetwork(), mCellNetworkAgent.getNetwork()); + assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); + break; + case TRANSPORT_ETHERNET: + assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork()); break; default: break; @@ -1621,6 +1637,7 @@ public class ConnectivityServiceTest { assertNotNull(mCm.getNetworkInfo(mCm.getActiveNetwork())); assertEquals(transportToLegacyType(transport), mCm.getNetworkInfo(mCm.getActiveNetwork()).getType()); + assertNotNull(mCm.getActiveNetworkInfoForUid(Process.myUid())); // Test getNetworkCapabilities(Network) assertNotNull(mCm.getNetworkCapabilities(mCm.getActiveNetwork())); assertTrue(mCm.getNetworkCapabilities(mCm.getActiveNetwork()).hasTransport(transport)); @@ -2158,6 +2175,24 @@ public class ConnectivityServiceTest { } } + static void expectOnLost(TestNetworkAgentWrapper network, TestNetworkCallback ... callbacks) { + for (TestNetworkCallback c : callbacks) { + c.expectCallback(CallbackEntry.LOST, network); + } + } + + static void expectAvailableCallbacksUnvalidatedWithSpecifier(TestNetworkAgentWrapper network, + NetworkSpecifier specifier, TestNetworkCallback ... callbacks) { + for (TestNetworkCallback c : callbacks) { + c.expectCallback(CallbackEntry.AVAILABLE, network); + c.expectCapabilitiesThat(network, (nc) -> + !nc.hasCapability(NET_CAPABILITY_VALIDATED) + && Objects.equals(specifier, nc.getNetworkSpecifier())); + c.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, network); + c.expectCallback(CallbackEntry.BLOCKED_STATUS, network); + } + } + @Test public void testStateChangeNetworkCallbacks() throws Exception { final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); @@ -2753,7 +2788,8 @@ public class ConnectivityServiceTest { if (capability == NET_CAPABILITY_CBS || capability == NET_CAPABILITY_DUN || capability == NET_CAPABILITY_EIMS || capability == NET_CAPABILITY_FOTA || capability == NET_CAPABILITY_IA || capability == NET_CAPABILITY_IMS || - capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP) { + capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP + || capability == NET_CAPABILITY_ENTERPRISE) { assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); } else { assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); @@ -2761,10 +2797,14 @@ public class ConnectivityServiceTest { NetworkCapabilities filter = new NetworkCapabilities(); filter.addCapability(capability); + // Add NOT_VCN_MANAGED capability into filter unconditionally since some request will add + // NOT_VCN_MANAGED automatically but not for NetworkCapabilities, + // see {@code NetworkCapabilities#deduceNotVcnManagedCapability} for more details. + filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests"); handlerThread.start(); final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), - mServiceContext, "testFactory", filter); + mServiceContext, "testFactory", filter, mCsHandlerThread); testFactory.setScoreFilter(40); ConditionVariable cv = testFactory.getNetworkStartedCV(); testFactory.register(); @@ -2852,6 +2892,7 @@ public class ConnectivityServiceTest { tryNetworkFactoryRequests(NET_CAPABILITY_IA); tryNetworkFactoryRequests(NET_CAPABILITY_RCS); tryNetworkFactoryRequests(NET_CAPABILITY_XCAP); + tryNetworkFactoryRequests(NET_CAPABILITY_ENTERPRISE); tryNetworkFactoryRequests(NET_CAPABILITY_EIMS); tryNetworkFactoryRequests(NET_CAPABILITY_NOT_METERED); tryNetworkFactoryRequests(NET_CAPABILITY_INTERNET); @@ -2872,7 +2913,7 @@ public class ConnectivityServiceTest { // does not crash. for (int i = 0; i < 100; i++) { final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), - mServiceContext, "testFactory", filter); + mServiceContext, "testFactory", filter, mCsHandlerThread); // Register the factory and don't be surprised when the default request arrives. testFactory.register(); testFactory.expectRequestAdd(); @@ -3521,11 +3562,9 @@ public class ConnectivityServiceTest { /** * Verify request matching behavior with network specifiers. * - * Note: this test is somewhat problematic since it involves removing capabilities from - * agents - i.e. agents rejecting requests which they previously accepted. This is flagged - * as a WTF bug in - * {@link ConnectivityService#mixInCapabilities(NetworkAgentInfo, NetworkCapabilities)} but - * does work. + * This test does not check updating the specifier on a live network because the specifier is + * immutable and this triggers a WTF in + * {@link ConnectivityService#mixInCapabilities(NetworkAgentInfo, NetworkCapabilities)}. */ @Test public void testNetworkSpecifier() throws Exception { @@ -3610,60 +3649,49 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - cEmpty1.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - cEmpty2.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - cEmpty3.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - cEmpty4.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, null /* specifier */, + cEmpty1, cEmpty2, cEmpty3, cEmpty4); assertNoCallbacks(cFoo, cBar); + mWiFiNetworkAgent.disconnect(); + expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.setNetworkSpecifier(nsFoo); - cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - for (TestNetworkCallback c: emptyCallbacks) { - c.expectCapabilitiesThat(mWiFiNetworkAgent, - (caps) -> caps.getNetworkSpecifier().equals(nsFoo)); - } - cFoo.expectCapabilitiesThat(mWiFiNetworkAgent, - (caps) -> caps.getNetworkSpecifier().equals(nsFoo)); + mWiFiNetworkAgent.connect(false); + expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, nsFoo, + cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo); + cBar.assertNoCallback(); assertEquals(nsFoo, mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier()); - cFoo.assertNoCallback(); + assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo); + mWiFiNetworkAgent.disconnect(); + expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.setNetworkSpecifier(nsBar); - cFoo.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - cBar.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - for (TestNetworkCallback c: emptyCallbacks) { - c.expectCapabilitiesThat(mWiFiNetworkAgent, - (caps) -> caps.getNetworkSpecifier().equals(nsBar)); - } - cBar.expectCapabilitiesThat(mWiFiNetworkAgent, - (caps) -> caps.getNetworkSpecifier().equals(nsBar)); + mWiFiNetworkAgent.connect(false); + expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, nsBar, + cEmpty1, cEmpty2, cEmpty3, cEmpty4, cBar); + cFoo.assertNoCallback(); assertEquals(nsBar, mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier()); - cBar.assertNoCallback(); + mWiFiNetworkAgent.disconnect(); + expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4, cBar); + cFoo.assertNoCallback(); + + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.setNetworkSpecifier(new ConfidentialMatchAllNetworkSpecifier()); - cFoo.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); - for (TestNetworkCallback c : emptyCallbacks) { - c.expectCapabilitiesThat(mWiFiNetworkAgent, - (caps) -> caps.getNetworkSpecifier() == null); - } - cFoo.expectCapabilitiesThat(mWiFiNetworkAgent, - (caps) -> caps.getNetworkSpecifier() == null); - cBar.expectCapabilitiesThat(mWiFiNetworkAgent, - (caps) -> caps.getNetworkSpecifier() == null); + mWiFiNetworkAgent.connect(false); + expectAvailableCallbacksUnvalidatedWithSpecifier(mWiFiNetworkAgent, null /* specifier */, + cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar); assertNull( mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).getNetworkSpecifier()); - cFoo.assertNoCallback(); - cBar.assertNoCallback(); - - mWiFiNetworkAgent.setNetworkSpecifier(null); - cFoo.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - cBar.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); - for (TestNetworkCallback c: emptyCallbacks) { - c.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mWiFiNetworkAgent); - } - assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar); + mWiFiNetworkAgent.disconnect(); + expectOnLost(mWiFiNetworkAgent, cEmpty1, cEmpty2, cEmpty3, cEmpty4, cFoo, cBar); } /** @@ -4120,9 +4148,10 @@ public class ConnectivityServiceTest { handlerThread.start(); NetworkCapabilities filter = new NetworkCapabilities() .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) .addCapability(NET_CAPABILITY_INTERNET); final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), - mServiceContext, "testFactory", filter); + mServiceContext, "testFactory", filter, mCsHandlerThread); testFactory.setScoreFilter(40); // Register the factory and expect it to start looking for a network. @@ -4170,6 +4199,7 @@ public class ConnectivityServiceTest { // ... and cell data to be torn down after nascent network timeout. cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent, mService.mNascentDelayMs + TEST_CALLBACK_TIMEOUT_MS); + waitForIdle(); assertLength(1, mCm.getAllNetworks()); } finally { testFactory.terminate(); @@ -4469,7 +4499,7 @@ public class ConnectivityServiceTest { .addTransportType(TRANSPORT_WIFI) .addCapability(NET_CAPABILITY_INTERNET); final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), - mServiceContext, "testFactory", filter); + mServiceContext, "testFactory", filter, mCsHandlerThread); testFactory.setScoreFilter(40); // Register the factory and expect it to receive the default request. @@ -6023,6 +6053,7 @@ public class ConnectivityServiceTest { .addTransportType(TRANSPORT_CELLULAR) .addCapability(NET_CAPABILITY_INTERNET) .addCapability(NET_CAPABILITY_NOT_CONGESTED) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) .setLinkDownstreamBandwidthKbps(10); final NetworkCapabilities wifiNc = new NetworkCapabilities() .addTransportType(TRANSPORT_WIFI) @@ -6031,6 +6062,7 @@ public class ConnectivityServiceTest { .addCapability(NET_CAPABILITY_NOT_ROAMING) .addCapability(NET_CAPABILITY_NOT_CONGESTED) .addCapability(NET_CAPABILITY_NOT_SUSPENDED) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) .setLinkUpstreamBandwidthKbps(20); mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */); mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */); @@ -6818,6 +6850,7 @@ public class ConnectivityServiceTest { .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)); final Intent addedIntent = new Intent(ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); // Send a USER_ADDED broadcast for it. @@ -6828,7 +6861,7 @@ public class ConnectivityServiceTest { callback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.getUids().size() == 2 && caps.getUids().contains(new UidRange(uid, uid)) - && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER)) + && caps.getUids().contains(createUidRange(RESTRICTED_USER)) && caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_WIFI)); @@ -6838,12 +6871,13 @@ public class ConnectivityServiceTest { callback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.getUids().size() == 2 && caps.getUids().contains(new UidRange(uid, uid)) - && caps.getUids().contains(UidRange.createForUser(RESTRICTED_USER)) + && caps.getUids().contains(createUidRange(RESTRICTED_USER)) && caps.hasTransport(TRANSPORT_VPN) && !caps.hasTransport(TRANSPORT_WIFI)); // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. final Intent removedIntent = new Intent(ACTION_USER_REMOVED); + removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); processBroadcastForVpn(removedIntent); @@ -6901,6 +6935,7 @@ public class ConnectivityServiceTest { RESTRICTED_USER_INFO)); // TODO: check that VPN app within restricted profile still has access, etc. final Intent addedIntent = new Intent(ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); processBroadcastForVpn(addedIntent); assertNull(mCm.getActiveNetworkForUid(uid)); @@ -6911,6 +6946,7 @@ public class ConnectivityServiceTest { // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. final Intent removedIntent = new Intent(ACTION_USER_REMOVED); + removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER)); removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER); processBroadcastForVpn(removedIntent); assertNull(mCm.getActiveNetworkForUid(uid)); @@ -7469,7 +7505,7 @@ public class ConnectivityServiceTest { assertNotNull(underlying); mMockVpn.setVpnType(VpnManager.TYPE_VPN_LEGACY); // The legacy lockdown VPN only supports userId 0. - final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + final Set<UidRange> ranges = Collections.singleton(createUidRange(PRIMARY_USER)); mMockVpn.registerAgent(ranges); mMockVpn.setUnderlyingNetworks(new Network[]{underlying}); mMockVpn.connect(true); @@ -7505,6 +7541,7 @@ public class ConnectivityServiceTest { // Send a USER_UNLOCKED broadcast so CS starts LockdownVpnTracker. final int userId = UserHandle.getUserId(Process.myUid()); final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED); + addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); processBroadcastForVpn(addedIntent); @@ -7718,19 +7755,13 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.removeCapability(testCap); callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent); callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent); - // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has - // it. - if (testCap == NET_CAPABILITY_TRUSTED) { - verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); - reset(mMockNetd); - } + verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId)); + reset(mMockNetd); mCellNetworkAgent.removeCapability(testCap); callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); callbackWithoutCap.assertNoCallback(); - if (testCap == NET_CAPABILITY_TRUSTED) { - verify(mMockNetd).networkClearDefault(); - } + verify(mMockNetd).networkClearDefault(); mCm.unregisterNetworkCallback(callbackWithCap); mCm.unregisterNetworkCallback(callbackWithoutCap); @@ -8205,8 +8236,8 @@ public class ConnectivityServiceTest { reset(mNetworkManagementService); mCellNetworkAgent.connect(true); networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(), - eq(NetworkCapabilities.TRANSPORT_CELLULAR)); + verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); final LinkProperties wifiLp = new LinkProperties(); @@ -8214,25 +8245,27 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.sendLinkProperties(wifiLp); // Network switch - reset(mNetworkManagementService); mWiFiNetworkAgent.connect(true); networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); - verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(), - eq(NetworkCapabilities.TRANSPORT_WIFI)); - verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME)); + verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_WIFI))); + verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); // Disconnect wifi and switch back to cell - reset(mNetworkManagementService); + reset(mMockNetd); mWiFiNetworkAgent.disconnect(); networkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); assertNoCallbacks(networkCallback); - verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME)); - verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(), - eq(NetworkCapabilities.TRANSPORT_CELLULAR)); + verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(WIFI_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_WIFI))); + verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); // reconnect wifi + reset(mMockNetd); mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); wifiLp.setInterfaceName(WIFI_IFNAME); mWiFiNetworkAgent.sendLinkProperties(wifiLp); @@ -8240,9 +8273,12 @@ public class ConnectivityServiceTest { networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); networkCallback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent); networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_WIFI))); + verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); // Disconnect cell - reset(mNetworkManagementService); reset(mMockNetd); mCellNetworkAgent.disconnect(); networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); @@ -8250,17 +8286,18 @@ public class ConnectivityServiceTest { // sent as network being switched. Ensure rule removal for cell will not be triggered // unexpectedly before network being removed. waitForIdle(); - verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME)); + verify(mMockNetd, times(0)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); verify(mMockNetd, times(1)).networkDestroy(eq(mCellNetworkAgent.getNetwork().netId)); verify(mMockDnsResolver, times(1)) .destroyNetworkCache(eq(mCellNetworkAgent.getNetwork().netId)); // Disconnect wifi ExpectedBroadcast b = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED); - reset(mNetworkManagementService); mWiFiNetworkAgent.disconnect(); b.expectBroadcast(); - verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME)); + verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(WIFI_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_WIFI))); // Clean up mCm.unregisterNetworkCallback(networkCallback); @@ -8381,7 +8418,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER)); mMockVpn.establish(lp, VPN_UID, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); @@ -8409,7 +8446,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER)); mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); @@ -8425,7 +8462,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun0")); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER)); mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID); @@ -8440,7 +8477,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER)); mMockVpn.establish(lp, VPN_UID, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, VPN_UID); @@ -8492,7 +8529,7 @@ public class ConnectivityServiceTest { lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. - final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER); + final UidRange vpnRange = createUidRange(PRIMARY_USER); final Set<UidRange> vpnRanges = Collections.singleton(vpnRange); mMockVpn.establish(lp, VPN_UID, vpnRanges); assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); @@ -8691,7 +8728,7 @@ public class ConnectivityServiceTest { private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType) throws Exception { - final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(PRIMARY_USER)); + final Set<UidRange> vpnRange = Collections.singleton(createUidRange(PRIMARY_USER)); mMockVpn.setVpnType(vpnType); mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange); assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid); @@ -9250,7 +9287,7 @@ public class ConnectivityServiceTest { lp.setInterfaceName("tun0"); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); - final UidRange vpnRange = UidRange.createForUser(PRIMARY_USER); + final UidRange vpnRange = createUidRange(PRIMARY_USER); Set<UidRange> vpnRanges = Collections.singleton(vpnRange); mMockVpn.establish(lp, VPN_UID, vpnRanges); assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID); @@ -9431,7 +9468,7 @@ public class ConnectivityServiceTest { } private void mockGetApplicationInfo(@NonNull final String packageName, @NonNull final int uid) - throws PackageManager.NameNotFoundException { + throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.uid = uid; when(mPackageManager.getApplicationInfo(eq(packageName), anyInt())) @@ -9451,7 +9488,7 @@ public class ConnectivityServiceTest { private OemNetworkPreferences createDefaultOemNetworkPreferences( @OemNetworkPreferences.OemNetworkPreference final int preference) - throws PackageManager.NameNotFoundException { + throws Exception { // Arrange PackageManager mocks mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID); @@ -9465,7 +9502,7 @@ public class ConnectivityServiceTest { public void testOemNetworkRequestFactoryPreferenceUninitializedThrowsError() throws PackageManager.NameNotFoundException { @OemNetworkPreferences.OemNetworkPreference final int prefToTest = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED; + OEM_NETWORK_PREFERENCE_UNINITIALIZED; // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() assertThrows(IllegalArgumentException.class, @@ -9476,13 +9513,13 @@ public class ConnectivityServiceTest { @Test public void testOemNetworkRequestFactoryPreferenceOemPaid() - throws PackageManager.NameNotFoundException { + throws Exception { // Expectations final int expectedNumOfNris = 1; final int expectedNumOfRequests = 3; @OemNetworkPreferences.OemNetworkPreference final int prefToTest = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; + OEM_NETWORK_PREFERENCE_OEM_PAID; // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() final ArraySet<ConnectivityService.NetworkRequestInfo> nris = @@ -9505,13 +9542,13 @@ public class ConnectivityServiceTest { @Test public void testOemNetworkRequestFactoryPreferenceOemPaidNoFallback() - throws PackageManager.NameNotFoundException { + throws Exception { // Expectations final int expectedNumOfNris = 1; final int expectedNumOfRequests = 2; @OemNetworkPreferences.OemNetworkPreference final int prefToTest = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; + OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() final ArraySet<ConnectivityService.NetworkRequestInfo> nris = @@ -9531,13 +9568,13 @@ public class ConnectivityServiceTest { @Test public void testOemNetworkRequestFactoryPreferenceOemPaidOnly() - throws PackageManager.NameNotFoundException { + throws Exception { // Expectations final int expectedNumOfNris = 1; final int expectedNumOfRequests = 1; @OemNetworkPreferences.OemNetworkPreference final int prefToTest = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() final ArraySet<ConnectivityService.NetworkRequestInfo> nris = @@ -9554,13 +9591,13 @@ public class ConnectivityServiceTest { @Test public void testOemNetworkRequestFactoryPreferenceOemPrivateOnly() - throws PackageManager.NameNotFoundException { + throws Exception { // Expectations final int expectedNumOfNris = 1; final int expectedNumOfRequests = 1; @OemNetworkPreferences.OemNetworkPreference final int prefToTest = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; + OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; // Act on OemNetworkRequestFactory.createNrisFromOemNetworkPreferences() final ArraySet<ConnectivityService.NetworkRequestInfo> nris = @@ -9578,7 +9615,7 @@ public class ConnectivityServiceTest { @Test public void testOemNetworkRequestFactoryCreatesCorrectNumOfNris() - throws PackageManager.NameNotFoundException { + throws Exception { // Expectations final int expectedNumOfNris = 2; @@ -9588,8 +9625,8 @@ public class ConnectivityServiceTest { mockGetApplicationInfo(testPackageName2, TEST_PACKAGE_UID); // Build OemNetworkPreferences object - final int testOemPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; - final int testOemPref2 = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; + final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID; + final int testOemPref2 = OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref) .addNetworkPreference(testPackageName2, testOemPref2) @@ -9605,7 +9642,7 @@ public class ConnectivityServiceTest { @Test public void testOemNetworkRequestFactoryCorrectlySetsUids() - throws PackageManager.NameNotFoundException { + throws Exception { // Arrange PackageManager mocks final String testPackageName2 = "com.google.apps.dialer"; final int testPackageNameUid2 = 456; @@ -9613,8 +9650,8 @@ public class ConnectivityServiceTest { mockGetApplicationInfo(testPackageName2, testPackageNameUid2); // Build OemNetworkPreferences object - final int testOemPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; - final int testOemPref2 = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; + final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID; + final int testOemPref2 = OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref) .addNetworkPreference(testPackageName2, testOemPref2) @@ -9636,7 +9673,7 @@ public class ConnectivityServiceTest { @Test public void testOemNetworkRequestFactoryAddsPackagesToCorrectPreference() - throws PackageManager.NameNotFoundException { + throws Exception { // Expectations final int expectedNumOfNris = 1; final int expectedNumOfAppUids = 2; @@ -9648,7 +9685,7 @@ public class ConnectivityServiceTest { mockGetApplicationInfo(testPackageName2, testPackageNameUid2); // Build OemNetworkPreferences object - final int testOemPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID; + final int testOemPref = OEM_NETWORK_PREFERENCE_OEM_PAID; final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() .addNetworkPreference(TEST_PACKAGE_NAME, testOemPref) .addNetworkPreference(testPackageName2, testOemPref) @@ -9666,8 +9703,6 @@ public class ConnectivityServiceTest { @Test public void testSetOemNetworkPreferenceNullListenerAndPrefParamThrowsNpe() { mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true); - @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; // Act on ConnectivityService.setOemNetworkPreference() assertThrows(NullPointerException.class, @@ -9678,15 +9713,641 @@ public class ConnectivityServiceTest { @Test public void testSetOemNetworkPreferenceFailsForNonAutomotive() - throws PackageManager.NameNotFoundException, RemoteException { + throws Exception { mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, false); @OemNetworkPreferences.OemNetworkPreference final int networkPref = - OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; + OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; // Act on ConnectivityService.setOemNetworkPreference() assertThrows(UnsupportedOperationException.class, () -> mService.setOemNetworkPreference( createDefaultOemNetworkPreferences(networkPref), - mOnSetOemNetworkPreferenceListener)); + new TestOemListenerCallback())); + } + + private void setOemNetworkPreferenceAgentConnected(final int transportType, + final boolean connectAgent) throws Exception { + switch(transportType) { + // Corresponds to a metered cellular network. Will be used for the default network. + case TRANSPORT_CELLULAR: + if (!connectAgent) { + mCellNetworkAgent.disconnect(); + break; + } + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED); + mCellNetworkAgent.connect(true); + break; + // Corresponds to a restricted ethernet network with OEM_PAID/OEM_PRIVATE. + case TRANSPORT_ETHERNET: + if (!connectAgent) { + stopOemManagedNetwork(); + break; + } + startOemManagedNetwork(true); + break; + // Corresponds to unmetered Wi-Fi. + case TRANSPORT_WIFI: + if (!connectAgent) { + mWiFiNetworkAgent.disconnect(); + break; + } + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mWiFiNetworkAgent.connect(true); + break; + default: + throw new AssertionError("Unsupported transport type passed in."); + + } + waitForIdle(); + } + + private void startOemManagedNetwork(final boolean isOemPaid) throws Exception { + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); + mEthernetNetworkAgent.addCapability( + isOemPaid ? NET_CAPABILITY_OEM_PAID : NET_CAPABILITY_OEM_PRIVATE); + mEthernetNetworkAgent.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + mEthernetNetworkAgent.connect(true); + } + + private void stopOemManagedNetwork() { + mEthernetNetworkAgent.disconnect(); + waitForIdle(); + } + + private void verifyMultipleDefaultNetworksTracksCorrectly( + final int expectedOemRequestsSize, + @NonNull final Network expectedDefaultNetwork, + @NonNull final Network expectedPerAppNetwork) { + // The current test setup assumes two tracked default network requests; one for the default + // network and the other for the OEM network preference being tested. This will be validated + // each time to confirm it doesn't change under test. + final int expectedDefaultNetworkRequestsSize = 2; + assertEquals(expectedDefaultNetworkRequestsSize, mService.mDefaultNetworkRequests.size()); + for (final ConnectivityService.NetworkRequestInfo defaultRequest + : mService.mDefaultNetworkRequests) { + final Network defaultNetwork = defaultRequest.getSatisfier() == null + ? null : defaultRequest.getSatisfier().network(); + // If this is the default request. + if (defaultRequest == mService.mDefaultRequest) { + assertEquals( + expectedDefaultNetwork, + defaultNetwork); + // Make sure this value doesn't change. + assertEquals(1, defaultRequest.mRequests.size()); + continue; + } + assertEquals(expectedPerAppNetwork, defaultNetwork); + assertEquals(expectedOemRequestsSize, defaultRequest.mRequests.size()); + } + } + + private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest( + @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup) + throws Exception { + final int testPackageNameUid = 123; + final String testPackageName = "per.app.defaults.package"; + setupMultipleDefaultNetworksForOemNetworkPreferenceTest( + networkPrefToSetup, testPackageNameUid, testPackageName); + } + + private void setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest( + @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup) + throws Exception { + final int testPackageNameUid = Process.myUid(); + final String testPackageName = "per.app.defaults.package"; + setupMultipleDefaultNetworksForOemNetworkPreferenceTest( + networkPrefToSetup, testPackageNameUid, testPackageName); + } + + private void setupMultipleDefaultNetworksForOemNetworkPreferenceTest( + @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup, + final int testPackageUid, @NonNull final String testPackageName) throws Exception { + // Only the default request should be included at start. + assertEquals(1, mService.mDefaultNetworkRequests.size()); + + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUid(testPackageUid)); + setupSetOemNetworkPreferenceForPreferenceTest( + networkPrefToSetup, uidRanges, testPackageName); + } + + private void setupSetOemNetworkPreferenceForPreferenceTest( + @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup, + @NonNull final UidRangeParcel[] uidRanges, + @NonNull final String testPackageName) + throws Exception { + mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true); + + // These tests work off a single UID therefore using 'start' is valid. + mockGetApplicationInfo(testPackageName, uidRanges[0].start); + + // Build OemNetworkPreferences object + final OemNetworkPreferences pref = new OemNetworkPreferences.Builder() + .addNetworkPreference(testPackageName, networkPrefToSetup) + .build(); + + // Act on ConnectivityService.setOemNetworkPreference() + final TestOemListenerCallback mOnSetOemNetworkPreferenceTestListener = + new TestOemListenerCallback(); + mService.setOemNetworkPreference(pref, mOnSetOemNetworkPreferenceTestListener); + + // Verify call returned successfully + mOnSetOemNetworkPreferenceTestListener.expectOnComplete(); + } + + private static class TestOemListenerCallback implements IOnSetOemNetworkPreferenceListener { + final CompletableFuture<Object> mDone = new CompletableFuture<>(); + + @Override + public void onComplete() { + mDone.complete(new Object()); + } + + void expectOnComplete() throws Exception { + try { + mDone.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + fail("Expected onComplete() not received after " + TIMEOUT_MS + " ms"); + } + } + + @Override + public IBinder asBinder() { + return null; + } + } + + @Test + public void testMultiDefaultGetActiveNetworkIsCorrect() throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + final int expectedOemPrefRequestSize = 1; + + // Setup the test process to use networkPref for their default network. + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + // The active network for the default should be null at this point as this is a retricted + // network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + // Verify that the active network is correct + verifyActiveNetwork(TRANSPORT_ETHERNET); + } + + @Test + public void testMultiDefaultIsActiveNetworkMeteredIsCorrect() throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + final int expectedOemPrefRequestSize = 1; + + // Setup the test process to use networkPref for their default network. + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + + // Returns true by default when no network is available. + assertTrue(mCm.isActiveNetworkMetered()); + + // Connect to an unmetered restricted network that will only be available to the OEM pref. + mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET); + mEthernetNetworkAgent.addCapability(NET_CAPABILITY_OEM_PAID); + mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED); + mEthernetNetworkAgent.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + mEthernetNetworkAgent.connect(true); + waitForIdle(); + + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + assertFalse(mCm.isActiveNetworkMetered()); + } + + @Test + public void testPerAppDefaultRegisterDefaultNetworkCallback() throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + final int expectedOemPrefRequestSize = 1; + final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); + + // Register the default network callback before the pref is already set. This means that + // the policy will be applied to the callback on setOemNetworkPreference(). + mCm.registerDefaultNetworkCallback(defaultNetworkCallback); + defaultNetworkCallback.assertNoCallback(); + + // Setup the test process to use networkPref for their default network. + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + // The active nai for the default is null at this point as this is a restricted network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + // At this point with a restricted network used, the available callback should trigger + defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); + assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Now bring down the default network which should trigger a LOST callback. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + + // At this point, with no network is available, the lost callback should trigger + defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + + // Confirm we can unregister without issues. + mCm.unregisterNetworkCallback(defaultNetworkCallback); + } + + @Test + public void testPerAppDefaultRegisterDefaultNetworkCallbackAfterPrefSet() throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + final int expectedOemPrefRequestSize = 1; + final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); + + // Setup the test process to use networkPref for their default network. + setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); + + // Register the default network callback after the pref is already set. This means that + // the policy will be applied to the callback on requestNetwork(). + mCm.registerDefaultNetworkCallback(defaultNetworkCallback); + defaultNetworkCallback.assertNoCallback(); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + // The active nai for the default is null at this point as this is a restricted network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + // At this point with a restricted network used, the available callback should trigger + defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); + assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // Now bring down the default network which should trigger a LOST callback. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + + // At this point, with no network is available, the lost callback should trigger + defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + + // Confirm we can unregister without issues. + mCm.unregisterNetworkCallback(defaultNetworkCallback); + } + + @Test + public void testPerAppDefaultRegisterDefaultNetworkCallbackDoesNotFire() throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + final int expectedOemPrefRequestSize = 1; + final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); + final int userId = UserHandle.getUserId(Process.myUid()); + + mCm.registerDefaultNetworkCallback(defaultNetworkCallback); + defaultNetworkCallback.assertNoCallback(); + + // Setup a process different than the test process to use the default network. This means + // that the defaultNetworkCallback won't be tracked by the per-app policy. + setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(networkPref); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + // The active nai for the default is null at this point as this is a restricted network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + null, + mEthernetNetworkAgent.getNetwork()); + + // As this callback does not have access to the OEM_PAID network, it will not fire. + defaultNetworkCallback.assertNoCallback(); + assertDefaultNetworkCapabilities(userId /* no networks */); + + // Bring up unrestricted cellular. This should now satisfy the default network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, + mCellNetworkAgent.getNetwork(), + mEthernetNetworkAgent.getNetwork()); + + // At this point with an unrestricted network used, the available callback should trigger + defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), + mCellNetworkAgent.getNetwork()); + assertDefaultNetworkCapabilities(userId, mCellNetworkAgent); + + // Now bring down the per-app network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + + // Since the callback didn't use the per-app network, no callback should fire. + defaultNetworkCallback.assertNoCallback(); + + // Now bring down the default network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + + // As this callback was tracking the default, this should now trigger. + defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + + // Confirm we can unregister without issues. + mCm.unregisterNetworkCallback(defaultNetworkCallback); + } + + private void verifySetOemNetworkPreferenceForPreference( + @NonNull final UidRangeParcel[] uidRanges, + final int addUidRangesNetId, + final int addUidRangesTimes, + final int removeUidRangesNetId, + final int removeUidRangesTimes, + final boolean shouldDestroyNetwork) throws RemoteException { + final boolean useAnyIdForAdd = OEM_PREF_ANY_NET_ID == addUidRangesNetId; + final boolean useAnyIdForRemove = OEM_PREF_ANY_NET_ID == removeUidRangesNetId; + + // Validate netd. + verify(mMockNetd, times(addUidRangesTimes)) + .networkAddUidRanges( + (useAnyIdForAdd ? anyInt() : eq(addUidRangesNetId)), eq(uidRanges)); + verify(mMockNetd, times(removeUidRangesTimes)) + .networkRemoveUidRanges( + (useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId)), eq(uidRanges)); + if (shouldDestroyNetwork) { + verify(mMockNetd, times(1)) + .networkDestroy((useAnyIdForRemove ? anyInt() : eq(removeUidRangesNetId))); + } + reset(mMockNetd); + } + + /** + * Test the tracked default requests clear previous OEM requests on setOemNetworkPreference(). + * @throws Exception + */ + @Test + public void testSetOemNetworkPreferenceClearPreviousOemValues() throws Exception { + @OemNetworkPreferences.OemNetworkPreference int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID; + final int testPackageUid = 123; + final String testPackageName = "com.google.apps.contacts"; + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUid(testPackageUid)); + + // Validate the starting requests only includes the fallback request. + assertEquals(1, mService.mDefaultNetworkRequests.size()); + + // Add an OEM default network request to track. + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, testPackageName); + + // Two requests should exist, one for the fallback and one for the pref. + assertEquals(2, mService.mDefaultNetworkRequests.size()); + + networkPref = OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, testPackageName); + + // Two requests should still exist validating the previous per-app request was replaced. + assertEquals(2, mService.mDefaultNetworkRequests.size()); + } + + /** + * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID following in order: + * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback + * @throws Exception + */ + @Test + public void testMultilayerForPreferenceOemPaidEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID; + + // Arrange PackageManager mocks + final int testPackageNameUid = 123; + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUid(testPackageNameUid)); + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); + + // Verify the starting state. No networks should be connected. + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Test lowest to highest priority requests. + // Bring up metered cellular. This will satisfy the fallback network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mWiFiNetworkAgent.getNetwork().netId, 1 /* times */, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + + // Disconnecting OEM_PAID should have no effect as it is lower in priority then unmetered. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + // netd should not be called as default networks haven't changed. + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Disconnecting unmetered should put PANS on lowest priority fallback request. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mCellNetworkAgent.getNetwork().netId, 1 /* times */, + mWiFiNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + + // Disconnecting the fallback network should result in no connectivity. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + mCellNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + } + + /** + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK following in order: + * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID + * @throws Exception + */ + @Test + public void testMultilayerForPreferenceOemPaidNoFallbackEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK; + + // Arrange PackageManager mocks + final int testPackageNameUid = 123; + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUid(testPackageNameUid)); + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); + + // Verify the starting state. This preference doesn't support using the fallback network + // therefore should be on the disconnected network as it has no networks to connect to. + verifySetOemNetworkPreferenceForPreference(uidRanges, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Test lowest to highest priority requests. + // Bring up metered cellular. This will satisfy the fallback network. + // This preference should not use this network as it doesn't support fallback usage. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up unmetered Wi-Fi. This will satisfy NET_CAPABILITY_NOT_METERED. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mWiFiNetworkAgent.getNetwork().netId, 1 /* times */, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + false /* shouldDestroyNetwork */); + + // Disconnecting unmetered should put PANS on OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + mWiFiNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + + // Disconnecting OEM_PAID should result in no connectivity. + // OEM_PAID_NO_FALLBACK not supporting a fallback now uses the disconnected network. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + mEthernetNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + } + + /** + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY following in order: + * NET_CAPABILITY_OEM_PAID + * This preference should only apply to OEM_PAID networks. + * @throws Exception + */ + @Test + public void testMultilayerForPreferenceOemPaidOnlyEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY; + + // Arrange PackageManager mocks + final int testPackageNameUid = 123; + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUid(testPackageNameUid)); + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); + + // Verify the starting state. This preference doesn't support using the fallback network + // therefore should be on the disconnected network as it has no networks to connect to. + verifySetOemNetworkPreferenceForPreference(uidRanges, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up metered cellular. This should not apply to this preference. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up unmetered Wi-Fi. This should not apply to this preference. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + false /* shouldDestroyNetwork */); + + // Disconnecting OEM_PAID should result in no connectivity. + setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + mEthernetNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + } + + /** + * Test network priority for OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY following in order: + * NET_CAPABILITY_OEM_PRIVATE + * This preference should only apply to OEM_PRIVATE networks. + * @throws Exception + */ + @Test + public void testMultilayerForPreferenceOemPrivateOnlyEvaluatesCorrectly() + throws Exception { + @OemNetworkPreferences.OemNetworkPreference final int networkPref = + OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY; + + // Arrange PackageManager mocks + final int testPackageNameUid = 123; + final UidRangeParcel[] uidRanges = + toUidRangeStableParcels(uidRangesForUid(testPackageNameUid)); + setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME); + + // Verify the starting state. This preference doesn't support using the fallback network + // therefore should be on the disconnected network as it has no networks to connect to. + verifySetOemNetworkPreferenceForPreference(uidRanges, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up metered cellular. This should not apply to this preference. + setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up unmetered Wi-Fi. This should not apply to this preference. + setOemNetworkPreferenceAgentConnected(TRANSPORT_WIFI, true); + verifySetOemNetworkPreferenceForPreference(uidRanges, + OEM_PREF_ANY_NET_ID, 0 /* times */, + OEM_PREF_ANY_NET_ID, 0 /* times */, + false /* shouldDestroyNetwork */); + + // Bring up ethernet with OEM_PRIVATE. This will satisfy NET_CAPABILITY_OEM_PRIVATE. + startOemManagedNetwork(false); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mEthernetNetworkAgent.getNetwork().netId, 1 /* times */, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + false /* shouldDestroyNetwork */); + + // Disconnecting OEM_PRIVATE should result in no connectivity. + stopOemManagedNetwork(); + verifySetOemNetworkPreferenceForPreference(uidRanges, + mService.mNoServiceNetwork.network.getNetId(), 1 /* times */, + mEthernetNetworkAgent.getNetwork().netId, 0 /* times */, + true /* shouldDestroyNetwork */); + } + + private UidRange createUidRange(int userId) { + return UidRange.createForUser(UserHandle.of(userId)); } } diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index c86224a71978..32c95f149979 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -16,12 +16,16 @@ package com.android.server; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.INetd.IF_STATE_DOWN; +import static android.net.INetd.IF_STATE_UP; import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; @@ -36,6 +40,7 @@ import android.content.pm.PackageManager; import android.net.ConnectivityManager; import android.net.INetd; import android.net.InetAddresses; +import android.net.InterfaceConfigurationParcel; import android.net.IpSecAlgorithm; import android.net.IpSecConfig; import android.net.IpSecManager; @@ -48,7 +53,6 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.os.Binder; -import android.os.INetworkManagementService; import android.os.ParcelFileDescriptor; import android.system.Os; import android.test.mock.MockContext; @@ -148,10 +152,17 @@ public class IpSecServiceParameterizedTest { } throw new SecurityException("Unavailable permission requested"); } + + @Override + public int checkCallingOrSelfPermission(String permission) { + if (android.Manifest.permission.NETWORK_STACK.equals(permission)) { + return PERMISSION_GRANTED; + } + throw new UnsupportedOperationException(); + } }; INetd mMockNetd; - INetworkManagementService mNetworkManager; PackageManager mMockPkgMgr; IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; IpSecService mIpSecService; @@ -181,10 +192,9 @@ public class IpSecServiceParameterizedTest { @Before public void setUp() throws Exception { mMockNetd = mock(INetd.class); - mNetworkManager = mock(INetworkManagementService.class); mMockPkgMgr = mock(PackageManager.class); mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); - mIpSecService = new IpSecService(mMockContext, mNetworkManager, mMockIpSecSrvConfig); + mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); // Injecting mock netd when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd); @@ -644,7 +654,10 @@ public class IpSecServiceParameterizedTest { } private IpSecTunnelInterfaceResponse createAndValidateTunnel( - String localAddr, String remoteAddr, String pkgName) { + String localAddr, String remoteAddr, String pkgName) throws Exception { + final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel(); + config.flags = new String[] {IF_STATE_DOWN}; + when(mMockNetd.interfaceGetCfg(anyString())).thenReturn(config); IpSecTunnelInterfaceResponse createTunnelResp = mIpSecService.createTunnelInterface( mSourceAddr, mDestinationAddr, fakeNetwork, new Binder(), pkgName); @@ -674,7 +687,8 @@ public class IpSecServiceParameterizedTest { anyInt(), anyInt(), anyInt()); - verify(mNetworkManager).setInterfaceUp(createTunnelResp.interfaceName); + verify(mMockNetd).interfaceSetCfg(argThat( + config -> Arrays.asList(config.flags).contains(IF_STATE_UP))); } @Test diff --git a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java index 788e4efe097e..22a2c94fc194 100644 --- a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java @@ -31,7 +31,6 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.os.Binder; import android.os.IBinder; -import android.os.INetworkManagementService; import android.os.RemoteException; import androidx.test.filters.SmallTest; @@ -62,8 +61,7 @@ public class IpSecServiceRefcountedResourceTest { public void setUp() throws Exception { mMockContext = mock(Context.class); mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); - mIpSecService = new IpSecService( - mMockContext, mock(INetworkManagementService.class), mMockIpSecSrvConfig); + mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); } private void assertResourceState( diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java index 536e98327e1f..f97eabf6366d 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -42,7 +42,6 @@ import android.net.IpSecManager; import android.net.IpSecSpiResponse; import android.net.IpSecUdpEncapResponse; import android.os.Binder; -import android.os.INetworkManagementService; import android.os.ParcelFileDescriptor; import android.os.Process; import android.system.ErrnoException; @@ -116,7 +115,6 @@ public class IpSecServiceTest { } Context mMockContext; - INetworkManagementService mMockNetworkManager; INetd mMockNetd; IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig; IpSecService mIpSecService; @@ -124,10 +122,9 @@ public class IpSecServiceTest { @Before public void setUp() throws Exception { mMockContext = mock(Context.class); - mMockNetworkManager = mock(INetworkManagementService.class); mMockNetd = mock(INetd.class); mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); - mIpSecService = new IpSecService(mMockContext, mMockNetworkManager, mMockIpSecSrvConfig); + mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); // Injecting mock netd when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd); @@ -135,7 +132,7 @@ public class IpSecServiceTest { @Test public void testIpSecServiceCreate() throws InterruptedException { - IpSecService ipSecSrv = IpSecService.create(mMockContext, mMockNetworkManager); + IpSecService ipSecSrv = IpSecService.create(mMockContext); assertNotNull(ipSecSrv); } @@ -608,7 +605,7 @@ public class IpSecServiceTest { public void testOpenUdpEncapSocketTagsSocket() throws Exception { IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class); IpSecService testIpSecService = new IpSecService( - mMockContext, mMockNetworkManager, mMockIpSecSrvConfig, mockTagger); + mMockContext, mMockIpSecSrvConfig, mockTagger); IpSecUdpEncapResponse udpEncapResp = testIpSecService.openUdpEncapsulationSocket(0, new Binder()); diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java index 46c408199b1a..d01dc03f5fe1 100644 --- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java @@ -51,6 +51,7 @@ import com.android.server.connectivity.NetworkNotificationManager.NotificationTy import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.AdditionalAnswers; @@ -199,6 +200,9 @@ public class NetworkNotificationManagerTest { } @Test + @Ignore + // Ignored because the code under test calls Log.wtf, which crashes the tests on eng builds. + // TODO: re-enable after fixing this (e.g., turn Log.wtf into exceptions that this test catches) public void testNoInternetNotificationsNotShownForCellular() { mManager.showNotification(100, NO_INTERNET, mCellNai, mWifiNai, null, false); mManager.showNotification(101, LOST_INTERNET, mCellNai, mWifiNai, null, false); diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index 8f5ae97bc4c5..e4e24b464838 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -61,6 +61,7 @@ import android.content.pm.PackageManagerInternal; import android.net.INetd; import android.net.UidRange; import android.os.Build; +import android.os.SystemConfigManager; import android.os.UserHandle; import android.os.UserManager; import android.util.SparseIntArray; @@ -114,6 +115,7 @@ public class PermissionMonitorTest { @Mock private PackageManagerInternal mMockPmi; @Mock private UserManager mUserManager; @Mock private PermissionMonitor.Dependencies mDeps; + @Mock private SystemConfigManager mSystemConfigManager; private PermissionMonitor mPermissionMonitor; @@ -124,6 +126,11 @@ public class PermissionMonitorTest { when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager); when(mUserManager.getUserHandles(eq(true))).thenReturn( Arrays.asList(new UserHandle[] { MOCK_USER1, MOCK_USER2 })); + when(mContext.getSystemServiceName(SystemConfigManager.class)) + .thenReturn(Context.SYSTEM_CONFIG_SERVICE); + when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE)) + .thenReturn(mSystemConfigManager); + when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]); mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps)); @@ -747,4 +754,20 @@ public class PermissionMonitorTest { GET_PERMISSIONS | MATCH_ANY_USER); assertTrue(monitor.hasPermission(systemInfo, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); } + + @Test + public void testUpdateUidPermissionsFromSystemConfig() throws Exception { + final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService); + when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(new ArrayList<>()); + when(mSystemConfigManager.getSystemPermissionUids(eq(INTERNET))) + .thenReturn(new int[]{ MOCK_UID1, MOCK_UID2 }); + when(mSystemConfigManager.getSystemPermissionUids(eq(UPDATE_DEVICE_STATS))) + .thenReturn(new int[]{ MOCK_UID2 }); + + mPermissionMonitor.startMonitoring(); + mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{ MOCK_UID1 }); + mNetdServiceMonitor.expectPermission( + INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS, + new int[]{ MOCK_UID2 }); + } } diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index cffd2d1d428f..7489a0f889dc 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -21,6 +21,8 @@ import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.content.pm.UserInfo.FLAG_PRIMARY; import static android.content.pm.UserInfo.FLAG_RESTRICTED; import static android.net.ConnectivityManager.NetworkCallback; +import static android.net.INetd.IF_STATE_DOWN; +import static android.net.INetd.IF_STATE_UP; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -62,6 +64,7 @@ import android.net.ConnectivityManager; import android.net.INetd; import android.net.Ikev2VpnProfile; import android.net.InetAddresses; +import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; import android.net.IpSecManager; import android.net.IpSecTunnelInterfaceResponse; @@ -179,7 +182,8 @@ public class VpnTest { mPackages.put(PKGS[i], PKG_UIDS[i]); } } - private static final UidRange PRI_USER_RANGE = UidRange.createForUser(primaryUser.id); + private static final UidRange PRI_USER_RANGE = + UidRange.createForUser(UserHandle.of(primaryUser.id)); @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Context mContext; @Mock private UserManager mUserManager; @@ -269,7 +273,7 @@ public class VpnTest { vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null); assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] { - PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id) + PRI_USER_RANGE, UidRange.createForUser(UserHandle.of(restrictedProfileA.id)) })), ranges); } @@ -872,17 +876,28 @@ public class VpnTest { eq(AppOpsManager.MODE_IGNORED)); } - private NetworkCallback triggerOnAvailableAndGetCallback() { + private NetworkCallback triggerOnAvailableAndGetCallback() throws Exception { final ArgumentCaptor<NetworkCallback> networkCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class); verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)) .requestNetwork(any(), networkCallbackCaptor.capture()); + // onAvailable() will trigger onDefaultNetworkChanged(), so NetdUtils#setInterfaceUp will be + // invoked. Set the return value of INetd#interfaceGetCfg to prevent NullPointerException. + final InterfaceConfigurationParcel config = new InterfaceConfigurationParcel(); + config.flags = new String[] {IF_STATE_DOWN}; + when(mNetd.interfaceGetCfg(anyString())).thenReturn(config); final NetworkCallback cb = networkCallbackCaptor.getValue(); cb.onAvailable(TEST_NETWORK); return cb; } + private void verifyInterfaceSetCfgWithFlags(String flag) throws Exception { + // Add a timeout for waiting for interfaceSetCfg to be called. + verify(mNetd, timeout(TEST_TIMEOUT_MS)).interfaceSetCfg(argThat( + config -> Arrays.asList(config.flags).contains(flag))); + } + @Test public void testStartPlatformVpnAuthenticationFailed() throws Exception { final ArgumentCaptor<IkeSessionCallback> captor = @@ -894,6 +909,8 @@ public class VpnTest { final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), (mVpnProfile)); final NetworkCallback cb = triggerOnAvailableAndGetCallback(); + verifyInterfaceSetCfgWithFlags(IF_STATE_UP); + // Wait for createIkeSession() to be called before proceeding in order to ensure consistent // state verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS)) @@ -912,6 +929,8 @@ public class VpnTest { final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); final NetworkCallback cb = triggerOnAvailableAndGetCallback(); + verifyInterfaceSetCfgWithFlags(IF_STATE_UP); + // Wait for createIkeSession() to be called before proceeding in order to ensure consistent // state verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java index 435c3c0af817..505ff9b6a34b 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java @@ -17,6 +17,7 @@ package com.android.server.net; import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.NetworkIdentity.OEM_NONE; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.TAG_NONE; @@ -213,7 +214,7 @@ public class NetworkStatsCollectionTest { final NetworkStats.Entry entry = new NetworkStats.Entry(); final NetworkIdentitySet identSet = new NetworkIdentitySet(); identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, - TEST_IMSI, null, false, true, true)); + TEST_IMSI, null, false, true, true, OEM_NONE)); int myUid = Process.myUid(); int otherUidInSameUser = Process.myUid() + 1; @@ -468,7 +469,7 @@ public class NetworkStatsCollectionTest { final NetworkStatsCollection large = new NetworkStatsCollection(HOUR_IN_MILLIS); final NetworkIdentitySet ident = new NetworkIdentitySet(); ident.add(new NetworkIdentity(ConnectivityManager.TYPE_MOBILE, -1, TEST_IMSI, null, - false, true, true)); + false, true, true, OEM_NONE)); large.recordData(ident, UID_ALL, SET_ALL, TAG_NONE, TIME_A, TIME_B, new NetworkStats.Entry(12_730_893_164L, 1, 0, 0, 0)); diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java index 291efc74aa47..9fa1c50423d9 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java @@ -17,6 +17,7 @@ package com.android.server.net; import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.NetworkIdentity.OEM_NONE; import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.DEFAULT_NETWORK_YES; import static android.net.NetworkStats.METERED_NO; @@ -220,7 +221,7 @@ public class NetworkStatsObserversTest { identSet.add(new NetworkIdentity( TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, IMSI_1, null /* networkId */, false /* roaming */, true /* metered */, - true /* defaultNetwork */)); + true /* defaultNetwork */, OEM_NONE)); return identSet; } diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index 214c82da17dc..54d6fb9f2c12 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -21,6 +21,9 @@ import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.NetworkIdentity.OEM_NONE; +import static android.net.NetworkIdentity.OEM_PAID; +import static android.net.NetworkIdentity.OEM_PRIVATE; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.DEFAULT_NETWORK_YES; @@ -40,7 +43,10 @@ import static android.net.NetworkStats.TAG_ALL; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStatsHistory.FIELD_ALL; +import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD; import static android.net.NetworkTemplate.NETWORK_TYPE_ALL; +import static android.net.NetworkTemplate.OEM_MANAGED_NO; +import static android.net.NetworkTemplate.OEM_MANAGED_YES; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.NetworkTemplate.buildTemplateMobileWithRatType; import static android.net.NetworkTemplate.buildTemplateWifi; @@ -643,6 +649,116 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { assertUidTotal(template5g, UID_RED, 5L, 13L, 31L, 9L, 2); } + @Test + public void testMobileStatsOemManaged() throws Exception { + final NetworkTemplate templateOemPaid = new NetworkTemplate(MATCH_MOBILE_WILDCARD, + /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PAID); + + final NetworkTemplate templateOemPrivate = new NetworkTemplate(MATCH_MOBILE_WILDCARD, + /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PRIVATE); + + final NetworkTemplate templateOemAll = new NetworkTemplate(MATCH_MOBILE_WILDCARD, + /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, + OEM_PAID | OEM_PRIVATE); + + final NetworkTemplate templateOemYes = new NetworkTemplate(MATCH_MOBILE_WILDCARD, + /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_YES); + + final NetworkTemplate templateOemNone = new NetworkTemplate(MATCH_MOBILE_WILDCARD, + /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, + METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_NO); + + // OEM_PAID network comes online. + NetworkState[] states = new NetworkState[]{buildOemManagedMobileState(IMSI_1, false, + new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PAID})}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 36L, 41L, 24L, 96L, 0L))); + forcePollAndWaitForIdle(); + + // OEM_PRIVATE network comes online. + states = new NetworkState[]{buildOemManagedMobileState(IMSI_1, false, + new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE})}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 49L, 71L, 72L, 48L, 0L))); + forcePollAndWaitForIdle(); + + // OEM_PAID + OEM_PRIVATE network comes online. + states = new NetworkState[]{buildOemManagedMobileState(IMSI_1, false, + new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, + NetworkCapabilities.NET_CAPABILITY_OEM_PAID})}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 57L, 86L, 83L, 93L, 0L))); + forcePollAndWaitForIdle(); + + // OEM_NONE network comes online. + states = new NetworkState[]{buildOemManagedMobileState(IMSI_1, false, new int[]{})}; + expectNetworkStatsSummary(buildEmptyStats()); + expectNetworkStatsUidDetail(buildEmptyStats()); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), + new UnderlyingNetworkInfo[0]); + + // Create some traffic. + incrementCurrentTime(MINUTE_IN_MILLIS); + expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1) + .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, + 29L, 73L, 34L, 31L, 0L))); + forcePollAndWaitForIdle(); + + // Verify OEM_PAID template gets only relevant stats. + assertUidTotal(templateOemPaid, UID_RED, 36L, 41L, 24L, 96L, 0); + + // Verify OEM_PRIVATE template gets only relevant stats. + assertUidTotal(templateOemPrivate, UID_RED, 49L, 71L, 72L, 48L, 0); + + // Verify OEM_PAID + OEM_PRIVATE template gets only relevant stats. + assertUidTotal(templateOemAll, UID_RED, 57L, 86L, 83L, 93L, 0); + + // Verify OEM_NONE sees only non-OEM managed stats. + assertUidTotal(templateOemNone, UID_RED, 29L, 73L, 34L, 31L, 0); + + // Verify OEM_MANAGED_YES sees all OEM managed stats. + assertUidTotal(templateOemYes, UID_RED, + 36L + 49L + 57L, + 41L + 71L + 86L, + 24L + 72L + 83L, + 96L + 48L + 93L, 0); + + // Verify ALL_MOBILE template gets both OEM managed and non-OEM managed stats. + assertUidTotal(sTemplateImsi1, UID_RED, + 36L + 49L + 57L + 29L, + 41L + 71L + 86L + 73L, + 24L + 72L + 83L + 34L, + 96L + 48L + 93L + 31L, 0); + } + // TODO: support per IMSI state private void setMobileRatTypeAndWaitForIdle(int ratType) { when(mNetworkStatsSubscriptionsMonitor.getRatTypeForSubscriberId(anyString())) @@ -1461,7 +1577,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); capabilities.setSSID(TEST_SSID); - return new NetworkState(TYPE_WIFI, prop, capabilities, WIFI_NETWORK, null, TEST_SSID); + return new NetworkState(TYPE_WIFI, prop, capabilities, WIFI_NETWORK, null); } private static NetworkState buildMobile3gState(String subscriberId) { @@ -1475,8 +1591,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming); capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - return new NetworkState( - TYPE_MOBILE, prop, capabilities, MOBILE_NETWORK, subscriberId, null); + return new NetworkState(TYPE_MOBILE, prop, capabilities, MOBILE_NETWORK, subscriberId); } private NetworkStats buildEmptyStats() { @@ -1486,7 +1601,21 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { private static NetworkState buildVpnState() { final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(TUN_IFACE); - return new NetworkState(TYPE_VPN, prop, new NetworkCapabilities(), VPN_NETWORK, null, null); + return new NetworkState(TYPE_VPN, prop, new NetworkCapabilities(), VPN_NETWORK, null); + } + + private static NetworkState buildOemManagedMobileState(String subscriberId, boolean isRoaming, + int[] oemNetCapabilities) { + final LinkProperties prop = new LinkProperties(); + prop.setInterfaceName(TEST_IFACE); + final NetworkCapabilities capabilities = new NetworkCapabilities(); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false); + capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming); + for (int nc : oemNetCapabilities) { + capabilities.setCapability(nc, true); + } + capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + return new NetworkState(TYPE_MOBILE, prop, capabilities, MOBILE_NETWORK, subscriberId); } private long getElapsedRealtime() { diff --git a/tests/net/jni/Android.bp b/tests/net/jni/Android.bp index 9225ffb24bd8..22a04f5c0945 100644 --- a/tests/net/jni/Android.bp +++ b/tests/net/jni/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_library_shared { name: "libnetworkstatsfactorytestjni", diff --git a/tests/net/smoketest/Android.bp b/tests/net/smoketest/Android.bp index 84ae2b5d845e..1535f3ddcb38 100644 --- a/tests/net/smoketest/Android.bp +++ b/tests/net/smoketest/Android.bp @@ -9,6 +9,15 @@ // // TODO: remove this hack when there is a better solution for jni_libs that includes // dependent libraries. +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"], +} + android_test { name: "FrameworksNetSmokeTests", defaults: ["FrameworksNetTests-jni-defaults"], @@ -19,4 +28,4 @@ android_test { "mockito-target-minus-junit4", "services.core", ], -}
\ No newline at end of file +} diff --git a/tests/notification/Android.bp b/tests/notification/Android.bp index f05edafbf8b7..1c1b5a231e86 100644 --- a/tests/notification/Android.bp +++ b/tests/notification/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_test { name: "NotificationTests", // Include all test java files. diff --git a/tests/permission/Android.bp b/tests/permission/Android.bp index bd07009de7b3..d06809b209a0 100644 --- a/tests/permission/Android.bp +++ b/tests/permission/Android.bp @@ -1,12 +1,26 @@ +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"], +} + android_test { name: "FrameworkPermissionTests", // Include all test java files. srcs: ["src/**/*.java"], libs: [ "android.test.runner", - "telephony-common", "android.test.base", + "telephony-common", + ], + static_libs: [ + "androidx.test.runner", + "junit", + "platform-test-annotations", ], - static_libs: ["junit"], platform_apis: true, + test_suites: ["device-tests"], } diff --git a/tests/permission/AndroidManifest.xml b/tests/permission/AndroidManifest.xml index b19bf006cfeb..9ff5fb39bc4b 100644 --- a/tests/permission/AndroidManifest.xml +++ b/tests/permission/AndroidManifest.xml @@ -24,9 +24,10 @@ <!-- The test declared in this instrumentation can be run via this command - "adb shell am instrument -w com.android.framework.permission.tests/android.test.InstrumentationTestRunner" + $ adb shell am instrument \ + -w com.android.framework.permission.tests/androidx.test.runner.AndroidJUnitRunner --> - <instrumentation android:name="android.test.InstrumentationTestRunner" + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.framework.permission.tests" android:label="Tests for private API framework permissions"/> diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java index 0f920b36cffd..fe6854316133 100644 --- a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java +++ b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java @@ -16,69 +16,135 @@ package com.android.framework.permission.tests; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static junit.framework.Assert.fail; + +import android.Manifest; import android.content.Context; import android.os.Binder; import android.os.CombinedVibrationEffect; -import android.os.IBinder; import android.os.IVibratorManagerService; +import android.os.IVibratorStateListener; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.VibrationAttributes; import android.os.VibrationEffect; -import android.test.suitebuilder.annotation.SmallTest; +import android.platform.test.annotations.Presubmit; -import junit.framework.TestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; /** * Verify that Hardware apis cannot be called without required permissions. */ -@SmallTest -public class VibratorManagerServicePermissionTest extends TestCase { +@Presubmit +@RunWith(JUnit4.class) +public class VibratorManagerServicePermissionTest { + + private static final String PACKAGE_NAME = "com.android.framework.permission.tests"; + private static final CombinedVibrationEffect EFFECT = + CombinedVibrationEffect.createSynced( + VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); + private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder() + .setUsage(VibrationAttributes.USAGE_ALARM) + .build(); + + @Rule + public ExpectedException exceptionRule = ExpectedException.none(); private IVibratorManagerService mVibratorService; - @Override - protected void setUp() throws Exception { + @Before + public void setUp() throws Exception { mVibratorService = IVibratorManagerService.Stub.asInterface( ServiceManager.getService(Context.VIBRATOR_MANAGER_SERVICE)); } - /** - * Test that calling {@link android.os.IVibratorManagerService#vibrate(int, String, - * CombinedVibrationEffect, VibrationAttributes, String, IBinder)} requires permissions. - * <p>Tests permission: - * {@link android.Manifest.permission#VIBRATE} - */ - public void testVibrate() throws RemoteException { - try { - CombinedVibrationEffect effect = - CombinedVibrationEffect.createSynced( - VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); - VibrationAttributes attrs = new VibrationAttributes.Builder() - .setUsage(VibrationAttributes.USAGE_ALARM) - .build(); - mVibratorService.vibrate(Process.myUid(), null, effect, attrs, - "testVibrate", new Binder()); - fail("vibrate did not throw SecurityException as expected"); - } catch (SecurityException e) { - // expected - } + @After + public void cleanUp() { + getInstrumentation().getUiAutomation().dropShellPermissionIdentity(); + } + + @Test + public void testIsVibratingFails() throws RemoteException { + expectSecurityException("ACCESS_VIBRATOR_STATE"); + mVibratorService.isVibrating(1); + } + + @Test + public void testRegisterVibratorStateListenerFails() throws RemoteException { + expectSecurityException("ACCESS_VIBRATOR_STATE"); + IVibratorStateListener listener = new IVibratorStateListener.Stub() { + @Override + public void onVibrating(boolean vibrating) { + fail("Listener callback was not expected."); + } + }; + mVibratorService.registerVibratorStateListener(1, listener); + } + + @Test + public void testUnregisterVibratorStateListenerFails() throws RemoteException { + expectSecurityException("ACCESS_VIBRATOR_STATE"); + mVibratorService.unregisterVibratorStateListener(1, null); + } + + @Test + public void testSetAlwaysOnEffectFails() throws RemoteException { + expectSecurityException("VIBRATE_ALWAYS_ON"); + mVibratorService.setAlwaysOnEffect(Process.myUid(), PACKAGE_NAME, 1, EFFECT, ATTRS); + } + + @Test + public void testVibrateWithoutPermissionFails() throws RemoteException { + expectSecurityException("VIBRATE"); + mVibratorService.vibrate(Process.myUid(), PACKAGE_NAME, EFFECT, ATTRS, "testVibrate", + new Binder()); + } + + @Test + public void testVibrateWithVibratePermissionAndSameProcessUidIsAllowed() + throws RemoteException { + getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( + Manifest.permission.VIBRATE); + mVibratorService.vibrate(Process.myUid(), PACKAGE_NAME, EFFECT, ATTRS, "testVibrate", + new Binder()); + } + + @Test + public void testVibrateWithVibratePermissionAndDifferentUidsFails() throws RemoteException { + expectSecurityException("UPDATE_APP_OPS_STATS"); + getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( + Manifest.permission.VIBRATE); + mVibratorService.vibrate(Process.SYSTEM_UID, "android", EFFECT, ATTRS, "testVibrate", + new Binder()); + } + + @Test + public void testVibrateWithAllPermissionsAndDifferentUidsIsAllowed() throws RemoteException { + getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( + Manifest.permission.VIBRATE, + Manifest.permission.UPDATE_APP_OPS_STATS); + mVibratorService.vibrate(Process.SYSTEM_UID, "android", EFFECT, ATTRS, "testVibrate", + new Binder()); + } + + @Test + public void testCancelVibrateFails() throws RemoteException { + expectSecurityException("VIBRATE"); + mVibratorService.cancelVibrate(new Binder()); } - /** - * Test that calling {@link android.os.IVibratorManagerService#cancelVibrate(IBinder)} requires - * permissions. - * <p>Tests permission: - * {@link android.Manifest.permission#VIBRATE} - */ - public void testCancelVibrate() throws RemoteException { - try { - mVibratorService.cancelVibrate(new Binder()); - fail("cancelVibrate did not throw SecurityException as expected"); - } catch (SecurityException e) { - // expected - } + private void expectSecurityException(String expectedPermission) { + exceptionRule.expect(SecurityException.class); + exceptionRule.expectMessage("permission." + expectedPermission); } } diff --git a/tests/privapp-permissions/Android.bp b/tests/privapp-permissions/Android.bp index b6618508d95d..082b08dea91f 100644 --- a/tests/privapp-permissions/Android.bp +++ b/tests/privapp-permissions/Android.bp @@ -1,3 +1,12 @@ +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"], +} + android_app { name: "PrivAppPermissionTest", sdk_version: "current", diff --git a/tests/testables/Android.bp b/tests/testables/Android.bp index eb6811cf490e..c0e3d630d1ab 100644 --- a/tests/testables/Android.bp +++ b/tests/testables/Android.bp @@ -14,6 +14,15 @@ // 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: "testables", srcs: ["src/**/*.java"], diff --git a/tests/testables/tests/Android.bp b/tests/testables/tests/Android.bp index e1a58be7bd68..ba323d3fe47a 100644 --- a/tests/testables/tests/Android.bp +++ b/tests/testables/tests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "TestablesTests", platform_apis: true, diff --git a/tests/utils/StubIME/Android.bp b/tests/utils/StubIME/Android.bp index 668c92c86c51..d86068cef8b4 100644 --- a/tests/utils/StubIME/Android.bp +++ b/tests/utils/StubIME/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "StubIME", srcs: ["src/**/*.java"], diff --git a/tests/utils/hostutils/Android.bp b/tests/utils/hostutils/Android.bp index c9ad70280aa6..05f3c74218c6 100644 --- a/tests/utils/hostutils/Android.bp +++ b/tests/utils/hostutils/Android.bp @@ -13,6 +13,15 @@ // 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_host { name: "frameworks-base-hostutils", diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp index a6625ab9c17f..af9786b92f40 100644 --- a/tests/utils/testutils/Android.bp +++ b/tests/utils/testutils/Android.bp @@ -14,6 +14,15 @@ // 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: "frameworks-base-testutils", diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp index 1dedc19f0c88..41f73cd9c706 100644 --- a/tests/vcn/Android.bp +++ b/tests/vcn/Android.bp @@ -2,6 +2,15 @@ // Build FrameworksVcnTests package //######################################################################## +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"], +} + android_test { name: "FrameworksVcnTests", srcs: [ diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java index 1a90fc319bce..66590c92579b 100644 --- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java +++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java @@ -16,12 +16,15 @@ package android.net.vcn; +import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; + import static androidx.test.InstrumentationRegistry.getContext; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -33,6 +36,7 @@ import android.content.Context; import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.net.vcn.VcnManager.VcnStatusCallback; +import android.net.vcn.VcnManager.VcnStatusCallbackBinder; import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener; import android.os.ParcelUuid; @@ -40,11 +44,15 @@ import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import java.net.UnknownHostException; import java.util.UUID; import java.util.concurrent.Executor; public class VcnManagerTest { private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0)); + private static final int[] UNDERLYING_NETWORK_CAPABILITIES = { + NetworkCapabilities.NET_CAPABILITY_IMS, NetworkCapabilities.NET_CAPABILITY_INTERNET + }; private static final Executor INLINE_EXECUTOR = Runnable::run; private IVcnManagementService mMockVcnManagementService; @@ -144,14 +152,8 @@ public class VcnManagerTest { public void testRegisterVcnStatusCallback() throws Exception { mVcnManager.registerVcnStatusCallback(SUB_GROUP, INLINE_EXECUTOR, mMockStatusCallback); - ArgumentCaptor<IVcnStatusCallback> captor = - ArgumentCaptor.forClass(IVcnStatusCallback.class); verify(mMockVcnManagementService) - .registerVcnStatusCallback(eq(SUB_GROUP), captor.capture(), any()); - - IVcnStatusCallback callbackWrapper = captor.getValue(); - callbackWrapper.onEnteredSafeMode(); - verify(mMockStatusCallback).onEnteredSafeMode(); + .registerVcnStatusCallback(eq(SUB_GROUP), notNull(), any()); } @Test(expected = IllegalStateException.class) @@ -195,4 +197,27 @@ public class VcnManagerTest { public void testUnregisterNullVcnStatusCallback() throws Exception { mVcnManager.unregisterVcnStatusCallback(null); } + + @Test + public void testVcnStatusCallbackBinder() throws Exception { + IVcnStatusCallback cbBinder = + new VcnStatusCallbackBinder(INLINE_EXECUTOR, mMockStatusCallback); + + cbBinder.onEnteredSafeMode(); + verify(mMockStatusCallback).onEnteredSafeMode(); + + cbBinder.onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE); + verify(mMockStatusCallback).onVcnStatusChanged(VCN_STATUS_CODE_ACTIVE); + + cbBinder.onGatewayConnectionError( + UNDERLYING_NETWORK_CAPABILITIES, + VcnManager.VCN_ERROR_CODE_NETWORK_ERROR, + UnknownHostException.class.getName(), + "exception_message"); + verify(mMockStatusCallback) + .onGatewayConnectionError( + eq(UNDERLYING_NETWORK_CAPABILITIES), + eq(VcnManager.VCN_ERROR_CODE_NETWORK_ERROR), + any(UnknownHostException.class)); + } } diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java index 3ba0a1f53a9f..a674425efea3 100644 --- a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java +++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java @@ -46,6 +46,6 @@ public class VcnUnderlyingNetworkPolicyTest { @Test public void testParcelUnparcel() { - assertParcelSane(SAMPLE_NETWORK_POLICY, 2); + assertParcelSane(SAMPLE_NETWORK_POLICY, 1); } } diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java new file mode 100644 index 000000000000..2110d6ee7c86 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java @@ -0,0 +1,61 @@ +/* + * 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.net.vcn; + +import static com.android.testutils.ParcelUtils.assertParcelSane; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.net.TelephonyNetworkSpecifier; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VcnUnderlyingNetworkSpecifierTest { + private static final int[] TEST_SUB_IDS = new int[] {1, 2, 3, 5}; + + @Test + public void testGetSubIds() { + final VcnUnderlyingNetworkSpecifier specifier = + new VcnUnderlyingNetworkSpecifier(TEST_SUB_IDS); + + assertEquals(TEST_SUB_IDS, specifier.getSubIds()); + } + + @Test + public void testParceling() { + final VcnUnderlyingNetworkSpecifier specifier = + new VcnUnderlyingNetworkSpecifier(TEST_SUB_IDS); + assertParcelSane(specifier, 1); + } + + @Test + public void testCanBeSatisfiedByTelephonyNetworkSpecifier() { + final TelephonyNetworkSpecifier telSpecifier = + new TelephonyNetworkSpecifier(TEST_SUB_IDS[0]); + + final VcnUnderlyingNetworkSpecifier specifier = + new VcnUnderlyingNetworkSpecifier(TEST_SUB_IDS); + assertTrue(specifier.canBeSatisfiedBy(telSpecifier)); + } +} diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java new file mode 100644 index 000000000000..28cf38a2a583 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java @@ -0,0 +1,66 @@ +/* + * 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.net.vcn.persistablebundleutils; + +import static org.junit.Assert.assertEquals; + +import android.net.InetAddresses; +import android.net.ipsec.ike.IkeTrafficSelector; +import android.os.PersistableBundle; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.InetAddress; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IkeTrafficSelectorUtilsTest { + private static final int START_PORT = 16; + private static final int END_PORT = 65520; + + private static final InetAddress IPV4_START_ADDRESS = + InetAddresses.parseNumericAddress("192.0.2.100"); + private static final InetAddress IPV4_END_ADDRESS = + InetAddresses.parseNumericAddress("192.0.2.101"); + + private static final InetAddress IPV6_START_ADDRESS = + InetAddresses.parseNumericAddress("2001:db8:2::100"); + private static final InetAddress IPV6_END_ADDRESS = + InetAddresses.parseNumericAddress("2001:db8:2::101"); + + private static void verifyPersistableBundleEncodeDecodeIsLossless(IkeTrafficSelector ts) { + final PersistableBundle bundle = IkeTrafficSelectorUtils.toPersistableBundle(ts); + final IkeTrafficSelector resultTs = IkeTrafficSelectorUtils.fromPersistableBundle(bundle); + assertEquals(ts, resultTs); + } + + @Test + public void testPersistableBundleEncodeDecodeIsLosslessIpv4Ts() throws Exception { + verifyPersistableBundleEncodeDecodeIsLossless( + new IkeTrafficSelector(START_PORT, END_PORT, IPV4_START_ADDRESS, IPV4_END_ADDRESS)); + } + + @Test + public void testPersistableBundleEncodeDecodeIsLosslessIpv6Ts() throws Exception { + verifyPersistableBundleEncodeDecodeIsLossless( + new IkeTrafficSelector(START_PORT, END_PORT, IPV6_START_ADDRESS, IPV6_END_ADDRESS)); + } +} diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 124ec3056fb2..9b500a7271d7 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -43,7 +43,6 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -59,6 +58,7 @@ import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfigTest; +import android.net.vcn.VcnManager; import android.net.vcn.VcnUnderlyingNetworkPolicy; import android.net.wifi.WifiInfo; import android.os.IBinder; @@ -75,7 +75,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.LocationPermissionChecker; -import com.android.server.VcnManagementService.VcnSafeModeCallback; +import com.android.server.VcnManagementService.VcnCallback; import com.android.server.VcnManagementService.VcnStatusCallbackInfo; import com.android.server.vcn.TelephonySubscriptionTracker; import com.android.server.vcn.Vcn; @@ -156,8 +156,8 @@ public class VcnManagementServiceTest { private final LocationPermissionChecker mLocationPermissionChecker = mock(LocationPermissionChecker.class); - private final ArgumentCaptor<VcnSafeModeCallback> mSafeModeCallbackCaptor = - ArgumentCaptor.forClass(VcnSafeModeCallback.class); + private final ArgumentCaptor<VcnCallback> mVcnCallbackCaptor = + ArgumentCaptor.forClass(VcnCallback.class); private final VcnManagementService mVcnMgmtSvc; @@ -721,7 +721,7 @@ public class VcnManagementServiceTest { verify(mMockPolicyListener).onPolicyChanged(); } - private void verifyVcnSafeModeCallback( + private void verifyVcnCallback( @NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot) throws Exception { verify(mMockDeps) @@ -730,22 +730,22 @@ public class VcnManagementServiceTest { eq(subGroup), eq(TEST_VCN_CONFIG), eq(snapshot), - mSafeModeCallbackCaptor.capture()); + mVcnCallbackCaptor.capture()); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); - VcnSafeModeCallback safeModeCallback = mSafeModeCallbackCaptor.getValue(); - safeModeCallback.onEnteredSafeMode(); + VcnCallback vcnCallback = mVcnCallbackCaptor.getValue(); + vcnCallback.onEnteredSafeMode(); verify(mMockPolicyListener).onPolicyChanged(); } @Test - public void testVcnSafeModeCallbackOnEnteredSafeMode() throws Exception { + public void testVcnCallbackOnEnteredSafeMode() throws Exception { TelephonySubscriptionSnapshot snapshot = triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1)); - verifyVcnSafeModeCallback(TEST_UUID_1, snapshot); + verifyVcnCallback(TEST_UUID_1, snapshot); } private void triggerVcnStatusCallbackOnEnteredSafeMode( @@ -771,7 +771,7 @@ public class VcnManagementServiceTest { // Trigger systemReady() to set up LocationPermissionChecker mVcnMgmtSvc.systemReady(); - verifyVcnSafeModeCallback(subGroup, snapshot); + verifyVcnCallback(subGroup, snapshot); } @Test @@ -783,7 +783,7 @@ public class VcnManagementServiceTest { true /* hasPermissionsforSubGroup */, true /* hasLocationPermission */); - verify(mMockStatusCallback, times(1)).onEnteredSafeMode(); + verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE); } @Test @@ -795,7 +795,8 @@ public class VcnManagementServiceTest { false /* hasPermissionsforSubGroup */, true /* hasLocationPermission */); - verify(mMockStatusCallback, never()).onEnteredSafeMode(); + verify(mMockStatusCallback, never()) + .onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE); } @Test @@ -807,7 +808,8 @@ public class VcnManagementServiceTest { true /* hasPermissionsforSubGroup */, false /* hasLocationPermission */); - verify(mMockStatusCallback, never()).onEnteredSafeMode(); + verify(mMockStatusCallback, never()) + .onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index b62a0b8c428a..69c21b967917 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -20,6 +20,9 @@ import static android.net.IpSecManager.DIRECTION_IN; import static android.net.IpSecManager.DIRECTION_OUT; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR; +import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR; +import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR; import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration; import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; @@ -39,6 +42,11 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import android.net.LinkProperties; import android.net.NetworkAgent; import android.net.NetworkCapabilities; +import android.net.ipsec.ike.exceptions.AuthenticationFailedException; +import android.net.ipsec.ike.exceptions.IkeException; +import android.net.ipsec.ike.exceptions.IkeInternalException; +import android.net.ipsec.ike.exceptions.TemporaryFailureException; +import android.net.vcn.VcnManager.VcnErrorCode; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -48,6 +56,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import java.io.IOException; +import java.net.UnknownHostException; import java.util.Collections; /** Tests for VcnGatewayConnection.ConnectedState */ @@ -208,6 +218,25 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection // Since network never validated, verify mSafeModeTimeoutAlarm not canceled verifyNoMoreInteractions(mSafeModeTimeoutAlarm); + + // The child session was closed without exception, so verify that the GatewayStatusCallback + // was not notified + verifyNoMoreInteractions(mGatewayStatusCallback); + } + + @Test + public void testChildSessionClosedExceptionallyNotifiesGatewayStatusCallback() + throws Exception { + final IkeInternalException exception = new IkeInternalException(mock(IOException.class)); + getChildSessionCallback().onClosedExceptionally(exception); + mTestLooper.dispatchAll(); + + verify(mGatewayStatusCallback) + .onGatewayConnectionError( + eq(mConfig.getRequiredUnderlyingCapabilities()), + eq(VCN_ERROR_CODE_INTERNAL_ERROR), + any(), + any()); } @Test @@ -223,5 +252,42 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection // Since network never validated, verify mSafeModeTimeoutAlarm not canceled verifyNoMoreInteractions(mSafeModeTimeoutAlarm); + + // IkeSession closed with no error, so verify that the GatewayStatusCallback was not + // notified + verifyNoMoreInteractions(mGatewayStatusCallback); + } + + private void verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback( + IkeException cause, @VcnErrorCode int expectedErrorType) { + getIkeSessionCallback().onClosedExceptionally(cause); + mTestLooper.dispatchAll(); + + verify(mIkeSession).close(); + + verify(mGatewayStatusCallback) + .onGatewayConnectionError( + eq(mConfig.getRequiredUnderlyingCapabilities()), + eq(expectedErrorType), + any(), + any()); + } + + @Test + public void testIkeSessionClosedExceptionallyAuthenticationFailure() throws Exception { + verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback( + new AuthenticationFailedException("vcn test"), VCN_ERROR_CODE_CONFIG_ERROR); + } + + @Test + public void testIkeSessionClosedExceptionallyDnsFailure() throws Exception { + verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback( + new IkeInternalException(new UnknownHostException()), VCN_ERROR_CODE_NETWORK_ERROR); + } + + @Test + public void testIkeSessionClosedExceptionallyInternalFailure() throws Exception { + verifyIkeSessionClosedExceptionalltyNotifiesStatusCallback( + new TemporaryFailureException("vcn test"), VCN_ERROR_CODE_INTERNAL_ERROR); } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java index 8e142c095ae3..9d3368271243 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java @@ -34,7 +34,7 @@ import android.net.vcn.VcnGatewayConnectionConfigTest; import android.os.ParcelUuid; import android.os.test.TestLooper; -import com.android.server.VcnManagementService.VcnSafeModeCallback; +import com.android.server.VcnManagementService.VcnCallback; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener; @@ -56,7 +56,7 @@ public class VcnTest { private VcnContext mVcnContext; private TelephonySubscriptionSnapshot mSubscriptionSnapshot; private VcnNetworkProvider mVcnNetworkProvider; - private VcnSafeModeCallback mVcnSafeModeCallback; + private VcnCallback mVcnCallback; private Vcn.Dependencies mDeps; private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor; @@ -72,7 +72,7 @@ public class VcnTest { mVcnContext = mock(VcnContext.class); mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class); mVcnNetworkProvider = mock(VcnNetworkProvider.class); - mVcnSafeModeCallback = mock(VcnSafeModeCallback.class); + mVcnCallback = mock(VcnCallback.class); mDeps = mock(Vcn.Dependencies.class); mTestLooper = new TestLooper(); @@ -104,7 +104,7 @@ public class VcnTest { TEST_SUB_GROUP, mConfig, mSubscriptionSnapshot, - mVcnSafeModeCallback, + mVcnCallback, mDeps); } @@ -179,6 +179,6 @@ public class VcnTest { verify(gatewayConnection).teardownAsynchronously(); } verify(mVcnNetworkProvider).unregisterListener(requestListener); - verify(mVcnSafeModeCallback).onEnteredSafeMode(); + verify(mVcnCallback).onEnteredSafeMode(); } } diff --git a/tools/aapt/Android.bp b/tools/aapt/Android.bp index a594e5bf0ce1..c75ba71c4432 100644 --- a/tools/aapt/Android.bp +++ b/tools/aapt/Android.bp @@ -19,6 +19,23 @@ // targets here. // ========================================================== +package { + default_applicable_licenses: ["frameworks_base_tools_aapt_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_base_tools_aapt_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_defaults { name: "aapt_defaults", diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 46ae2ecd9406..df727e0f883e 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + toolSources = [ "cmd/Command.cpp", "cmd/Compile.cpp", diff --git a/tools/aapt2/integration-tests/AutoVersionTest/Android.bp b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp index 79fb5734cd68..bfd35083366e 100644 --- a/tools/aapt2/integration-tests/AutoVersionTest/Android.bp +++ b/tools/aapt2/integration-tests/AutoVersionTest/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "AaptAutoVersionTest", sdk_version: "current", diff --git a/tools/aapt2/integration-tests/BasicTest/Android.bp b/tools/aapt2/integration-tests/BasicTest/Android.bp index a94a01f12c9e..7db9d2698cc7 100644 --- a/tools/aapt2/integration-tests/BasicTest/Android.bp +++ b/tools/aapt2/integration-tests/BasicTest/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "AaptBasicTest", sdk_version: "current", diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk index 7bf8cf84426c..c084849a9d28 100644 --- a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk +++ b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk @@ -20,9 +20,12 @@ include $(CLEAR_VARS) LOCAL_USE_AAPT2 := true LOCAL_AAPT_NAMESPACES := true LOCAL_MODULE := AaptTestMergeOnly_LeafLib +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_MODULE_TAGS := tests LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_MIN_SDK_VERSION := 21 LOCAL_AAPT_FLAGS := --merge-only -include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file +include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk index ba781c56a913..699ad79ecf1a 100644 --- a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk +++ b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk @@ -20,9 +20,12 @@ include $(CLEAR_VARS) LOCAL_USE_AAPT2 := true LOCAL_AAPT_NAMESPACES := true LOCAL_MODULE := AaptTestMergeOnly_LocalLib +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_MODULE_TAGS := tests LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_MIN_SDK_VERSION := 21 LOCAL_AAPT_FLAGS := --merge-only -include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file +include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk index c723d905db5b..dd4170234258 100644 --- a/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk +++ b/tools/aapt2/integration-tests/NamespaceTest/LibOne/Android.mk @@ -20,6 +20,9 @@ include $(CLEAR_VARS) LOCAL_USE_AAPT2 := true LOCAL_AAPT_NAMESPACES := true LOCAL_MODULE := AaptTestNamespace_LibOne +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_MODULE_TAGS := tests LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res diff --git a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk index 90a7f627e591..0d11bcbda64d 100644 --- a/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk +++ b/tools/aapt2/integration-tests/NamespaceTest/LibTwo/Android.mk @@ -20,6 +20,9 @@ include $(CLEAR_VARS) LOCAL_USE_AAPT2 := true LOCAL_AAPT_NAMESPACES := true LOCAL_MODULE := AaptTestNamespace_LibTwo +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE LOCAL_SDK_VERSION := current LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under,src) diff --git a/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp index 9aadff3d619e..80404eeb8d8e 100644 --- a/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp +++ b/tools/aapt2/integration-tests/StaticLibTest/App/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "AaptTestStaticLib_App", diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp index 4c8181343a33..a84da43c70c8 100644 --- a/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp +++ b/tools/aapt2/integration-tests/StaticLibTest/LibOne/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_library { name: "AaptTestStaticLib_LibOne", sdk_version: "current", diff --git a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp index 7c4f7ed90e69..d386c3a35d20 100644 --- a/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp +++ b/tools/aapt2/integration-tests/StaticLibTest/LibTwo/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_library { name: "AaptTestStaticLib_LibTwo", sdk_version: "current", diff --git a/tools/aapt2/integration-tests/SymlinkTest/Android.bp b/tools/aapt2/integration-tests/SymlinkTest/Android.bp index 68e6148e480c..1e8cf86ed811 100644 --- a/tools/aapt2/integration-tests/SymlinkTest/Android.bp +++ b/tools/aapt2/integration-tests/SymlinkTest/Android.bp @@ -14,6 +14,15 @@ // 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"], +} + android_test { name: "AaptSymlinkTest", sdk_version: "current", diff --git a/tools/bit/Android.bp b/tools/bit/Android.bp index a8062719d586..f6aa0fb8f3f0 100644 --- a/tools/bit/Android.bp +++ b/tools/bit/Android.bp @@ -17,6 +17,15 @@ // ========================================================== // Build the host executable: bit // ========================================================== +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"], +} + cc_binary_host { name: "bit", diff --git a/tools/codegen/Android.bp b/tools/codegen/Android.bp index 677bee2cce81..e53ba3e18a86 100644 --- a/tools/codegen/Android.bp +++ b/tools/codegen/Android.bp @@ -1,3 +1,12 @@ +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_binary_host { name: "codegen_cli", manifest: "manifest.txt", diff --git a/tools/dump-coverage/Android.bp b/tools/dump-coverage/Android.bp index 94356ebac035..f38177390972 100644 --- a/tools/dump-coverage/Android.bp +++ b/tools/dump-coverage/Android.bp @@ -15,6 +15,15 @@ // // Build variants {target,host} x {32,64} +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"], +} + cc_library { name: "libdumpcoverage", srcs: ["dump_coverage.cc"], diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py index f0b759547a93..ebc0ec1640f5 100755 --- a/tools/fonts/fontchain_linter.py +++ b/tools/fonts/fontchain_linter.py @@ -11,6 +11,12 @@ from fontTools import ttLib EMOJI_VS = 0xFE0F +#TODO(179952916): Rename CutiveMono and DancingScript +CANONICAL_NAME_EXCEPTION_LIST = [ + 'CutiveMono.ttf', + 'DancingScript-Regular.ttf', +] + LANG_TO_SCRIPT = { 'as': 'Beng', 'be': 'Cyrl', @@ -665,6 +671,53 @@ def check_cjk_punctuation(): assert_font_supports_none_of_chars(record.font, cjk_punctuation, name) +def getPostScriptName(font): + ttf = open_font(font) + nameTable = ttf['name'] + for name in nameTable.names: + if name.nameID == 6 and name.platformID == 3 and name.platEncID == 1 and name.langID == 0x0409: + return str(name) + + +def getSuffix(font): + file_path, index = font + with open(path.join(_fonts_dir, file_path), 'rb') as f: + tag = f.read(4) + isCollection = tag == b'ttcf' + + ttf = open_font(font) + isType1 = ('CFF ' in ttf or 'CFF2' in ttf) + + if isType1: + if isCollection: + return '.otc' + else: + return '.otf' + else: + if isCollection: + return '.ttc' + else: + return '.ttf' + + +def check_canonical_name(): + for record in _all_fonts: + file_name, index = record.font + if file_name in CANONICAL_NAME_EXCEPTION_LIST: + continue + + if index and index != 0: + continue + + psName = getPostScriptName(record.font) + assert psName, 'PostScript must be defined' + + suffix = getSuffix(record.font) + canonicalName = '%s%s' % (psName, suffix) + + assert file_name == canonicalName, ( + '%s is not a canonical name. Must be %s' % (file_name, canonicalName)) + def main(): global _fonts_dir target_out = sys.argv[1] @@ -682,6 +735,8 @@ def main(): check_cjk_punctuation() + check_canonical_name() + check_emoji = sys.argv[2] if check_emoji == 'true': ucd_path = sys.argv[3] diff --git a/tools/incident_report/Android.bp b/tools/incident_report/Android.bp index f2d0d0f3e553..fe519c7edff4 100644 --- a/tools/incident_report/Android.bp +++ b/tools/incident_report/Android.bp @@ -17,6 +17,15 @@ // ========================================================== // Build the host executable: incident_report // ========================================================== +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"], +} + cc_binary_host { name: "incident_report", diff --git a/tools/incident_section_gen/Android.bp b/tools/incident_section_gen/Android.bp index 0c7797eacf09..8227b60d6c09 100644 --- a/tools/incident_section_gen/Android.bp +++ b/tools/incident_section_gen/Android.bp @@ -17,6 +17,15 @@ // ========================================================== // Build the host executable: incident-section-gen // ========================================================== +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"], +} + cc_binary_host { name: "incident-section-gen", cflags: [ diff --git a/tools/lock_agent/Android.bp b/tools/lock_agent/Android.bp index 7b2ca9a65242..cabe1398903e 100644 --- a/tools/lock_agent/Android.bp +++ b/tools/lock_agent/Android.bp @@ -1,3 +1,12 @@ +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"], +} + cc_library { name: "liblockagent", host_supported: false, diff --git a/tools/locked_region_code_injection/Android.bp b/tools/locked_region_code_injection/Android.bp index 5f81a2eeb130..98c0e69cbf8e 100644 --- a/tools/locked_region_code_injection/Android.bp +++ b/tools/locked_region_code_injection/Android.bp @@ -1,3 +1,12 @@ +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_binary_host { name: "lockedregioncodeinjection", manifest: "manifest.txt", diff --git a/tools/obbtool/Android.bp b/tools/obbtool/Android.bp index f87965860ce1..1c50d18ed32b 100644 --- a/tools/obbtool/Android.bp +++ b/tools/obbtool/Android.bp @@ -4,6 +4,15 @@ // Opaque Binary Blob (OBB) Tool // +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"], +} + cc_binary_host { name: "obbtool", diff --git a/tools/powermodel/Android.bp b/tools/powermodel/Android.bp index f597aab0f464..35c135614027 100644 --- a/tools/powermodel/Android.bp +++ b/tools/powermodel/Android.bp @@ -1,4 +1,13 @@ +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_host { name: "powermodel", srcs: [ @@ -23,4 +32,3 @@ java_test_host { "mockito", ], } - diff --git a/tools/powerstats/Android.bp b/tools/powerstats/Android.bp index af41144167a9..9c58daf0f922 100644 --- a/tools/powerstats/Android.bp +++ b/tools/powerstats/Android.bp @@ -1,3 +1,12 @@ +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_binary_host { name: "PowerStatsServiceProtoParser", manifest: "PowerStatsServiceProtoParser_manifest.txt", diff --git a/tools/preload-check/Android.bp b/tools/preload-check/Android.bp index aaa6d76b39e3..73caac694cb4 100644 --- a/tools/preload-check/Android.bp +++ b/tools/preload-check/Android.bp @@ -12,6 +12,15 @@ // 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_test_host { name: "PreloadCheck", srcs: ["src/**/*.java"], diff --git a/tools/preload-check/device/Android.bp b/tools/preload-check/device/Android.bp index f40d8ba5a287..2a866c426336 100644 --- a/tools/preload-check/device/Android.bp +++ b/tools/preload-check/device/Android.bp @@ -12,6 +12,15 @@ // 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_test_helper_library { name: "preload-check-device", host_supported: false, diff --git a/tools/preload/Android.bp b/tools/preload/Android.bp index 809ee474969a..fad015ae35e0 100644 --- a/tools/preload/Android.bp +++ b/tools/preload/Android.bp @@ -1,3 +1,13 @@ +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 + // SPDX-license-identifier-MIT + default_applicable_licenses: ["frameworks_base_license"], +} + java_library_host { name: "preload", srcs: [ diff --git a/tools/preload/loadclass/Android.bp b/tools/preload/loadclass/Android.bp index 6f12015dae2b..ba36061e5fb6 100644 --- a/tools/preload/loadclass/Android.bp +++ b/tools/preload/loadclass/Android.bp @@ -1,3 +1,12 @@ +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_test { name: "loadclass", srcs: ["**/*.java"], diff --git a/tools/processors/intdef_mappings/Android.bp b/tools/processors/intdef_mappings/Android.bp index e255f7c784d3..82a5dac21160 100644 --- a/tools/processors/intdef_mappings/Android.bp +++ b/tools/processors/intdef_mappings/Android.bp @@ -1,3 +1,12 @@ +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_plugin { name: "intdef-annotation-processor", @@ -30,4 +39,4 @@ java_test_host { ], test_suites: ["general-tests"], -}
\ No newline at end of file +} diff --git a/tools/processors/staledataclass/Android.bp b/tools/processors/staledataclass/Android.bp index 58a7d346ce1f..1e5097662a0a 100644 --- a/tools/processors/staledataclass/Android.bp +++ b/tools/processors/staledataclass/Android.bp @@ -1,4 +1,13 @@ +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_plugin { name: "staledataclass-annotation-processor", processor_class: "android.processor.staledataclass.StaleDataclassProcessor", diff --git a/tools/processors/view_inspector/Android.bp b/tools/processors/view_inspector/Android.bp index 069e61f5b921..ea9974f06a64 100644 --- a/tools/processors/view_inspector/Android.bp +++ b/tools/processors/view_inspector/Android.bp @@ -1,3 +1,12 @@ +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_plugin { name: "view-inspector-annotation-processor", diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp index 0be80d31990a..039bb4e2788c 100644 --- a/tools/protologtool/Android.bp +++ b/tools/protologtool/Android.bp @@ -1,3 +1,12 @@ +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_host { name: "protologtool-lib", srcs: [ @@ -25,9 +34,13 @@ java_test_host { srcs: [ "tests/**/*.kt", ], + test_options: { + unit_test: true, + }, static_libs: [ "protologtool-lib", "junit", "mockito", + "objenesis", ], } diff --git a/tools/sdkparcelables/Android.bp b/tools/sdkparcelables/Android.bp index 00fb8aa20b18..9d773e44faea 100644 --- a/tools/sdkparcelables/Android.bp +++ b/tools/sdkparcelables/Android.bp @@ -1,3 +1,12 @@ +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_binary_host { name: "sdkparcelables", manifest: "manifest.txt", diff --git a/tools/split-select/Android.bp b/tools/split-select/Android.bp index ee822b7d7fa7..c12fc6a7f253 100644 --- a/tools/split-select/Android.bp +++ b/tools/split-select/Android.bp @@ -19,6 +19,15 @@ // targets here. // ========================================================== +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"], +} + cc_defaults { name: "split-select_defaults", diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp index 1390f63248f9..1ec83a360048 100644 --- a/tools/streaming_proto/Android.bp +++ b/tools/streaming_proto/Android.bp @@ -17,6 +17,15 @@ // ========================================================== // Build the host executable: protoc-gen-javastream // ========================================================== +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"], +} + cc_defaults { name: "protoc-gen-stream-defaults", srcs: [ diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp index 15b8b4105713..0423b7abd685 100644 --- a/tools/validatekeymaps/Android.bp +++ b/tools/validatekeymaps/Android.bp @@ -4,6 +4,15 @@ // Keymap validation tool. // +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"], +} + cc_binary_host { name: "validatekeymaps", diff --git a/tools/xmlpersistence/Android.bp b/tools/xmlpersistence/Android.bp index d58d0dcdc45a..0b6dba626794 100644 --- a/tools/xmlpersistence/Android.bp +++ b/tools/xmlpersistence/Android.bp @@ -1,3 +1,12 @@ +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_binary_host { name: "xmlpersistence_cli", manifest: "manifest.txt", diff --git a/wifi/java/Android.bp b/wifi/java/Android.bp index b35b4be55818..225e750923fd 100644 --- a/wifi/java/Android.bp +++ b/wifi/java/Android.bp @@ -16,6 +16,15 @@ // updatable), and are generally only called by the Wifi module. // necessary since we only want the `path` property to only refer to these 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"], +} + filegroup { name: "framework-wifi-non-updatable-sources-internal", srcs: ["src/**/*.java"], diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp index 3f5cacff017f..c9105f7454e4 100644 --- a/wifi/tests/Android.bp +++ b/wifi/tests/Android.bp @@ -12,6 +12,15 @@ // 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"], +} + android_test { name: "FrameworksWifiNonUpdatableApiTests", |